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