mirror of
https://git.rtems.org/rtems-libbsd/
synced 2025-06-10 10:59:16 +08:00

The sources can be obtained via: http://opensource.apple.com/tarballs/mDNSResponder/mDNSResponder-544.tar.gz
680 lines
23 KiB
C
Executable File
680 lines
23 KiB
C
Executable File
/* -*- 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);
|
|
}
|