| /* -*- Mode: C; tab-width: 4 -*- |
| * |
| * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #import <Cocoa/Cocoa.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <net/if.h> |
| #include <arpa/inet.h> |
| #include <netdb.h> |
| #include <sys/select.h> |
| #include <netinet/in.h> |
| #include <unistd.h> |
| #include <dns_sd.h> |
| |
| @class ServiceController; // holds state corresponding to outstanding DNSServiceRef |
| |
| @interface BrowserController : NSObject |
| { |
| IBOutlet id nameField; |
| IBOutlet id typeField; |
| |
| IBOutlet id serviceDisplayTable; |
| IBOutlet id typeColumn; |
| IBOutlet id nameColumn; |
| IBOutlet id serviceTypeField; |
| IBOutlet id serviceNameField; |
| |
| IBOutlet id hostField; |
| IBOutlet id ipAddressField; |
| IBOutlet id ip6AddressField; |
| IBOutlet id portField; |
| IBOutlet id interfaceField; |
| IBOutlet id textField; |
| |
| NSMutableArray *_srvtypeKeys; |
| NSMutableArray *_srvnameKeys; |
| NSMutableArray *_sortedServices; |
| NSMutableDictionary *_servicesDict; |
| |
| ServiceController *_serviceBrowser; |
| ServiceController *_serviceResolver; |
| ServiceController *_ipv4AddressResolver; |
| ServiceController *_ipv6AddressResolver; |
| } |
| |
| - (void)notifyTypeSelectionChange:(NSNotification*)note; |
| - (void)notifyNameSelectionChange:(NSNotification*)note; |
| |
| - (IBAction)connect:(id)sender; |
| |
| - (IBAction)handleTableClick:(id)sender; |
| - (IBAction)removeSelected:(id)sender; |
| - (IBAction)addNewService:(id)sender; |
| |
| - (IBAction)update:(NSString *)Type; |
| |
| - (void)updateBrowseWithName:(const char *)name type:(const char *)resulttype domain:(const char *)domain interface:(uint32_t)interface flags:(DNSServiceFlags)flags; |
| - (void)resolveClientWitHost:(NSString *)host port:(uint16_t)port interfaceIndex:(uint32_t)interface txtRecord:(const char*)txtRecord txtLen:(uint16_t)txtLen; |
| - (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; |
| |
| - (void)_cancelPendingResolve; |
| - (void)_clearResolvedInfo; |
| |
| @end |
| |
| // The ServiceController manages cleanup of DNSServiceRef & runloop info for an outstanding request |
| @interface ServiceController : NSObject |
| { |
| DNSServiceRef fServiceRef; |
| CFSocketRef fSocketRef; |
| CFRunLoopSourceRef fRunloopSrc; |
| } |
| |
| - (id)initWithServiceRef:(DNSServiceRef)ref; |
| - (void)addToCurrentRunLoop; |
| - (DNSServiceRef)serviceRef; |
| - (void)dealloc; |
| |
| @end // interface ServiceController |
| |
| |
| static void |
| ProcessSockData(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) |
| { |
| DNSServiceRef serviceRef = (DNSServiceRef)info; |
| DNSServiceErrorType err = DNSServiceProcessResult(serviceRef); |
| if (err != kDNSServiceErr_NoError) { |
| printf("DNSServiceProcessResult() returned an error! %d\n", err); |
| } |
| } |
| |
| |
| static void |
| ServiceBrowseReply(DNSServiceRef sdRef, DNSServiceFlags servFlags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, |
| const char *serviceName, const char *regtype, const char *replyDomain, void *context) |
| { |
| if (errorCode == kDNSServiceErr_NoError) { |
| [(BrowserController*)context updateBrowseWithName:serviceName type:regtype domain:replyDomain interface:interfaceIndex flags:servFlags]; |
| } else { |
| printf("ServiceBrowseReply got an error! %d\n", errorCode); |
| } |
| } |
| |
| |
| static void |
| ServiceResolveReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, |
| const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context) |
| { |
| if (errorCode == kDNSServiceErr_NoError) { |
| [(BrowserController*)context resolveClientWitHost:[NSString stringWithUTF8String:hosttarget] port:port interfaceIndex:interfaceIndex txtRecord:txtRecord txtLen:txtLen]; |
| } else { |
| printf("ServiceResolveReply got an error! %d\n", errorCode); |
| } |
| } |
| |
| |
| static void |
| QueryRecordReply(DNSServiceRef DNSServiceRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, |
| const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) |
| { |
| if (errorCode == kDNSServiceErr_NoError) { |
| [(BrowserController*)context updateAddress:rrtype addr:rdata addrLen:rdlen host:fullname interfaceIndex:interfaceIndex more:(flags & kDNSServiceFlagsMoreComing)]; |
| } else { |
| printf("QueryRecordReply got an error! %d\n", errorCode); |
| } |
| } |
| |
| |
| static void |
| InterfaceIndexToName(uint32_t interface, char *interfaceName) |
| { |
| assert(interfaceName); |
| |
| if (interface == kDNSServiceInterfaceIndexAny) { |
| // All active network interfaces. |
| strlcpy(interfaceName, "all", IF_NAMESIZE); |
| } else if (interface == kDNSServiceInterfaceIndexLocalOnly) { |
| // Only available locally on this machine. |
| strlcpy(interfaceName, "local", IF_NAMESIZE); |
| } else if (interface == kDNSServiceInterfaceIndexP2P) { |
| // Peer-to-peer. |
| strlcpy(interfaceName, "p2p", IF_NAMESIZE); |
| } else { |
| // Converts interface index to interface name. |
| if_indextoname(interface, interfaceName); |
| } |
| } |
| |
| |
| @implementation BrowserController //Begin implementation of BrowserController methods |
| |
| - (void)registerDefaults |
| { |
| NSMutableDictionary *regDict = [NSMutableDictionary dictionary]; |
| |
| NSArray *typeArray = [NSArray arrayWithObjects:@"_afpovertcp._tcp", |
| @"_smb._tcp", |
| @"_rfb._tcp", |
| @"_ssh._tcp", |
| @"_ftp._tcp", |
| @"_http._tcp", |
| @"_printer._tcp", |
| @"_ipp._tcp", |
| @"_airport._tcp", |
| @"_presence._tcp", |
| @"_daap._tcp", |
| @"_dpap._tcp", |
| nil]; |
| |
| NSArray *nameArray = [NSArray arrayWithObjects:@"AppleShare Servers", |
| @"Windows Sharing", |
| @"Screen Sharing", |
| @"Secure Shell", |
| @"FTP Servers", |
| @"Web Servers", |
| @"LPR Printers", |
| @"IPP Printers", |
| @"AirPort Base Stations", |
| @"iChat Buddies", |
| @"iTunes Libraries", |
| @"iPhoto Libraries", |
| nil]; |
| |
| [regDict setObject:typeArray forKey:@"SrvTypeKeys"]; |
| [regDict setObject:nameArray forKey:@"SrvNameKeys"]; |
| |
| [[NSUserDefaults standardUserDefaults] registerDefaults:regDict]; |
| } |
| |
| |
| - (id)init |
| { |
| self = [super init]; |
| if (self) { |
| _srvtypeKeys = nil; |
| _srvnameKeys = nil; |
| _serviceBrowser = nil; |
| _serviceResolver = nil; |
| _ipv4AddressResolver = nil; |
| _ipv6AddressResolver = nil; |
| _sortedServices = [[NSMutableArray alloc] init]; |
| _servicesDict = [[NSMutableDictionary alloc] init]; |
| } |
| return self; |
| } |
| |
| |
| - (void)awakeFromNib |
| { |
| [typeField sizeLastColumnToFit]; |
| [nameField sizeLastColumnToFit]; |
| [nameField setDoubleAction:@selector(connect:)]; |
| |
| [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notifyTypeSelectionChange:) name:NSTableViewSelectionDidChangeNotification object:typeField]; |
| [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notifyNameSelectionChange:) name:NSTableViewSelectionDidChangeNotification object:nameField]; |
| |
| _srvtypeKeys = [[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTypeKeys"] mutableCopy]; |
| _srvnameKeys = [[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvNameKeys"] mutableCopy]; |
| |
| if (!_srvtypeKeys || !_srvnameKeys) { |
| [_srvtypeKeys release]; |
| [_srvnameKeys release]; |
| [self registerDefaults]; |
| _srvtypeKeys = [[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTypeKeys"] mutableCopy]; |
| _srvnameKeys = [[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvNameKeys"] mutableCopy]; |
| } |
| |
| [typeField reloadData]; |
| } |
| |
| |
| - (void)dealloc |
| { |
| [_srvtypeKeys release]; |
| [_srvnameKeys release]; |
| [_servicesDict release]; |
| [_sortedServices release]; |
| [super dealloc]; |
| } |
| |
| |
| -(void)tableView:(NSTableView *)theTableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row |
| { |
| if (row < 0) return; |
| } |
| |
| |
| - (int)numberOfRowsInTableView:(NSTableView *)theTableView //Begin mandatory TableView methods |
| { |
| if (theTableView == typeField) { |
| return [_srvnameKeys count]; |
| } |
| if (theTableView == nameField) { |
| return [_servicesDict count]; |
| } |
| if (theTableView == serviceDisplayTable) { |
| return [_srvnameKeys count]; |
| } |
| return 0; |
| } |
| |
| |
| - (id)tableView:(NSTableView *)theTableView objectValueForTableColumn:(NSTableColumn *)theColumn row:(int)rowIndex |
| { |
| if (theTableView == typeField) { |
| return [_srvnameKeys objectAtIndex:rowIndex]; |
| } |
| if (theTableView == nameField) { |
| return [[_servicesDict objectForKey:[_sortedServices objectAtIndex:rowIndex]] name]; |
| } |
| if (theTableView == serviceDisplayTable) { |
| if (theColumn == typeColumn) { |
| return [_srvtypeKeys objectAtIndex:rowIndex]; |
| } |
| if (theColumn == nameColumn) { |
| return [_srvnameKeys objectAtIndex:rowIndex]; |
| } |
| return nil; |
| } |
| |
| return nil; |
| } |
| |
| |
| - (void)notifyTypeSelectionChange:(NSNotification*)note |
| { |
| [self _cancelPendingResolve]; |
| |
| int index = [[note object] selectedRow]; |
| if (index != -1) { |
| [self update:[_srvtypeKeys objectAtIndex:index]]; |
| } else { |
| [self update:nil]; |
| } |
| } |
| |
| |
| - (void)notifyNameSelectionChange:(NSNotification*)note |
| { |
| [self _cancelPendingResolve]; |
| |
| int index = [[note object] selectedRow]; |
| if (index == -1) { |
| return; |
| } |
| |
| // Get the currently selected service |
| NSNetService *service = [_servicesDict objectForKey:[_sortedServices objectAtIndex:index]]; |
| |
| DNSServiceRef serviceRef; |
| DNSServiceErrorType err = DNSServiceResolve(&serviceRef, |
| (DNSServiceFlags)0, |
| kDNSServiceInterfaceIndexAny, |
| (const char *)[[service name] UTF8String], |
| (const char *)[[service type] UTF8String], |
| (const char *)[[service domain] UTF8String], |
| (DNSServiceResolveReply)ServiceResolveReply, |
| self); |
| |
| if (kDNSServiceErr_NoError == err) { |
| _serviceResolver = [[ServiceController alloc] initWithServiceRef:serviceRef]; |
| [_serviceResolver addToCurrentRunLoop]; |
| } |
| } |
| |
| |
| - (IBAction)update:(NSString *)theType |
| { |
| [_servicesDict removeAllObjects]; |
| [_sortedServices removeAllObjects]; |
| [nameField reloadData]; |
| |
| // get rid of the previous browser if one exists |
| if (_serviceBrowser != nil) { |
| [_serviceBrowser release]; |
| _serviceBrowser = nil; |
| } |
| |
| if (theType) { |
| DNSServiceRef serviceRef; |
| DNSServiceErrorType err = DNSServiceBrowse(&serviceRef, (DNSServiceFlags)0, 0, [theType UTF8String], NULL, ServiceBrowseReply, self); |
| if (kDNSServiceErr_NoError == err) { |
| _serviceBrowser = [[ServiceController alloc] initWithServiceRef:serviceRef]; |
| [_serviceBrowser addToCurrentRunLoop]; |
| } |
| } |
| } |
| |
| |
| - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication |
| { |
| return YES; |
| } |
| |
| |
| - (void)updateBrowseWithName:(const char *)name type:(const char *)type domain:(const char *)domain interface:(uint32_t)interface flags:(DNSServiceFlags)flags |
| { |
| NSString *key = [NSString stringWithFormat:@"%s.%s%s%d", name, type, domain, interface]; |
| NSNetService *service = [[NSNetService alloc] initWithDomain:[NSString stringWithUTF8String:domain] type:[NSString stringWithUTF8String:type] name:[NSString stringWithUTF8String:name]]; |
| |
| if (flags & kDNSServiceFlagsAdd) { |
| [_servicesDict setObject:service forKey:key]; |
| } else { |
| [_servicesDict removeObjectForKey:key]; |
| } |
| |
| // If not expecting any more data, then reload (redraw) TableView with newly found data |
| if (!(flags & kDNSServiceFlagsMoreComing)) { |
| |
| // Save the current TableView selection |
| int index = [nameField selectedRow]; |
| NSString *selected = (index != -1) ? [[_sortedServices objectAtIndex:index] copy] : nil; |
| |
| [_sortedServices release]; |
| _sortedServices = [[_servicesDict allKeys] mutableCopy]; |
| [_sortedServices sortUsingSelector:@selector(caseInsensitiveCompare:)]; |
| [nameField reloadData]; |
| |
| // Restore the previous TableView selection |
| index = selected ? [_sortedServices indexOfObject:selected] : NSNotFound; |
| if (index != NSNotFound) { |
| [nameField selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO]; |
| [nameField scrollRowToVisible:index]; |
| } |
| |
| [selected release]; |
| } |
| |
| [service release]; |
| |
| return; |
| } |
| |
| |
| - (void)resolveClientWitHost:(NSString *)host port:(uint16_t)port interfaceIndex:(uint32_t)interface txtRecord:(const char*)txtRecord txtLen:(uint16_t)txtLen |
| { |
| DNSServiceRef serviceRef; |
| |
| if (_ipv4AddressResolver) { |
| [_ipv4AddressResolver release]; |
| _ipv4AddressResolver = nil; |
| } |
| |
| if (_ipv6AddressResolver) { |
| [_ipv6AddressResolver release]; |
| _ipv6AddressResolver = nil; |
| } |
| |
| // Start an async lookup for IPv4 addresses |
| DNSServiceErrorType err = DNSServiceQueryRecord(&serviceRef, (DNSServiceFlags)0, interface, [host UTF8String], kDNSServiceType_A, kDNSServiceClass_IN, QueryRecordReply, self); |
| if (err == kDNSServiceErr_NoError) { |
| _ipv4AddressResolver = [[ServiceController alloc] initWithServiceRef:serviceRef]; |
| [_ipv4AddressResolver addToCurrentRunLoop]; |
| } |
| |
| // Start an async lookup for IPv6 addresses |
| err = DNSServiceQueryRecord(&serviceRef, (DNSServiceFlags)0, interface, [host UTF8String], kDNSServiceType_AAAA, kDNSServiceClass_IN, QueryRecordReply, self); |
| if (err == kDNSServiceErr_NoError) { |
| _ipv6AddressResolver = [[ServiceController alloc] initWithServiceRef:serviceRef]; |
| [_ipv6AddressResolver addToCurrentRunLoop]; |
| } |
| |
| char interfaceName[IF_NAMESIZE]; |
| InterfaceIndexToName(interface, interfaceName); |
| |
| [hostField setStringValue:host]; |
| [interfaceField setStringValue:[NSString stringWithUTF8String:interfaceName]]; |
| [portField setIntValue:ntohs(port)]; |
| |
| // kind of a hack: munge txtRecord so it's human-readable |
| if (txtLen > 0) { |
| char *readableText = (char*) malloc(txtLen); |
| if (readableText != nil) { |
| ByteCount index, subStrLen; |
| memcpy(readableText, txtRecord, txtLen); |
| for (index=0; index < txtLen - 1; index += subStrLen + 1) { |
| subStrLen = readableText[index]; |
| readableText[index] = ' '; |
| } |
| [textField setStringValue:[NSString stringWithCString:&readableText[1] length:txtLen - 1]]; |
| free(readableText); |
| } |
| } |
| } |
| |
| |
| - (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 |
| { |
| char addrBuff[256]; |
| |
| if (rrtype == kDNSServiceType_A) { |
| inet_ntop(AF_INET, buff, addrBuff, sizeof(addrBuff)); |
| if ([[ipAddressField stringValue] length] > 0) { |
| [ipAddressField setStringValue:[NSString stringWithFormat:@"%@, ", [ipAddressField stringValue]]]; |
| } |
| [ipAddressField setStringValue:[NSString stringWithFormat:@"%@%s", [ipAddressField stringValue], addrBuff]]; |
| |
| if (!moreToCome) { |
| [_ipv4AddressResolver release]; |
| _ipv4AddressResolver = nil; |
| } |
| } else if (rrtype == kDNSServiceType_AAAA) { |
| inet_ntop(AF_INET6, buff, addrBuff, sizeof(addrBuff)); |
| if ([[ip6AddressField stringValue] length] > 0) { |
| [ip6AddressField setStringValue:[NSString stringWithFormat:@"%@, ", [ip6AddressField stringValue]]]; |
| } |
| [ip6AddressField setStringValue:[NSString stringWithFormat:@"%@%s", [ip6AddressField stringValue], addrBuff]]; |
| |
| if (!moreToCome) { |
| [_ipv6AddressResolver release]; |
| _ipv6AddressResolver = nil; |
| } |
| } |
| } |
| |
| |
| - (void)connect:(id)sender |
| { |
| NSString *host = [hostField stringValue]; |
| NSString *txtRecord = [textField stringValue]; |
| int port = [portField intValue]; |
| |
| int index = [nameField selectedRow]; |
| NSString *selected = (index >= 0) ? [_sortedServices objectAtIndex:index] : nil; |
| NSString *type = [[_servicesDict objectForKey:selected] type]; |
| |
| if ([type isEqual:@"_http._tcp."]) { |
| NSString *pathDelim = @"path="; |
| NSRange where; |
| |
| // If the TXT record specifies a path, extract it. |
| where = [txtRecord rangeOfString:pathDelim options:NSCaseInsensitiveSearch]; |
| if (where.length) { |
| NSRange targetRange = { where.location + where.length, [txtRecord length] - where.location - where.length }; |
| NSRange endDelim = [txtRecord rangeOfString:@"\n" options:kNilOptions range:targetRange]; |
| |
| if (endDelim.length) // if a delimiter was found, truncate the target range |
| targetRange.length = endDelim.location - targetRange.location; |
| |
| NSString *path = [txtRecord substringWithRange:targetRange]; |
| [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%d%@", host, port, path]]]; |
| } else { |
| [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%d", host, port]]]; |
| } |
| } |
| else if ([type isEqual:@"_ftp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ftp://%@:%d/", host, port]]]; |
| else if ([type isEqual:@"_ssh._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ssh://%@:%d/", host, port]]]; |
| else if ([type isEqual:@"_afpovertcp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"afp://%@:%d/", host, port]]]; |
| else if ([type isEqual:@"_smb._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"smb://%@:%d/", host, port]]]; |
| else if ([type isEqual:@"_rfb._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"vnc://%@:%d/", host, port]]]; |
| |
| return; |
| } |
| |
| |
| - (IBAction)handleTableClick:(id)sender |
| { |
| //populate the text fields |
| } |
| |
| |
| - (IBAction)removeSelected:(id)sender |
| { |
| // remove the selected row and force a refresh |
| |
| int selectedRow = [serviceDisplayTable selectedRow]; |
| |
| if (selectedRow) { |
| |
| [_srvtypeKeys removeObjectAtIndex:selectedRow]; |
| [_srvnameKeys removeObjectAtIndex:selectedRow]; |
| |
| [[NSUserDefaults standardUserDefaults] setObject:_srvtypeKeys forKey:@"SrvTypeKeys"]; |
| [[NSUserDefaults standardUserDefaults] setObject:_srvnameKeys forKey:@"SrvNameKeys"]; |
| |
| [typeField reloadData]; |
| [serviceDisplayTable reloadData]; |
| } |
| } |
| |
| |
| - (IBAction)addNewService:(id)sender |
| { |
| // add new entries from the edit fields to the arrays for the defaults |
| NSString *newType = [serviceTypeField stringValue]; |
| NSString *newName = [serviceNameField stringValue]; |
| |
| // 3282283: trim trailing '.' from service type field |
| if ([newType length] && [newType hasSuffix:@"."]) |
| newType = [newType substringToIndex:[newType length] - 1]; |
| |
| if ([newType length] && [newName length]) { |
| [_srvtypeKeys addObject:newType]; |
| [_srvnameKeys addObject:newName]; |
| |
| [[NSUserDefaults standardUserDefaults] setObject:_srvtypeKeys forKey:@"SrvTypeKeys"]; |
| [[NSUserDefaults standardUserDefaults] setObject:_srvnameKeys forKey:@"SrvNameKeys"]; |
| |
| [typeField reloadData]; |
| [serviceDisplayTable reloadData]; |
| } |
| } |
| |
| |
| - (void)_cancelPendingResolve |
| { |
| [_ipv4AddressResolver release]; |
| _ipv4AddressResolver = nil; |
| |
| [_ipv6AddressResolver release]; |
| _ipv6AddressResolver = nil; |
| |
| [_serviceResolver release]; |
| _serviceResolver = nil; |
| |
| [self _clearResolvedInfo]; |
| } |
| |
| |
| - (void)_clearResolvedInfo |
| { |
| [hostField setStringValue:@""]; |
| [ipAddressField setStringValue:@""]; |
| [ip6AddressField setStringValue:@""]; |
| [portField setStringValue:@""]; |
| [interfaceField setStringValue:@""]; |
| [textField setStringValue:@""]; |
| } |
| |
| @end // implementation BrowserController |
| |
| |
| @implementation ServiceController : NSObject |
| { |
| DNSServiceRef fServiceRef; |
| CFSocketRef fSocketRef; |
| CFRunLoopSourceRef fRunloopSrc; |
| } |
| |
| |
| - (id)initWithServiceRef:(DNSServiceRef)ref |
| { |
| self = [super init]; |
| if (self) { |
| fServiceRef = ref; |
| fSocketRef = NULL; |
| fRunloopSrc = NULL; |
| } |
| return self; |
| } |
| |
| |
| - (void)addToCurrentRunLoop |
| { |
| CFSocketContext context = { 0, (void*)fServiceRef, NULL, NULL, NULL }; |
| |
| fSocketRef = CFSocketCreateWithNative(kCFAllocatorDefault, DNSServiceRefSockFD(fServiceRef), kCFSocketReadCallBack, ProcessSockData, &context); |
| if (fSocketRef) { |
| // Prevent CFSocketInvalidate from closing DNSServiceRef's socket. |
| CFOptionFlags sockFlags = CFSocketGetSocketFlags(fSocketRef); |
| CFSocketSetSocketFlags(fSocketRef, sockFlags & (~kCFSocketCloseOnInvalidate)); |
| fRunloopSrc = CFSocketCreateRunLoopSource(kCFAllocatorDefault, fSocketRef, 0); |
| } |
| if (fRunloopSrc) { |
| CFRunLoopAddSource(CFRunLoopGetCurrent(), fRunloopSrc, kCFRunLoopDefaultMode); |
| } else { |
| printf("Could not listen to runloop socket\n"); |
| } |
| } |
| |
| |
| - (DNSServiceRef)serviceRef |
| { |
| return fServiceRef; |
| } |
| |
| |
| - (void)dealloc |
| { |
| if (fSocketRef) { |
| CFSocketInvalidate(fSocketRef); // Note: Also closes the underlying socket |
| CFRelease(fSocketRef); |
| |
| // Workaround that gives time to CFSocket's select thread so it can remove the socket from its |
| // FD set before we close the socket by calling DNSServiceRefDeallocate. <rdar://problem/3585273> |
| usleep(1000); |
| } |
| |
| if (fRunloopSrc) { |
| CFRunLoopRemoveSource(CFRunLoopGetCurrent(), fRunloopSrc, kCFRunLoopDefaultMode); |
| CFRelease(fRunloopSrc); |
| } |
| |
| DNSServiceRefDeallocate(fServiceRef); |
| |
| [super dealloc]; |
| } |
| |
| |
| @end // implementation ServiceController |
| |
| int main(int argc, const char *argv[]) |
| { |
| return NSApplicationMain(argc, argv); |
| } |