1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include "CDNSSDService.h"
19#include "nsThreadUtils.h"
20#include "nsIEventTarget.h"
21#include "private/pprio.h"
22#include <string>
23#include <stdio.h>
24
25
26NS_IMPL_ISUPPORTS2(CDNSSDService, IDNSSDService, nsIRunnable)
27
28CDNSSDService::CDNSSDService()
29:
30	m_master( 1 ),
31	m_threadPool( NULL ),
32	m_mainRef( NULL ),
33	m_subRef( NULL ),
34	m_listener( NULL ),
35	m_fileDesc( NULL ),
36	m_job( NULL )
37{
38	nsresult err;
39
40	if ( DNSServiceCreateConnection( &m_mainRef ) != kDNSServiceErr_NoError )
41	{
42		err = NS_ERROR_FAILURE;
43		goto exit;
44	}
45
46	if ( ( m_fileDesc = PR_ImportTCPSocket( DNSServiceRefSockFD( m_mainRef ) ) ) == NULL )
47	{
48		err = NS_ERROR_FAILURE;
49		goto exit;
50	}
51
52	if ( ( m_threadPool = PR_CreateThreadPool( 1, 1, 8192 ) ) == NULL )
53	{
54		err = NS_ERROR_FAILURE;
55		goto exit;
56	}
57
58	err = SetupNotifications();
59
60exit:
61
62	if ( err != NS_OK )
63	{
64		Cleanup();
65	}
66}
67
68
69CDNSSDService::CDNSSDService( DNSServiceRef ref, nsISupports * listener )
70:
71	m_master( 0 ),
72	m_threadPool( NULL ),
73	m_mainRef( ref ),
74	m_subRef( ref ),
75	m_listener( listener ),
76	m_fileDesc( NULL ),
77	m_job( NULL )
78{
79}
80
81
82CDNSSDService::~CDNSSDService()
83{
84	Cleanup();
85}
86
87
88void
89CDNSSDService::Cleanup()
90{
91	if ( m_master )
92	{
93		if ( m_job )
94		{
95			PR_CancelJob( m_job );
96			m_job = NULL;
97		}
98
99		if ( m_threadPool != NULL )
100		{
101			PR_ShutdownThreadPool( m_threadPool );
102			m_threadPool = NULL;
103		}
104
105		if ( m_fileDesc != NULL )
106		{
107			PR_Close( m_fileDesc );
108			m_fileDesc = NULL;
109		}
110
111		if ( m_mainRef )
112		{
113			DNSServiceRefDeallocate( m_mainRef );
114			m_mainRef = NULL;
115		}
116	}
117	else
118	{
119		if ( m_subRef )
120		{
121			DNSServiceRefDeallocate( m_subRef );
122			m_subRef = NULL;
123		}
124	}
125}
126
127
128nsresult
129CDNSSDService::SetupNotifications()
130{
131	NS_PRECONDITION( m_threadPool != NULL, "m_threadPool is NULL" );
132	NS_PRECONDITION( m_fileDesc != NULL, "m_fileDesc is NULL" );
133	NS_PRECONDITION( m_job == NULL, "m_job is not NULL" );
134
135	m_iod.socket	= m_fileDesc;
136	m_iod.timeout	= PR_INTERVAL_MAX;
137	m_job			= PR_QueueJob_Read( m_threadPool, &m_iod, Read, this, PR_FALSE );
138	return ( m_job ) ? NS_OK : NS_ERROR_FAILURE;
139}
140
141
142/* IDNSSDService browse (in long interfaceIndex, in AString regtype, in AString domain, in IDNSSDBrowseListener listener); */
143NS_IMETHODIMP
144CDNSSDService::Browse(PRInt32 interfaceIndex, const nsAString & regtype, const nsAString & domain, IDNSSDBrowseListener *listener, IDNSSDService **_retval NS_OUTPARAM)
145{
146	CDNSSDService	*	service	= NULL;
147	DNSServiceErrorType dnsErr	= 0;
148	nsresult			err		= 0;
149
150	*_retval = NULL;
151
152	if ( !m_mainRef )
153	{
154		err = NS_ERROR_NOT_AVAILABLE;
155		goto exit;
156	}
157
158	try
159	{
160		service = new CDNSSDService( m_mainRef, listener );
161	}
162	catch ( ... )
163	{
164		service = NULL;
165	}
166
167	if ( service == NULL )
168	{
169		err = NS_ERROR_FAILURE;
170		goto exit;
171	}
172
173	dnsErr = DNSServiceBrowse( &service->m_subRef, kDNSServiceFlagsShareConnection, interfaceIndex, NS_ConvertUTF16toUTF8( regtype ).get(), NS_ConvertUTF16toUTF8( domain ).get(), ( DNSServiceBrowseReply ) BrowseReply, service );
174
175	if ( dnsErr != kDNSServiceErr_NoError )
176	{
177		err = NS_ERROR_FAILURE;
178		goto exit;
179	}
180
181	listener->AddRef();
182	service->AddRef();
183	*_retval = service;
184	err = NS_OK;
185
186exit:
187
188	if ( err && service )
189	{
190		delete service;
191		service = NULL;
192	}
193
194	return err;
195}
196
197
198/* IDNSSDService resolve (in long interfaceIndex, in AString name, in AString regtype, in AString domain, in IDNSSDResolveListener listener); */
199NS_IMETHODIMP
200CDNSSDService::Resolve(PRInt32 interfaceIndex, const nsAString & name, const nsAString & regtype, const nsAString & domain, IDNSSDResolveListener *listener, IDNSSDService **_retval NS_OUTPARAM)
201{
202    CDNSSDService	*	service;
203	DNSServiceErrorType dnsErr;
204	nsresult			err;
205
206	*_retval = NULL;
207
208	if ( !m_mainRef )
209	{
210		err = NS_ERROR_NOT_AVAILABLE;
211		goto exit;
212	}
213
214	try
215	{
216		service = new CDNSSDService( m_mainRef, listener );
217	}
218	catch ( ... )
219	{
220		service = NULL;
221	}
222
223	if ( service == NULL )
224	{
225		err = NS_ERROR_FAILURE;
226		goto exit;
227	}
228
229	dnsErr = DNSServiceResolve( &service->m_subRef, kDNSServiceFlagsShareConnection, interfaceIndex, NS_ConvertUTF16toUTF8( name ).get(), NS_ConvertUTF16toUTF8( regtype ).get(), NS_ConvertUTF16toUTF8( domain ).get(), ( DNSServiceResolveReply ) ResolveReply, service );
230
231	if ( dnsErr != kDNSServiceErr_NoError )
232	{
233		err = NS_ERROR_FAILURE;
234		goto exit;
235	}
236
237	listener->AddRef();
238	service->AddRef();
239	*_retval = service;
240	err = NS_OK;
241
242exit:
243
244	if ( err && service )
245	{
246		delete service;
247		service = NULL;
248	}
249
250	return err;
251}
252
253
254/* void stop (); */
255NS_IMETHODIMP
256CDNSSDService::Stop()
257{
258    if ( m_subRef )
259	{
260		DNSServiceRefDeallocate( m_subRef );
261		m_subRef = NULL;
262	}
263
264	return NS_OK;
265}
266
267
268void
269CDNSSDService::Read( void * arg )
270{
271	NS_PRECONDITION( arg != NULL, "arg is NULL" );
272
273	NS_DispatchToMainThread( ( CDNSSDService* ) arg );
274}
275
276
277NS_IMETHODIMP
278CDNSSDService::Run()
279{
280	nsresult err = NS_OK;
281
282	NS_PRECONDITION( m_mainRef != NULL, "m_mainRef is NULL" );
283
284	m_job = NULL;
285
286	if ( PR_Available( m_fileDesc ) > 0 )
287	{
288		if ( DNSServiceProcessResult( m_mainRef ) != kDNSServiceErr_NoError )
289		{
290			err = NS_ERROR_FAILURE;
291		}
292	}
293
294	if ( !err )
295	{
296		err = SetupNotifications();
297	}
298
299	return err;
300}
301
302
303void DNSSD_API
304CDNSSDService::BrowseReply
305		(
306		DNSServiceRef		sdRef,
307		DNSServiceFlags		flags,
308		uint32_t			interfaceIndex,
309		DNSServiceErrorType	errorCode,
310		const char		*	serviceName,
311		const char		*	regtype,
312		const char		*	replyDomain,
313		void			*	context
314		)
315{
316	CDNSSDService * self = ( CDNSSDService* ) context;
317
318	// This should never be NULL, but let's be defensive.
319
320	if ( self != NULL )
321	{
322		IDNSSDBrowseListener * listener = ( IDNSSDBrowseListener* ) self->m_listener;
323
324		// Same for this
325
326		if ( listener != NULL )
327		{
328			listener->OnBrowse( self, ( flags & kDNSServiceFlagsAdd ) ? PR_TRUE : PR_FALSE, interfaceIndex, errorCode, NS_ConvertUTF8toUTF16( serviceName ), NS_ConvertUTF8toUTF16( regtype ), NS_ConvertUTF8toUTF16( replyDomain ) );
329		}
330	}
331}
332
333
334void DNSSD_API
335CDNSSDService::ResolveReply
336		(
337		DNSServiceRef			sdRef,
338		DNSServiceFlags			flags,
339		uint32_t				interfaceIndex,
340		DNSServiceErrorType		errorCode,
341		const char			*	fullname,
342		const char			*	hosttarget,
343		uint16_t				port,
344		uint16_t				txtLen,
345		const unsigned char	*	txtRecord,
346		void				*	context
347		)
348{
349	CDNSSDService * self = ( CDNSSDService* ) context;
350
351	// This should never be NULL, but let's be defensive.
352
353	if ( self != NULL )
354	{
355		IDNSSDResolveListener * listener = ( IDNSSDResolveListener* ) self->m_listener;
356
357		// Same for this
358
359		if ( listener != NULL )
360		{
361			std::string		path = "";
362			const void	*	value = NULL;
363			uint8_t			valueLen = 0;
364
365			value = TXTRecordGetValuePtr( txtLen, txtRecord, "path", &valueLen );
366
367			if ( value && valueLen )
368			{
369				char * temp;
370
371				temp = new char[ valueLen + 2 ];
372
373				if ( temp )
374				{
375					char * dst = temp;
376
377					memset( temp, 0, valueLen + 2 );
378
379					if ( ( ( char* ) value )[ 0 ] != '/' )
380					{
381						*dst++ = '/';
382					}
383
384					memcpy( dst, value, valueLen );
385					path = temp;
386					delete [] temp;
387				}
388			}
389
390			listener->OnResolve( self, interfaceIndex, errorCode, NS_ConvertUTF8toUTF16( fullname ), NS_ConvertUTF8toUTF16( hosttarget ) , ntohs( port ), NS_ConvertUTF8toUTF16( path.c_str() ) );
391		}
392	}
393}
394
395