1/* -*- Mode: C; tab-width: 4 -*- 2 * 3 * Copyright (c) 2002-2003 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#import <Cocoa/Cocoa.h> 19#include <sys/types.h> 20#include <sys/socket.h> 21#include <net/if.h> 22#include <arpa/inet.h> 23#include <netdb.h> 24#include <sys/select.h> 25#include <netinet/in.h> 26#include <unistd.h> 27#include <dns_sd.h> 28 29@class ServiceController; // holds state corresponding to outstanding DNSServiceRef 30 31@interface BrowserController : NSObject 32{ 33 IBOutlet id nameField; 34 IBOutlet id typeField; 35 36 IBOutlet id serviceDisplayTable; 37 IBOutlet id typeColumn; 38 IBOutlet id nameColumn; 39 IBOutlet id serviceTypeField; 40 IBOutlet id serviceNameField; 41 42 IBOutlet id hostField; 43 IBOutlet id ipAddressField; 44 IBOutlet id ip6AddressField; 45 IBOutlet id portField; 46 IBOutlet id interfaceField; 47 IBOutlet id textField; 48 49 NSMutableArray *_srvtypeKeys; 50 NSMutableArray *_srvnameKeys; 51 NSMutableArray *_sortedServices; 52 NSMutableDictionary *_servicesDict; 53 54 ServiceController *_serviceBrowser; 55 ServiceController *_serviceResolver; 56 ServiceController *_ipv4AddressResolver; 57 ServiceController *_ipv6AddressResolver; 58} 59 60- (void)notifyTypeSelectionChange:(NSNotification*)note; 61- (void)notifyNameSelectionChange:(NSNotification*)note; 62 63- (IBAction)connect:(id)sender; 64 65- (IBAction)handleTableClick:(id)sender; 66- (IBAction)removeSelected:(id)sender; 67- (IBAction)addNewService:(id)sender; 68 69- (IBAction)update:(NSString *)Type; 70 71- (void)updateBrowseWithName:(const char *)name type:(const char *)resulttype domain:(const char *)domain interface:(uint32_t)interface flags:(DNSServiceFlags)flags; 72- (void)resolveClientWitHost:(NSString *)host port:(uint16_t)port interfaceIndex:(uint32_t)interface txtRecord:(const char*)txtRecord txtLen:(uint16_t)txtLen; 73- (void)updateAddress:(uint16_t)rrtype addr:(const void *)buff addrLen:(uint16_t)addrLen host:(const char*)host interfaceIndex:(uint32_t)interface more:(boolean_t)moreToCome; 74 75- (void)_cancelPendingResolve; 76- (void)_clearResolvedInfo; 77 78@end 79 80// The ServiceController manages cleanup of DNSServiceRef & runloop info for an outstanding request 81@interface ServiceController : NSObject 82{ 83 DNSServiceRef fServiceRef; 84 CFSocketRef fSocketRef; 85 CFRunLoopSourceRef fRunloopSrc; 86} 87 88- (id)initWithServiceRef:(DNSServiceRef)ref; 89- (void)addToCurrentRunLoop; 90- (DNSServiceRef)serviceRef; 91- (void)dealloc; 92 93@end // interface ServiceController 94 95 96static void 97ProcessSockData(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) 98{ 99 DNSServiceRef serviceRef = (DNSServiceRef)info; 100 DNSServiceErrorType err = DNSServiceProcessResult(serviceRef); 101 if (err != kDNSServiceErr_NoError) { 102 printf("DNSServiceProcessResult() returned an error! %d\n", err); 103 } 104} 105 106 107static void 108ServiceBrowseReply(DNSServiceRef sdRef, DNSServiceFlags servFlags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, 109 const char *serviceName, const char *regtype, const char *replyDomain, void *context) 110{ 111 if (errorCode == kDNSServiceErr_NoError) { 112 [(BrowserController*)context updateBrowseWithName:serviceName type:regtype domain:replyDomain interface:interfaceIndex flags:servFlags]; 113 } else { 114 printf("ServiceBrowseReply got an error! %d\n", errorCode); 115 } 116} 117 118 119static void 120ServiceResolveReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, 121 const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context) 122{ 123 if (errorCode == kDNSServiceErr_NoError) { 124 [(BrowserController*)context resolveClientWitHost:[NSString stringWithUTF8String:hosttarget] port:port interfaceIndex:interfaceIndex txtRecord:txtRecord txtLen:txtLen]; 125 } else { 126 printf("ServiceResolveReply got an error! %d\n", errorCode); 127 } 128} 129 130 131static void 132QueryRecordReply(DNSServiceRef DNSServiceRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, 133 const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) 134{ 135 if (errorCode == kDNSServiceErr_NoError) { 136 [(BrowserController*)context updateAddress:rrtype addr:rdata addrLen:rdlen host:fullname interfaceIndex:interfaceIndex more:(flags & kDNSServiceFlagsMoreComing)]; 137 } else { 138 printf("QueryRecordReply got an error! %d\n", errorCode); 139 } 140} 141 142 143static void 144InterfaceIndexToName(uint32_t interface, char *interfaceName) 145{ 146 assert(interfaceName); 147 148 if (interface == kDNSServiceInterfaceIndexAny) { 149 // All active network interfaces. 150 strlcpy(interfaceName, "all", IF_NAMESIZE); 151 } else if (interface == kDNSServiceInterfaceIndexLocalOnly) { 152 // Only available locally on this machine. 153 strlcpy(interfaceName, "local", IF_NAMESIZE); 154 } else if (interface == kDNSServiceInterfaceIndexP2P) { 155 // Peer-to-peer. 156 strlcpy(interfaceName, "p2p", IF_NAMESIZE); 157 } else { 158 // Converts interface index to interface name. 159 if_indextoname(interface, interfaceName); 160 } 161} 162 163 164@implementation BrowserController //Begin implementation of BrowserController methods 165 166- (void)registerDefaults 167{ 168 NSMutableDictionary *regDict = [NSMutableDictionary dictionary]; 169 170 NSArray *typeArray = [NSArray arrayWithObjects:@"_afpovertcp._tcp", 171 @"_smb._tcp", 172 @"_rfb._tcp", 173 @"_ssh._tcp", 174 @"_ftp._tcp", 175 @"_http._tcp", 176 @"_printer._tcp", 177 @"_ipp._tcp", 178 @"_airport._tcp", 179 @"_presence._tcp", 180 @"_daap._tcp", 181 @"_dpap._tcp", 182 nil]; 183 184 NSArray *nameArray = [NSArray arrayWithObjects:@"AppleShare Servers", 185 @"Windows Sharing", 186 @"Screen Sharing", 187 @"Secure Shell", 188 @"FTP Servers", 189 @"Web Servers", 190 @"LPR Printers", 191 @"IPP Printers", 192 @"AirPort Base Stations", 193 @"iChat Buddies", 194 @"iTunes Libraries", 195 @"iPhoto Libraries", 196 nil]; 197 198 [regDict setObject:typeArray forKey:@"SrvTypeKeys"]; 199 [regDict setObject:nameArray forKey:@"SrvNameKeys"]; 200 201 [[NSUserDefaults standardUserDefaults] registerDefaults:regDict]; 202} 203 204 205- (id)init 206{ 207 self = [super init]; 208 if (self) { 209 _srvtypeKeys = nil; 210 _srvnameKeys = nil; 211 _serviceBrowser = nil; 212 _serviceResolver = nil; 213 _ipv4AddressResolver = nil; 214 _ipv6AddressResolver = nil; 215 _sortedServices = [[NSMutableArray alloc] init]; 216 _servicesDict = [[NSMutableDictionary alloc] init]; 217 } 218 return self; 219} 220 221 222- (void)awakeFromNib 223{ 224 [typeField sizeLastColumnToFit]; 225 [nameField sizeLastColumnToFit]; 226 [nameField setDoubleAction:@selector(connect:)]; 227 228 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notifyTypeSelectionChange:) name:NSTableViewSelectionDidChangeNotification object:typeField]; 229 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notifyNameSelectionChange:) name:NSTableViewSelectionDidChangeNotification object:nameField]; 230 231 _srvtypeKeys = [[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTypeKeys"] mutableCopy]; 232 _srvnameKeys = [[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvNameKeys"] mutableCopy]; 233 234 if (!_srvtypeKeys || !_srvnameKeys) { 235 [_srvtypeKeys release]; 236 [_srvnameKeys release]; 237 [self registerDefaults]; 238 _srvtypeKeys = [[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTypeKeys"] mutableCopy]; 239 _srvnameKeys = [[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvNameKeys"] mutableCopy]; 240 } 241 242 [typeField reloadData]; 243} 244 245 246- (void)dealloc 247{ 248 [_srvtypeKeys release]; 249 [_srvnameKeys release]; 250 [_servicesDict release]; 251 [_sortedServices release]; 252 [super dealloc]; 253} 254 255 256-(void)tableView:(NSTableView *)theTableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row 257{ 258 if (row < 0) return; 259} 260 261 262- (int)numberOfRowsInTableView:(NSTableView *)theTableView //Begin mandatory TableView methods 263{ 264 if (theTableView == typeField) { 265 return [_srvnameKeys count]; 266 } 267 if (theTableView == nameField) { 268 return [_servicesDict count]; 269 } 270 if (theTableView == serviceDisplayTable) { 271 return [_srvnameKeys count]; 272 } 273 return 0; 274} 275 276 277- (id)tableView:(NSTableView *)theTableView objectValueForTableColumn:(NSTableColumn *)theColumn row:(int)rowIndex 278{ 279 if (theTableView == typeField) { 280 return [_srvnameKeys objectAtIndex:rowIndex]; 281 } 282 if (theTableView == nameField) { 283 return [[_servicesDict objectForKey:[_sortedServices objectAtIndex:rowIndex]] name]; 284 } 285 if (theTableView == serviceDisplayTable) { 286 if (theColumn == typeColumn) { 287 return [_srvtypeKeys objectAtIndex:rowIndex]; 288 } 289 if (theColumn == nameColumn) { 290 return [_srvnameKeys objectAtIndex:rowIndex]; 291 } 292 return nil; 293 } 294 295 return nil; 296} 297 298 299- (void)notifyTypeSelectionChange:(NSNotification*)note 300{ 301 [self _cancelPendingResolve]; 302 303 int index = [[note object] selectedRow]; 304 if (index != -1) { 305 [self update:[_srvtypeKeys objectAtIndex:index]]; 306 } else { 307 [self update:nil]; 308 } 309} 310 311 312- (void)notifyNameSelectionChange:(NSNotification*)note 313{ 314 [self _cancelPendingResolve]; 315 316 int index = [[note object] selectedRow]; 317 if (index == -1) { 318 return; 319 } 320 321 // Get the currently selected service 322 NSNetService *service = [_servicesDict objectForKey:[_sortedServices objectAtIndex:index]]; 323 324 DNSServiceRef serviceRef; 325 DNSServiceErrorType err = DNSServiceResolve(&serviceRef, 326 (DNSServiceFlags)0, 327 kDNSServiceInterfaceIndexAny, 328 (const char *)[[service name] UTF8String], 329 (const char *)[[service type] UTF8String], 330 (const char *)[[service domain] UTF8String], 331 (DNSServiceResolveReply)ServiceResolveReply, 332 self); 333 334 if (kDNSServiceErr_NoError == err) { 335 _serviceResolver = [[ServiceController alloc] initWithServiceRef:serviceRef]; 336 [_serviceResolver addToCurrentRunLoop]; 337 } 338} 339 340 341- (IBAction)update:(NSString *)theType 342{ 343 [_servicesDict removeAllObjects]; 344 [_sortedServices removeAllObjects]; 345 [nameField reloadData]; 346 347 // get rid of the previous browser if one exists 348 if (_serviceBrowser != nil) { 349 [_serviceBrowser release]; 350 _serviceBrowser = nil; 351 } 352 353 if (theType) { 354 DNSServiceRef serviceRef; 355 DNSServiceErrorType err = DNSServiceBrowse(&serviceRef, (DNSServiceFlags)0, 0, [theType UTF8String], NULL, ServiceBrowseReply, self); 356 if (kDNSServiceErr_NoError == err) { 357 _serviceBrowser = [[ServiceController alloc] initWithServiceRef:serviceRef]; 358 [_serviceBrowser addToCurrentRunLoop]; 359 } 360 } 361} 362 363 364- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication 365{ 366 return YES; 367} 368 369 370- (void)updateBrowseWithName:(const char *)name type:(const char *)type domain:(const char *)domain interface:(uint32_t)interface flags:(DNSServiceFlags)flags 371{ 372 NSString *key = [NSString stringWithFormat:@"%s.%s%s%d", name, type, domain, interface]; 373 NSNetService *service = [[NSNetService alloc] initWithDomain:[NSString stringWithUTF8String:domain] type:[NSString stringWithUTF8String:type] name:[NSString stringWithUTF8String:name]]; 374 375 if (flags & kDNSServiceFlagsAdd) { 376 [_servicesDict setObject:service forKey:key]; 377 } else { 378 [_servicesDict removeObjectForKey:key]; 379 } 380 381 // If not expecting any more data, then reload (redraw) TableView with newly found data 382 if (!(flags & kDNSServiceFlagsMoreComing)) { 383 384 // Save the current TableView selection 385 int index = [nameField selectedRow]; 386 NSString *selected = (index != -1) ? [[_sortedServices objectAtIndex:index] copy] : nil; 387 388 [_sortedServices release]; 389 _sortedServices = [[_servicesDict allKeys] mutableCopy]; 390 [_sortedServices sortUsingSelector:@selector(caseInsensitiveCompare:)]; 391 [nameField reloadData]; 392 393 // Restore the previous TableView selection 394 index = selected ? [_sortedServices indexOfObject:selected] : NSNotFound; 395 if (index != NSNotFound) { 396 [nameField selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO]; 397 [nameField scrollRowToVisible:index]; 398 } 399 400 [selected release]; 401 } 402 403 [service release]; 404 405 return; 406} 407 408 409- (void)resolveClientWitHost:(NSString *)host port:(uint16_t)port interfaceIndex:(uint32_t)interface txtRecord:(const char*)txtRecord txtLen:(uint16_t)txtLen 410{ 411 DNSServiceRef serviceRef; 412 413 if (_ipv4AddressResolver) { 414 [_ipv4AddressResolver release]; 415 _ipv4AddressResolver = nil; 416 } 417 418 if (_ipv6AddressResolver) { 419 [_ipv6AddressResolver release]; 420 _ipv6AddressResolver = nil; 421 } 422 423 // Start an async lookup for IPv4 addresses 424 DNSServiceErrorType err = DNSServiceQueryRecord(&serviceRef, (DNSServiceFlags)0, interface, [host UTF8String], kDNSServiceType_A, kDNSServiceClass_IN, QueryRecordReply, self); 425 if (err == kDNSServiceErr_NoError) { 426 _ipv4AddressResolver = [[ServiceController alloc] initWithServiceRef:serviceRef]; 427 [_ipv4AddressResolver addToCurrentRunLoop]; 428 } 429 430 // Start an async lookup for IPv6 addresses 431 err = DNSServiceQueryRecord(&serviceRef, (DNSServiceFlags)0, interface, [host UTF8String], kDNSServiceType_AAAA, kDNSServiceClass_IN, QueryRecordReply, self); 432 if (err == kDNSServiceErr_NoError) { 433 _ipv6AddressResolver = [[ServiceController alloc] initWithServiceRef:serviceRef]; 434 [_ipv6AddressResolver addToCurrentRunLoop]; 435 } 436 437 char interfaceName[IF_NAMESIZE]; 438 InterfaceIndexToName(interface, interfaceName); 439 440 [hostField setStringValue:host]; 441 [interfaceField setStringValue:[NSString stringWithUTF8String:interfaceName]]; 442 [portField setIntValue:ntohs(port)]; 443 444 // kind of a hack: munge txtRecord so it's human-readable 445 if (txtLen > 0) { 446 char *readableText = (char*) malloc(txtLen); 447 if (readableText != nil) { 448 ByteCount index, subStrLen; 449 memcpy(readableText, txtRecord, txtLen); 450 for (index=0; index < txtLen - 1; index += subStrLen + 1) { 451 subStrLen = readableText[index]; 452 readableText[index] = ' '; 453 } 454 [textField setStringValue:[NSString stringWithCString:&readableText[1] length:txtLen - 1]]; 455 free(readableText); 456 } 457 } 458} 459 460 461- (void)updateAddress:(uint16_t)rrtype addr:(const void *)buff addrLen:(uint16_t)addrLen host:(const char*) host interfaceIndex:(uint32_t)interface more:(boolean_t)moreToCome 462{ 463 char addrBuff[256]; 464 465 if (rrtype == kDNSServiceType_A) { 466 inet_ntop(AF_INET, buff, addrBuff, sizeof(addrBuff)); 467 if ([[ipAddressField stringValue] length] > 0) { 468 [ipAddressField setStringValue:[NSString stringWithFormat:@"%@, ", [ipAddressField stringValue]]]; 469 } 470 [ipAddressField setStringValue:[NSString stringWithFormat:@"%@%s", [ipAddressField stringValue], addrBuff]]; 471 472 if (!moreToCome) { 473 [_ipv4AddressResolver release]; 474 _ipv4AddressResolver = nil; 475 } 476 } else if (rrtype == kDNSServiceType_AAAA) { 477 inet_ntop(AF_INET6, buff, addrBuff, sizeof(addrBuff)); 478 if ([[ip6AddressField stringValue] length] > 0) { 479 [ip6AddressField setStringValue:[NSString stringWithFormat:@"%@, ", [ip6AddressField stringValue]]]; 480 } 481 [ip6AddressField setStringValue:[NSString stringWithFormat:@"%@%s", [ip6AddressField stringValue], addrBuff]]; 482 483 if (!moreToCome) { 484 [_ipv6AddressResolver release]; 485 _ipv6AddressResolver = nil; 486 } 487 } 488} 489 490 491- (void)connect:(id)sender 492{ 493 NSString *host = [hostField stringValue]; 494 NSString *txtRecord = [textField stringValue]; 495 int port = [portField intValue]; 496 497 int index = [nameField selectedRow]; 498 NSString *selected = (index >= 0) ? [_sortedServices objectAtIndex:index] : nil; 499 NSString *type = [[_servicesDict objectForKey:selected] type]; 500 501 if ([type isEqual:@"_http._tcp."]) { 502 NSString *pathDelim = @"path="; 503 NSRange where; 504 505 // If the TXT record specifies a path, extract it. 506 where = [txtRecord rangeOfString:pathDelim options:NSCaseInsensitiveSearch]; 507 if (where.length) { 508 NSRange targetRange = { where.location + where.length, [txtRecord length] - where.location - where.length }; 509 NSRange endDelim = [txtRecord rangeOfString:@"\n" options:kNilOptions range:targetRange]; 510 511 if (endDelim.length) // if a delimiter was found, truncate the target range 512 targetRange.length = endDelim.location - targetRange.location; 513 514 NSString *path = [txtRecord substringWithRange:targetRange]; 515 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%d%@", host, port, path]]]; 516 } else { 517 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%d", host, port]]]; 518 } 519 } 520 else if ([type isEqual:@"_ftp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ftp://%@:%d/", host, port]]]; 521 else if ([type isEqual:@"_ssh._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ssh://%@:%d/", host, port]]]; 522 else if ([type isEqual:@"_afpovertcp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"afp://%@:%d/", host, port]]]; 523 else if ([type isEqual:@"_smb._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"smb://%@:%d/", host, port]]]; 524 else if ([type isEqual:@"_rfb._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"vnc://%@:%d/", host, port]]]; 525 526 return; 527} 528 529 530- (IBAction)handleTableClick:(id)sender 531{ 532 //populate the text fields 533} 534 535 536- (IBAction)removeSelected:(id)sender 537{ 538 // remove the selected row and force a refresh 539 540 int selectedRow = [serviceDisplayTable selectedRow]; 541 542 if (selectedRow) { 543 544 [_srvtypeKeys removeObjectAtIndex:selectedRow]; 545 [_srvnameKeys removeObjectAtIndex:selectedRow]; 546 547 [[NSUserDefaults standardUserDefaults] setObject:_srvtypeKeys forKey:@"SrvTypeKeys"]; 548 [[NSUserDefaults standardUserDefaults] setObject:_srvnameKeys forKey:@"SrvNameKeys"]; 549 550 [typeField reloadData]; 551 [serviceDisplayTable reloadData]; 552 } 553} 554 555 556- (IBAction)addNewService:(id)sender 557{ 558 // add new entries from the edit fields to the arrays for the defaults 559 NSString *newType = [serviceTypeField stringValue]; 560 NSString *newName = [serviceNameField stringValue]; 561 562 // 3282283: trim trailing '.' from service type field 563 if ([newType length] && [newType hasSuffix:@"."]) 564 newType = [newType substringToIndex:[newType length] - 1]; 565 566 if ([newType length] && [newName length]) { 567 [_srvtypeKeys addObject:newType]; 568 [_srvnameKeys addObject:newName]; 569 570 [[NSUserDefaults standardUserDefaults] setObject:_srvtypeKeys forKey:@"SrvTypeKeys"]; 571 [[NSUserDefaults standardUserDefaults] setObject:_srvnameKeys forKey:@"SrvNameKeys"]; 572 573 [typeField reloadData]; 574 [serviceDisplayTable reloadData]; 575 } 576} 577 578 579- (void)_cancelPendingResolve 580{ 581 [_ipv4AddressResolver release]; 582 _ipv4AddressResolver = nil; 583 584 [_ipv6AddressResolver release]; 585 _ipv6AddressResolver = nil; 586 587 [_serviceResolver release]; 588 _serviceResolver = nil; 589 590 [self _clearResolvedInfo]; 591} 592 593 594- (void)_clearResolvedInfo 595{ 596 [hostField setStringValue:@""]; 597 [ipAddressField setStringValue:@""]; 598 [ip6AddressField setStringValue:@""]; 599 [portField setStringValue:@""]; 600 [interfaceField setStringValue:@""]; 601 [textField setStringValue:@""]; 602} 603 604@end // implementation BrowserController 605 606 607@implementation ServiceController : NSObject 608{ 609 DNSServiceRef fServiceRef; 610 CFSocketRef fSocketRef; 611 CFRunLoopSourceRef fRunloopSrc; 612} 613 614 615- (id)initWithServiceRef:(DNSServiceRef)ref 616{ 617 self = [super init]; 618 if (self) { 619 fServiceRef = ref; 620 fSocketRef = NULL; 621 fRunloopSrc = NULL; 622 } 623 return self; 624} 625 626 627- (void)addToCurrentRunLoop 628{ 629 CFSocketContext context = { 0, (void*)fServiceRef, NULL, NULL, NULL }; 630 631 fSocketRef = CFSocketCreateWithNative(kCFAllocatorDefault, DNSServiceRefSockFD(fServiceRef), kCFSocketReadCallBack, ProcessSockData, &context); 632 if (fSocketRef) { 633 // Prevent CFSocketInvalidate from closing DNSServiceRef's socket. 634 CFOptionFlags sockFlags = CFSocketGetSocketFlags(fSocketRef); 635 CFSocketSetSocketFlags(fSocketRef, sockFlags & (~kCFSocketCloseOnInvalidate)); 636 fRunloopSrc = CFSocketCreateRunLoopSource(kCFAllocatorDefault, fSocketRef, 0); 637 } 638 if (fRunloopSrc) { 639 CFRunLoopAddSource(CFRunLoopGetCurrent(), fRunloopSrc, kCFRunLoopDefaultMode); 640 } else { 641 printf("Could not listen to runloop socket\n"); 642 } 643} 644 645 646- (DNSServiceRef)serviceRef 647{ 648 return fServiceRef; 649} 650 651 652- (void)dealloc 653{ 654 if (fSocketRef) { 655 CFSocketInvalidate(fSocketRef); // Note: Also closes the underlying socket 656 CFRelease(fSocketRef); 657 658 // Workaround that gives time to CFSocket's select thread so it can remove the socket from its 659 // FD set before we close the socket by calling DNSServiceRefDeallocate. <rdar://problem/3585273> 660 usleep(1000); 661 } 662 663 if (fRunloopSrc) { 664 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), fRunloopSrc, kCFRunLoopDefaultMode); 665 CFRelease(fRunloopSrc); 666 } 667 668 DNSServiceRefDeallocate(fServiceRef); 669 670 [super dealloc]; 671} 672 673 674@end // implementation ServiceController 675 676int main(int argc, const char *argv[]) 677{ 678 return NSApplicationMain(argc, argv); 679} 680