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