DNSServiceBrowser.m [plain text]
/*
* Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
Change History (most recent first):
$Log: DNSServiceBrowser.m,v $
Revision 1.18 2003/08/12 19:55:07 cheshire
Update to APSL 2.0
*/
#import "BrowserController.h"
#include "arpa/inet.h"
void
MyHandleMachMessage ( CFMachPortRef port, void * msg, CFIndex size, void * info )
{
DNSServiceDiscovery_handleReply(msg);
}
void browse_reply (
DNSServiceBrowserReplyResultType resultType, // One of DNSServiceBrowserReplyResultType
const char *replyName,
const char *replyType,
const char *replyDomain,
DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information
void *context
)
{
[[NSApp delegate] updateBrowseWithResult:resultType name:[NSString stringWithUTF8String:replyName] type:[NSString stringWithUTF8String:replyType] domain:[NSString stringWithUTF8String:replyDomain] flags:flags];
return;
}
void enum_reply (
DNSServiceDomainEnumerationReplyResultType resultType,
const char *replyDomain,
DNSServiceDiscoveryReplyFlags flags,
void *context
)
{
[[NSApp delegate] updateEnumWithResult:resultType domain:[NSString stringWithUTF8String:replyDomain] flags:flags];
return;
}
void resolve_reply (
struct sockaddr *interface,
struct sockaddr *address,
const char *txtRecord,
DNSServiceDiscoveryReplyFlags flags,
void *context
)
{
[[NSApp delegate] resolveClientWithInterface:interface address:address txtRecord:[NSString stringWithUTF8String:txtRecord]];
return;
}
@implementation BrowserController //Begin implementation of BrowserController methods
- (void)registerDefaults
{
NSMutableDictionary *regDict = [NSMutableDictionary dictionary];
NSArray *typeArray = [NSArray arrayWithObjects:@"_ftp._tcp.", @"_tftp._tcp.",
@"_ssh._tcp.", @"_telnet._tcp.",
@"_http._tcp.",
@"_printer._tcp.", @"_ipp._tcp.",
@"_ichat._tcp.", @"_eppc._tcp.",
@"_afpovertcp._tcp.", @"_afpovertcp._tcp.", @"_MacOSXDupSuppress._tcp.", nil];
NSArray *nameArray = [NSArray arrayWithObjects:@"File Transfer (ftp)", @"Trivial File Transfer (tftp)",
@"Secure Shell (ssh)", @"Telnet",
@"Web Server (http)",
@"LPR Printer", @"IPP Printer",
@"iChat", @"Remote AppleEvents",
@"AppleShare Server", @"SMB File Server", @"Mystery Service", nil];
[regDict setObject:typeArray forKey:@"SrvTypeKeys"];
[regDict setObject:nameArray forKey:@"SrvNameKeys"];
[[NSUserDefaults standardUserDefaults] registerDefaults:regDict];
}
- (id)init
{
[self registerDefaults];
browse_client = nil;
return [super init];
}
- (void)awakeFromNib //BrowserController startup procedure
{
SrvType=NULL;
Domain=NULL;
srvtypeKeys = [NSMutableArray array]; //Define arrays for Type, Domain, and Name
srvnameKeys = [NSMutableArray array];
domainKeys = [NSMutableArray array];
[domainKeys retain];
nameKeys = [NSMutableArray array];
[nameKeys retain];
[srvtypeKeys retain]; //Keep arrays in memory until BrowserController closes
[srvnameKeys retain]; //Keep arrays in memory until BrowserController closes
[typeField setDataSource:self]; //Set application fields' data source to BrowserController
[typeField sizeLastColumnToFit]; //and set column sizes to use their whole table's width.
[nameField setDataSource:self];
[nameField sizeLastColumnToFit];
[domainField setDataSource:self];
[domainField sizeLastColumnToFit];
[nameField setDoubleAction:@selector(connect:)];
//[srvtypeKeys addObject:@"_ftp._tcp."]; //Add supported protocols and domains to their
//[srvnameKeys addObject:@"File Transfer (ftp)"];
//[srvtypeKeys addObject:@"_printer._tcp."]; //respective arrays
//[srvnameKeys addObject:@"Printer (lpr)"];
//[srvtypeKeys addObject:@"_http._tcp."]; //respective arrays
//[srvnameKeys addObject:@"Web Server (http)"];
//[srvtypeKeys addObject:@"_afp._tcp."]; //respective arrays
//[srvnameKeys addObject:@"AppleShare Server (afp)"];
[ipAddressField setStringValue:@""];
[portField setStringValue:@""];
[textField setStringValue:@""];
[srvtypeKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTypeKeys"]];
[srvnameKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvNameKeys"]];
[typeField reloadData]; //Reload (redraw) data in fields
[domainField reloadData];
[self loadDomains:self];
}
- (void)dealloc //Deallocation method
{
[srvtypeKeys release];
[srvnameKeys release];
[nameKeys release];
[domainKeys release];
}
-(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 == domainField)
{
return [domainKeys count];
}
if (theTableView == nameField)
{
return [nameKeys 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 == domainField)
{
return [domainKeys objectAtIndex:rowIndex];
}
if (theTableView == nameField)
{
return [[nameKeys sortedArrayUsingSelector:@selector(compare:)] objectAtIndex:rowIndex];
}
if (theTableView == serviceDisplayTable)
{
if (theColumn == typeColumn) {
return [srvtypeKeys objectAtIndex:rowIndex];
}
if (theColumn == nameColumn) {
return [srvnameKeys objectAtIndex:rowIndex];
}
return 0;
}
else
return(0);
} //End of mandatory TableView methods
- (IBAction)handleTypeClick:(id)sender //Handle clicks for Type
{
int index=[sender selectedRow]; //Find index of selected row
if (index==-1) return; //Error checking
SrvType = [srvtypeKeys objectAtIndex:index]; //Save desired Type
SrvName = [srvnameKeys objectAtIndex:index]; //Save desired Type
[ipAddressField setStringValue:@""];
[portField setStringValue:@""];
[textField setStringValue:@""];
[self update:SrvType Domain:Domain]; //If Type and Domain are set, update records
}
- (IBAction)handleDomainClick:(id)sender //Handle clicks for Domain
{
int index=[sender selectedRow]; //Find index of selected row
if (index==-1) return; //Error checking
Domain = [domainKeys objectAtIndex:index]; //Save desired Domain
[ipAddressField setStringValue:@""];
[portField setStringValue:@""];
[textField setStringValue:@""];
if (SrvType!=NULL) [self update:SrvType Domain:Domain]; //If Type and Domain are set, update records
}
- (IBAction)handleNameClick:(id)sender //Handle clicks for Name
{
int index=[sender selectedRow]; //Find index of selected row
if (index==-1) return; //Error checking
Name=[[nameKeys sortedArrayUsingSelector:@selector(compare:)] objectAtIndex:index]; //Save desired name
{
CFMachPortRef cfMachPort;
CFMachPortContext context;
Boolean shouldFreeInfo;
dns_service_discovery_ref dns_client;
mach_port_t port;
CFRunLoopSourceRef rls;
context.version = 1;
context.info = 0;
context.retain = NULL;
context.release = NULL;
context.copyDescription = NULL;
[ipAddressField setStringValue:@"?"];
[portField setStringValue:@"?"];
[textField setStringValue:@"?"];
// start an enumerator on the local server
dns_client = DNSServiceResolverResolve
(
(char *)[Name UTF8String],
(char *)[SrvType UTF8String],
(char *)(Domain?[Domain UTF8String]:""),
resolve_reply,
nil
);
port = DNSServiceDiscoveryMachPort(dns_client);
if (port) {
cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo );
/* Create and add a run loop source for the port */
rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
} else {
printf("Could not obtain client port\n");
return;
}
}
}
- (IBAction)loadDomains:(id)sender
{
CFMachPortRef cfMachPort;
CFMachPortContext context;
Boolean shouldFreeInfo;
dns_service_discovery_ref dns_client;
mach_port_t port;
CFRunLoopSourceRef rls;
context.version = 1;
context.info = 0;
context.retain = NULL;
context.release = NULL;
context.copyDescription = NULL;
// start an enumerator on the local server
dns_client = DNSServiceDomainEnumerationCreate
(
0,
enum_reply,
nil
);
port = DNSServiceDiscoveryMachPort(dns_client);
if (port) {
cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo );
/* Create and add a run loop source for the port */
rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
} else {
printf("Could not obtain client port\n");
return;
}
}
- (IBAction)update:theType Domain:theDomain; //The Big Kahuna: Fetch PTR records and update application
{
const char * DomainC;
const char * TypeC=[theType UTF8String]; //Type in C string format
if (theDomain) {
DomainC = [theDomain UTF8String]; //Domain in C string format
} else {
DomainC = "";
}
[nameKeys removeAllObjects]; //Get rid of displayed records if we're going to go get new ones
[nameField reloadData]; //Reload (redraw) names to show the old data is gone
// get rid of the previous browser if one exists
if (browse_client) {
DNSServiceDiscoveryDeallocate(browse_client);
browse_client = nil;
}
// now create a browser to return the values for the nameField ...
{
CFMachPortRef cfMachPort;
CFMachPortContext context;
Boolean shouldFreeInfo;
mach_port_t port;
CFRunLoopSourceRef rls;
context.version = 1;
context.info = 0;
context.retain = NULL;
context.release = NULL;
context.copyDescription = NULL;
// start an enumerator on the local server
browse_client = DNSServiceBrowserCreate
(
(char *)TypeC,
(char *)DomainC,
browse_reply,
nil
);
port = DNSServiceDiscoveryMachPort(browse_client);
if (port) {
cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo );
/* Create and add a run loop source for the port */
rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
} else {
printf("Could not obtain client port\n");
return;
}
}
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication //Quit when main window is closed
{
return YES;
}
- (BOOL)windowShouldClose:(NSWindow *)sender //Save domains to our domain file when quitting
{
[domainField reloadData];
return YES;
}
- (void)updateEnumWithResult:(int)resultType domain:(NSString *)domain flags:(int)flags
{
// new domain received
if (DNSServiceDomainEnumerationReplyAddDomain == resultType || DNSServiceDomainEnumerationReplyAddDomainDefault == resultType) {
// add the domain to the list
[domainKeys addObject:domain];
} else {
// remove the domain from the list
NSEnumerator *dmnEnum = [domainKeys objectEnumerator];
NSString *aDomain = nil;
while (aDomain = [dmnEnum nextObject]) {
if ([aDomain isEqualToString:domain]) {
[domainKeys removeObject:domain];
break;
}
}
}
// update the domain table
[domainField reloadData];
return;
}
- (void)updateBrowseWithResult:(int)type name:(NSString *)name type:(NSString *)resulttype domain:(NSString *)domain flags:(int)flags
{
//NSLog(@"Received result
if (([domain isEqualToString:Domain] || [domain isEqualToString:@"local."]) && [resulttype isEqualToString:SrvType]) {
if (type == DNSServiceBrowserReplyRemoveInstance) {
if ([nameKeys containsObject:name]) {
[nameKeys removeObject:name];
}
}
if (type == DNSServiceBrowserReplyAddInstance) {
if (![nameKeys containsObject:name]) {
[nameKeys addObject:name];
}
}
// If not expecting any more data, then reload (redraw) Name TableView with newly found data
if ((flags & kDNSServiceDiscoveryMoreRepliesImmediately) == 0)
[nameField reloadData];
}
return;
}
- (void)resolveClientWithInterface:(struct sockaddr *)interface address:(struct sockaddr *)address txtRecord:(NSString *)txtRecord
{
if (address->sa_family != AF_INET) return; // For now we only handle IPv4
//printf("interface length = //printf("address length = NSString *ipAddr = [NSString stringWithCString:inet_ntoa(((struct in_addr)((struct sockaddr_in *)address)->sin_addr))];
int port = ((struct sockaddr_in *)address)->sin_port;
[ipAddressField setStringValue:ipAddr];
[portField setIntValue:port];
[textField setStringValue:txtRecord];
return;
}
- (void)connect:(id)sender
{
NSString *ipAddr = [ipAddressField stringValue];
int port = [portField intValue];
NSString *txtRecord = [textField stringValue];
if (!txtRecord) txtRecord = @"";
if (!ipAddr || !port) return;
if ([SrvType isEqualToString:@"_ftp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ftp://%@: else if ([SrvType isEqualToString:@"_tftp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"tftp://%@: else if ([SrvType isEqualToString:@"_ssh._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ssh:// else if ([SrvType isEqualToString:@"_telnet._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"telnet:// else if ([SrvType isEqualToString:@"_http._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@: else if ([SrvType isEqualToString:@"_printer._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"lpr:// else if ([SrvType isEqualToString:@"_ipp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ipp:// else if ([SrvType isEqualToString:@"_afpovertcp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"afp:// else if ([SrvType isEqualToString:@"_smb._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"smb://
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
if ([[serviceTypeField stringValue] length] && [[serviceNameField stringValue] length]) {
[srvtypeKeys addObject:[serviceTypeField stringValue]];
[srvnameKeys addObject:[serviceNameField stringValue]];
[[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"];
[[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"];
[typeField reloadData];
[serviceDisplayTable reloadData];
}
}
@end