<rdar://problem/7402802> Build Firefox plugin for 2.0
[people/sha0/mDNSResponder.git] / Clients / FirefoxExtension / CDNSSDService.cpp
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
26 NS_IMPL_ISUPPORTS2(CDNSSDService, IDNSSDService, nsIRunnable)
27
28 CDNSSDService::CDNSSDService()
29 :
30         m_threadPool( NULL ),
31         m_mainRef( NULL ),
32         m_subRef( NULL ),
33         m_listener( NULL ),
34         m_fileDesc( NULL ),
35         m_job( NULL )
36 {
37         nsresult err;
38
39         if ( DNSServiceCreateConnection( &m_mainRef ) != kDNSServiceErr_NoError )
40         {
41                 err = NS_ERROR_FAILURE;
42                 goto exit;
43         }
44
45         if ( ( m_fileDesc = PR_ImportTCPSocket( DNSServiceRefSockFD( m_mainRef ) ) ) == NULL )
46         {
47                 err = NS_ERROR_FAILURE;
48                 goto exit;
49         }
50
51         if ( ( m_threadPool = PR_CreateThreadPool( 1, 1, 8192 ) ) == NULL )
52         {
53                 err = NS_ERROR_FAILURE;
54                 goto exit;
55         }
56         
57         err = SetupNotifications();
58
59 exit:
60
61         if ( err != NS_OK )
62         {
63                 Cleanup();
64         }
65 }
66
67
68 CDNSSDService::CDNSSDService( DNSServiceRef ref, nsISupports * listener )
69 :
70         m_threadPool( NULL ),
71         m_mainRef( ref ),
72         m_subRef( ref ),
73         m_listener( listener ),
74         m_fileDesc( NULL ),
75         m_job( NULL )
76 {
77 }
78
79
80 CDNSSDService::~CDNSSDService()
81 {
82         Cleanup();
83 }
84
85
86 void
87 CDNSSDService::Cleanup()
88 {
89         if ( m_job )
90         {
91                 PR_CancelJob( m_job );
92                 m_job = NULL;
93         }
94
95         if ( m_threadPool != NULL )
96         {
97                 PR_ShutdownThreadPool( m_threadPool );
98                 m_threadPool = NULL;
99         }
100         
101         if ( m_fileDesc != NULL )
102         {
103                 PR_Close( m_fileDesc );
104                 m_fileDesc = NULL;
105         }
106         
107         if ( m_subRef )
108         {
109                 DNSServiceRefDeallocate( m_subRef );
110                 m_subRef = NULL;
111         }
112         else if ( m_mainRef )
113         {
114                 DNSServiceRefDeallocate( m_mainRef );
115                 m_mainRef = NULL;
116         }
117 }
118
119
120 nsresult
121 CDNSSDService::SetupNotifications()
122 {
123         NS_PRECONDITION( m_threadPool != NULL, "m_threadPool is NULL" );
124         NS_PRECONDITION( m_fileDesc != NULL, "m_fileDesc is NULL" );
125         NS_PRECONDITION( m_job == NULL, "m_job is not NULL" );
126
127         m_iod.socket    = m_fileDesc;
128         m_iod.timeout   = PR_INTERVAL_MAX;
129         m_job                   = PR_QueueJob_Read( m_threadPool, &m_iod, Read, this, PR_FALSE );       
130         return ( m_job ) ? NS_OK : NS_ERROR_FAILURE;
131 }
132
133
134 /* IDNSSDService browse (in long interfaceIndex, in AString regtype, in AString domain, in IDNSSDBrowseListener listener); */
135 NS_IMETHODIMP
136 CDNSSDService::Browse(PRInt32 interfaceIndex, const nsAString & regtype, const nsAString & domain, IDNSSDBrowseListener *listener, IDNSSDService **_retval NS_OUTPARAM)
137 {
138         CDNSSDService   *       service = NULL;
139         DNSServiceErrorType dnsErr      = 0;
140         nsresult                        err             = 0;
141
142         *_retval = NULL;
143         
144         if ( !m_mainRef )
145         {
146                 err = NS_ERROR_NOT_AVAILABLE;
147                 goto exit;
148         }
149
150         try
151         {
152                 service = new CDNSSDService( m_mainRef, listener );
153         }
154         catch ( ... )
155         {
156                 service = NULL;
157         }
158         
159         if ( service == NULL )
160         {
161                 err = NS_ERROR_FAILURE;
162                 goto exit;
163         }
164         
165         dnsErr = DNSServiceBrowse( &service->m_subRef, kDNSServiceFlagsShareConnection, interfaceIndex, NS_ConvertUTF16toUTF8( regtype ).get(), NS_ConvertUTF16toUTF8( domain ).get(), ( DNSServiceBrowseReply ) BrowseReply, service );
166         
167         if ( dnsErr != kDNSServiceErr_NoError )
168         {
169                 err = NS_ERROR_FAILURE;
170                 goto exit;
171         }
172         
173         listener->AddRef();
174         service->AddRef();
175         *_retval = service;
176         err = NS_OK;
177         
178 exit:
179
180         if ( err && service )
181         {
182                 delete service;
183                 service = NULL;
184         }
185         
186         return err;
187 }
188
189
190 /* IDNSSDService resolve (in long interfaceIndex, in AString name, in AString regtype, in AString domain, in IDNSSDResolveListener listener); */
191 NS_IMETHODIMP
192 CDNSSDService::Resolve(PRInt32 interfaceIndex, const nsAString & name, const nsAString & regtype, const nsAString & domain, IDNSSDResolveListener *listener, IDNSSDService **_retval NS_OUTPARAM)
193 {
194     CDNSSDService       *       service;
195         DNSServiceErrorType dnsErr;
196         nsresult                        err;
197
198         *_retval = NULL;
199         
200         if ( !m_mainRef )
201         {
202                 err = NS_ERROR_NOT_AVAILABLE;
203                 goto exit;
204         }
205
206         try
207         {
208                 service = new CDNSSDService( m_mainRef, listener );
209         }
210         catch ( ... )
211         {
212                 service = NULL;
213         }
214         
215         if ( service == NULL )
216         {
217                 err = NS_ERROR_FAILURE;
218                 goto exit;
219         }
220
221         dnsErr = DNSServiceResolve( &service->m_subRef, kDNSServiceFlagsShareConnection, interfaceIndex, NS_ConvertUTF16toUTF8( name ).get(), NS_ConvertUTF16toUTF8( regtype ).get(), NS_ConvertUTF16toUTF8( domain ).get(), ( DNSServiceResolveReply ) ResolveReply, service );
222         
223         if ( dnsErr != kDNSServiceErr_NoError )
224         {
225                 err = NS_ERROR_FAILURE;
226                 goto exit;
227         }
228         
229         listener->AddRef();
230         service->AddRef();
231         *_retval = service;
232         err = NS_OK;
233         
234 exit:
235         
236         if ( err && service )
237         {
238                 delete service;
239                 service = NULL;
240         }
241         
242         return err;
243 }
244
245
246 /* void stop (); */
247 NS_IMETHODIMP
248 CDNSSDService::Stop()
249 {
250     if ( m_subRef )
251         {
252                 DNSServiceRefDeallocate( m_subRef );
253                 m_subRef = NULL;
254         }
255         
256         return NS_OK;
257 }
258
259
260 void
261 CDNSSDService::Read( void * arg )
262 {
263         NS_PRECONDITION( arg != NULL, "arg is NULL" );
264         
265         NS_DispatchToMainThread( ( CDNSSDService* ) arg );
266 }
267
268
269 NS_IMETHODIMP
270 CDNSSDService::Run()
271 {
272         nsresult err;
273         
274         NS_PRECONDITION( m_mainRef != NULL, "m_mainRef is NULL" );
275
276         m_job = NULL;
277
278         if ( DNSServiceProcessResult( m_mainRef ) == kDNSServiceErr_NoError )
279         {
280                 err = SetupNotifications();
281         }
282         else
283         {
284                 err = NS_ERROR_FAILURE;
285         }
286         
287         return err;
288 }
289
290
291 void DNSSD_API
292 CDNSSDService::BrowseReply
293                 (
294                 DNSServiceRef           sdRef,
295                 DNSServiceFlags         flags,
296                 uint32_t                        interfaceIndex,
297                 DNSServiceErrorType     errorCode,
298                 const char              *       serviceName,
299                 const char              *       regtype,
300                 const char              *       replyDomain,
301                 void                    *       context
302                 )
303 {
304         CDNSSDService * self = ( CDNSSDService* ) context;
305
306         // This should never be NULL, but let's be defensive.
307         
308         if ( self != NULL )
309         {
310                 IDNSSDBrowseListener * listener = ( IDNSSDBrowseListener* ) self->m_listener;
311
312                 // Same for this
313
314                 if ( listener != NULL )
315                 {
316                         listener->OnBrowse( self, ( flags & kDNSServiceFlagsAdd ) ? PR_TRUE : PR_FALSE, interfaceIndex, errorCode, NS_ConvertUTF8toUTF16( serviceName ), NS_ConvertUTF8toUTF16( regtype ), NS_ConvertUTF8toUTF16( replyDomain ) );
317                 }
318         }
319 }
320
321
322 void DNSSD_API
323 CDNSSDService::ResolveReply
324                 (
325                 DNSServiceRef                   sdRef,
326                 DNSServiceFlags                 flags,
327                 uint32_t                                interfaceIndex,
328                 DNSServiceErrorType             errorCode,
329                 const char                      *       fullname,
330                 const char                      *       hosttarget,
331                 uint16_t                                port,
332                 uint16_t                                txtLen,
333                 const unsigned char     *       txtRecord,
334                 void                            *       context
335                 )
336 {
337         CDNSSDService * self = ( CDNSSDService* ) context;
338         
339         // This should never be NULL, but let's be defensive.
340         
341         if ( self != NULL )
342         {
343                 IDNSSDResolveListener * listener = ( IDNSSDResolveListener* ) self->m_listener;
344                 
345                 // Same for this
346
347                 if ( listener != NULL )
348                 {
349                         std::string             path = "";
350                         const void      *       value = NULL;
351                         uint8_t                 valueLen = 0;
352
353                         value = TXTRecordGetValuePtr( txtLen, txtRecord, "path", &valueLen );
354                         
355                         if ( value && valueLen )
356                         {
357                                 char * temp;
358                                 
359                                 temp = new char[ valueLen + 1 ];
360                                 
361                                 if ( temp )
362                                 {
363                                         memset( temp, 0, valueLen + 1 );
364                                         memcpy( temp, value, valueLen );
365                                         path = temp;
366                                         delete [] temp;
367                                 }
368                         }
369
370                         listener->OnResolve( self, interfaceIndex, errorCode, NS_ConvertUTF8toUTF16( fullname ), NS_ConvertUTF8toUTF16( hosttarget ) , ntohs( port ), NS_ConvertUTF8toUTF16( path.c_str() ) );
371                 }
372         }
373 }
374