]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/DNSServiceBrowser.m
mDNSResponder-58.8.1.tar.gz
[apple/mdnsresponder.git] / Clients / DNSServiceBrowser.m
1 /*
2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22
23 Change History (most recent first):
24
25 $Log: DNSServiceBrowser.m,v $
26 Revision 1.18 2003/08/12 19:55:07 cheshire
27 Update to APSL 2.0
28
29 */
30
31 #import "BrowserController.h"
32
33 #include "arpa/inet.h"
34
35 void
36 MyHandleMachMessage ( CFMachPortRef port, void * msg, CFIndex size, void * info )
37 {
38 DNSServiceDiscovery_handleReply(msg);
39 }
40
41 void browse_reply (
42 DNSServiceBrowserReplyResultType resultType, // One of DNSServiceBrowserReplyResultType
43 const char *replyName,
44 const char *replyType,
45 const char *replyDomain,
46 DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information
47 void *context
48 )
49 {
50 [[NSApp delegate] updateBrowseWithResult:resultType name:[NSString stringWithUTF8String:replyName] type:[NSString stringWithUTF8String:replyType] domain:[NSString stringWithUTF8String:replyDomain] flags:flags];
51 return;
52 }
53
54 void enum_reply (
55 DNSServiceDomainEnumerationReplyResultType resultType,
56 const char *replyDomain,
57 DNSServiceDiscoveryReplyFlags flags,
58 void *context
59 )
60 {
61 [[NSApp delegate] updateEnumWithResult:resultType domain:[NSString stringWithUTF8String:replyDomain] flags:flags];
62
63 return;
64 }
65
66 void resolve_reply (
67 struct sockaddr *interface,
68 struct sockaddr *address,
69 const char *txtRecord,
70 DNSServiceDiscoveryReplyFlags flags,
71 void *context
72 )
73 {
74 [[NSApp delegate] resolveClientWithInterface:interface address:address txtRecord:[NSString stringWithUTF8String:txtRecord]];
75
76 return;
77 }
78
79 @implementation BrowserController //Begin implementation of BrowserController methods
80
81 - (void)registerDefaults
82 {
83 NSMutableDictionary *regDict = [NSMutableDictionary dictionary];
84
85 NSArray *typeArray = [NSArray arrayWithObjects:@"_ftp._tcp.", @"_tftp._tcp.",
86 @"_ssh._tcp.", @"_telnet._tcp.",
87 @"_http._tcp.",
88 @"_printer._tcp.", @"_ipp._tcp.",
89 @"_ichat._tcp.", @"_eppc._tcp.",
90 @"_afpovertcp._tcp.", @"_afpovertcp._tcp.", @"_MacOSXDupSuppress._tcp.", nil];
91 NSArray *nameArray = [NSArray arrayWithObjects:@"File Transfer (ftp)", @"Trivial File Transfer (tftp)",
92 @"Secure Shell (ssh)", @"Telnet",
93 @"Web Server (http)",
94 @"LPR Printer", @"IPP Printer",
95 @"iChat", @"Remote AppleEvents",
96 @"AppleShare Server", @"SMB File Server", @"Mystery Service", nil];
97
98 [regDict setObject:typeArray forKey:@"SrvTypeKeys"];
99 [regDict setObject:nameArray forKey:@"SrvNameKeys"];
100
101 [[NSUserDefaults standardUserDefaults] registerDefaults:regDict];
102 }
103
104
105 - (id)init
106 {
107 [self registerDefaults];
108
109 browse_client = nil;
110
111 return [super init];
112 }
113
114 - (void)awakeFromNib //BrowserController startup procedure
115 {
116 SrvType=NULL;
117 Domain=NULL;
118 srvtypeKeys = [NSMutableArray array]; //Define arrays for Type, Domain, and Name
119 srvnameKeys = [NSMutableArray array];
120
121 domainKeys = [NSMutableArray array];
122 [domainKeys retain];
123
124 nameKeys = [NSMutableArray array];
125 [nameKeys retain];
126
127 [srvtypeKeys retain]; //Keep arrays in memory until BrowserController closes
128 [srvnameKeys retain]; //Keep arrays in memory until BrowserController closes
129 [typeField setDataSource:self]; //Set application fields' data source to BrowserController
130 [typeField sizeLastColumnToFit]; //and set column sizes to use their whole table's width.
131 [nameField setDataSource:self];
132 [nameField sizeLastColumnToFit];
133 [domainField setDataSource:self];
134 [domainField sizeLastColumnToFit];
135
136 [nameField setDoubleAction:@selector(connect:)];
137
138 //[srvtypeKeys addObject:@"_ftp._tcp."]; //Add supported protocols and domains to their
139 //[srvnameKeys addObject:@"File Transfer (ftp)"];
140 //[srvtypeKeys addObject:@"_printer._tcp."]; //respective arrays
141 //[srvnameKeys addObject:@"Printer (lpr)"];
142 //[srvtypeKeys addObject:@"_http._tcp."]; //respective arrays
143 //[srvnameKeys addObject:@"Web Server (http)"];
144 //[srvtypeKeys addObject:@"_afp._tcp."]; //respective arrays
145 //[srvnameKeys addObject:@"AppleShare Server (afp)"];
146
147 [ipAddressField setStringValue:@""];
148 [portField setStringValue:@""];
149 [textField setStringValue:@""];
150
151 [srvtypeKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTypeKeys"]];
152 [srvnameKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvNameKeys"]];
153
154
155 [typeField reloadData]; //Reload (redraw) data in fields
156 [domainField reloadData];
157
158 [self loadDomains:self];
159
160 }
161
162 - (void)dealloc //Deallocation method
163 {
164 [srvtypeKeys release];
165 [srvnameKeys release];
166 [nameKeys release];
167 [domainKeys release];
168 }
169
170 -(void)tableView:(NSTableView *)theTableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row
171 {
172 if (row<0) return;
173 }
174
175 - (int)numberOfRowsInTableView:(NSTableView *)theTableView //Begin mandatory TableView methods
176 {
177 if (theTableView == typeField)
178 {
179 return [srvnameKeys count];
180 }
181 if (theTableView == domainField)
182 {
183 return [domainKeys count];
184 }
185 if (theTableView == nameField)
186 {
187 return [nameKeys count];
188 }
189 if (theTableView == serviceDisplayTable)
190 {
191 return [srvnameKeys count];
192 }
193 return 0;
194 }
195
196 - (id)tableView:(NSTableView *)theTableView objectValueForTableColumn:(NSTableColumn *)theColumn row:(int)rowIndex
197 {
198 if (theTableView == typeField)
199 {
200 return [srvnameKeys objectAtIndex:rowIndex];
201 }
202 if (theTableView == domainField)
203 {
204 return [domainKeys objectAtIndex:rowIndex];
205 }
206 if (theTableView == nameField)
207 {
208 return [[nameKeys sortedArrayUsingSelector:@selector(compare:)] objectAtIndex:rowIndex];
209 }
210 if (theTableView == serviceDisplayTable)
211 {
212 if (theColumn == typeColumn) {
213 return [srvtypeKeys objectAtIndex:rowIndex];
214 }
215 if (theColumn == nameColumn) {
216 return [srvnameKeys objectAtIndex:rowIndex];
217 }
218 return 0;
219 }
220 else
221 return(0);
222 } //End of mandatory TableView methods
223
224 - (IBAction)handleTypeClick:(id)sender //Handle clicks for Type
225 {
226 int index=[sender selectedRow]; //Find index of selected row
227 if (index==-1) return; //Error checking
228 SrvType = [srvtypeKeys objectAtIndex:index]; //Save desired Type
229 SrvName = [srvnameKeys objectAtIndex:index]; //Save desired Type
230
231 [ipAddressField setStringValue:@""];
232 [portField setStringValue:@""];
233 [textField setStringValue:@""];
234
235 [self update:SrvType Domain:Domain]; //If Type and Domain are set, update records
236 }
237
238 - (IBAction)handleDomainClick:(id)sender //Handle clicks for Domain
239 {
240 int index=[sender selectedRow]; //Find index of selected row
241 if (index==-1) return; //Error checking
242 Domain = [domainKeys objectAtIndex:index]; //Save desired Domain
243
244 [ipAddressField setStringValue:@""];
245 [portField setStringValue:@""];
246 [textField setStringValue:@""];
247
248 if (SrvType!=NULL) [self update:SrvType Domain:Domain]; //If Type and Domain are set, update records
249 }
250
251 - (IBAction)handleNameClick:(id)sender //Handle clicks for Name
252 {
253 int index=[sender selectedRow]; //Find index of selected row
254 if (index==-1) return; //Error checking
255 Name=[[nameKeys sortedArrayUsingSelector:@selector(compare:)] objectAtIndex:index]; //Save desired name
256
257 {
258 CFMachPortRef cfMachPort;
259 CFMachPortContext context;
260 Boolean shouldFreeInfo;
261 dns_service_discovery_ref dns_client;
262 mach_port_t port;
263 CFRunLoopSourceRef rls;
264
265 context.version = 1;
266 context.info = 0;
267 context.retain = NULL;
268 context.release = NULL;
269 context.copyDescription = NULL;
270
271 [ipAddressField setStringValue:@"?"];
272 [portField setStringValue:@"?"];
273 [textField setStringValue:@"?"];
274 // start an enumerator on the local server
275 dns_client = DNSServiceResolverResolve
276 (
277 (char *)[Name UTF8String],
278 (char *)[SrvType UTF8String],
279 (char *)(Domain?[Domain UTF8String]:""),
280 resolve_reply,
281 nil
282 );
283
284 port = DNSServiceDiscoveryMachPort(dns_client);
285
286 if (port) {
287 cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo );
288
289 /* Create and add a run loop source for the port */
290 rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
291 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
292 CFRelease(rls);
293 } else {
294 printf("Could not obtain client port\n");
295 return;
296 }
297 }
298 }
299
300 - (IBAction)loadDomains:(id)sender
301 {
302 CFMachPortRef cfMachPort;
303 CFMachPortContext context;
304 Boolean shouldFreeInfo;
305 dns_service_discovery_ref dns_client;
306 mach_port_t port;
307 CFRunLoopSourceRef rls;
308
309 context.version = 1;
310 context.info = 0;
311 context.retain = NULL;
312 context.release = NULL;
313 context.copyDescription = NULL;
314
315 // start an enumerator on the local server
316 dns_client = DNSServiceDomainEnumerationCreate
317 (
318 0,
319 enum_reply,
320 nil
321 );
322
323 port = DNSServiceDiscoveryMachPort(dns_client);
324
325 if (port) {
326 cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo );
327
328 /* Create and add a run loop source for the port */
329 rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
330 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
331 CFRelease(rls);
332 } else {
333 printf("Could not obtain client port\n");
334 return;
335 }
336 }
337
338 - (IBAction)update:theType Domain:theDomain; //The Big Kahuna: Fetch PTR records and update application
339 {
340 const char * DomainC;
341 const char * TypeC=[theType UTF8String]; //Type in C string format
342
343 if (theDomain) {
344 DomainC = [theDomain UTF8String]; //Domain in C string format
345 } else {
346 DomainC = "";
347 }
348
349 [nameKeys removeAllObjects]; //Get rid of displayed records if we're going to go get new ones
350 [nameField reloadData]; //Reload (redraw) names to show the old data is gone
351
352 // get rid of the previous browser if one exists
353 if (browse_client) {
354 DNSServiceDiscoveryDeallocate(browse_client);
355 browse_client = nil;
356 }
357
358 // now create a browser to return the values for the nameField ...
359 {
360 CFMachPortRef cfMachPort;
361 CFMachPortContext context;
362 Boolean shouldFreeInfo;
363 mach_port_t port;
364 CFRunLoopSourceRef rls;
365
366 context.version = 1;
367 context.info = 0;
368 context.retain = NULL;
369 context.release = NULL;
370 context.copyDescription = NULL;
371
372 // start an enumerator on the local server
373 browse_client = DNSServiceBrowserCreate
374 (
375 (char *)TypeC,
376 (char *)DomainC,
377 browse_reply,
378 nil
379 );
380
381 port = DNSServiceDiscoveryMachPort(browse_client);
382
383 if (port) {
384 cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo );
385
386 /* Create and add a run loop source for the port */
387 rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
388 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
389 CFRelease(rls);
390 } else {
391 printf("Could not obtain client port\n");
392 return;
393 }
394 }
395
396 }
397
398
399 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication //Quit when main window is closed
400 {
401 return YES;
402 }
403
404 - (BOOL)windowShouldClose:(NSWindow *)sender //Save domains to our domain file when quitting
405 {
406 [domainField reloadData];
407 return YES;
408 }
409
410 - (void)updateEnumWithResult:(int)resultType domain:(NSString *)domain flags:(int)flags
411 {
412 // new domain received
413 if (DNSServiceDomainEnumerationReplyAddDomain == resultType || DNSServiceDomainEnumerationReplyAddDomainDefault == resultType) {
414 // add the domain to the list
415 [domainKeys addObject:domain];
416 } else {
417 // remove the domain from the list
418 NSEnumerator *dmnEnum = [domainKeys objectEnumerator];
419 NSString *aDomain = nil;
420
421 while (aDomain = [dmnEnum nextObject]) {
422 if ([aDomain isEqualToString:domain]) {
423 [domainKeys removeObject:domain];
424 break;
425 }
426 }
427 }
428 // update the domain table
429 [domainField reloadData];
430 return;
431 }
432
433
434
435 - (void)updateBrowseWithResult:(int)type name:(NSString *)name type:(NSString *)resulttype domain:(NSString *)domain flags:(int)flags
436 {
437
438 //NSLog(@"Received result %@ %@ %@ %d", name, resulttype, domain, type);
439
440 if (([domain isEqualToString:Domain] || [domain isEqualToString:@"local."]) && [resulttype isEqualToString:SrvType]) {
441
442 if (type == DNSServiceBrowserReplyRemoveInstance) {
443 if ([nameKeys containsObject:name]) {
444 [nameKeys removeObject:name];
445 }
446 }
447 if (type == DNSServiceBrowserReplyAddInstance) {
448 if (![nameKeys containsObject:name]) {
449 [nameKeys addObject:name];
450 }
451 }
452
453 // If not expecting any more data, then reload (redraw) Name TableView with newly found data
454 if ((flags & kDNSServiceDiscoveryMoreRepliesImmediately) == 0)
455 [nameField reloadData];
456 }
457 return;
458 }
459
460 - (void)resolveClientWithInterface:(struct sockaddr *)interface address:(struct sockaddr *)address txtRecord:(NSString *)txtRecord
461 {
462 if (address->sa_family != AF_INET) return; // For now we only handle IPv4
463 //printf("interface length = %d, port = %d, family = %d, address = %s\n", ((struct sockaddr_in *)interface)->sin_len, ((struct sockaddr_in *)interface)->sin_port, ((struct sockaddr_in *)interface)->sin_family, inet_ntoa(((struct in_addr)((struct sockaddr_in *)interface)->sin_addr)));
464 //printf("address length = %d, port = %d, family = %d, address = %s\n", ((struct sockaddr_in *)address)->sin_len, ((struct sockaddr_in *)address)->sin_port, ((struct sockaddr_in *)address)->sin_family, inet_ntoa(((struct in_addr)((struct sockaddr_in *)address)->sin_addr)));
465 NSString *ipAddr = [NSString stringWithCString:inet_ntoa(((struct in_addr)((struct sockaddr_in *)address)->sin_addr))];
466 int port = ((struct sockaddr_in *)address)->sin_port;
467
468 [ipAddressField setStringValue:ipAddr];
469 [portField setIntValue:port];
470 [textField setStringValue:txtRecord];
471
472 return;
473 }
474
475 - (void)connect:(id)sender
476 {
477 NSString *ipAddr = [ipAddressField stringValue];
478 int port = [portField intValue];
479 NSString *txtRecord = [textField stringValue];
480
481 if (!txtRecord) txtRecord = @"";
482
483 if (!ipAddr || !port) return;
484
485 if ([SrvType isEqualToString:@"_ftp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ftp://%@:%d/", ipAddr, port]]];
486 else if ([SrvType isEqualToString:@"_tftp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"tftp://%@:%d/", ipAddr, port]]];
487 else if ([SrvType isEqualToString:@"_ssh._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ssh://%@:%d/", ipAddr, port]]];
488 else if ([SrvType isEqualToString:@"_telnet._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"telnet://%@:%d/", ipAddr, port]]];
489 else if ([SrvType isEqualToString:@"_http._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%d", ipAddr, port]]];
490 else if ([SrvType isEqualToString:@"_printer._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"lpr://%@:%d/", ipAddr, port]]];
491 else if ([SrvType isEqualToString:@"_ipp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ipp://%@:%d/", ipAddr, port]]];
492 else if ([SrvType isEqualToString:@"_afpovertcp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"afp://%@:%d/", ipAddr, port]]];
493 else if ([SrvType isEqualToString:@"_smb._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"smb://%@:%d/", ipAddr, port]]];
494
495 return;
496 }
497
498 - (IBAction)handleTableClick:(id)sender
499 {
500 //populate the text fields
501 }
502
503 - (IBAction)removeSelected:(id)sender
504 {
505 // remove the selected row and force a refresh
506
507 int selectedRow = [serviceDisplayTable selectedRow];
508
509 if (selectedRow) {
510
511 [srvtypeKeys removeObjectAtIndex:selectedRow];
512 [srvnameKeys removeObjectAtIndex:selectedRow];
513
514 [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"];
515 [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"];
516
517 [typeField reloadData];
518 [serviceDisplayTable reloadData];
519 }
520 }
521
522 - (IBAction)addNewService:(id)sender
523 {
524 // add new entries from the edit fields to the arrays for the defaults
525
526 if ([[serviceTypeField stringValue] length] && [[serviceNameField stringValue] length]) {
527 [srvtypeKeys addObject:[serviceTypeField stringValue]];
528 [srvnameKeys addObject:[serviceNameField stringValue]];
529
530 [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"];
531 [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"];
532
533 [typeField reloadData];
534 [serviceDisplayTable reloadData];
535 }
536
537 }
538
539
540
541 @end