1/* 2 * Copyright (c) 1999-2008 Apple 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#include <stdlib.h> 24#include <unistd.h> 25#include <sys/types.h> 26#include <sys/socket.h> 27#include <netinet/in.h> 28#include <arpa/inet.h> 29#include <sys/queue.h> 30 31#include <CoreFoundation/CoreFoundation.h> 32#include <dns_sd.h> 33 34#include <nfs/rpcv2.h> 35#include "showmount.h" 36 37struct cbinfo { 38 DNSServiceRef sdref; 39 CFSocketRef sockref; 40 CFRunLoopSourceRef rls; 41}; 42 43struct cbinfo nfsinfo, mountdinfo; 44 45/* 46 * CFRunloop callback that calls DNSServiceProcessResult() when 47 * there's new data on the socket. 48 */ 49static void 50socket_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *context) 51{ 52 struct cbinfo *info = context; 53 DNSServiceErrorType err; 54 55 if (callbackType == kCFSocketNoCallBack) { 56 printf("socket_callback: kCFSocketNoCallBack?\n"); 57 return; 58 } 59 60 if ((err = DNSServiceProcessResult(info->sdref)) != kDNSServiceErr_NoError) { 61 printf("DNSServiceProcessResult() returned an error! %d\n", err); 62 if (err == kDNSServiceErr_BadReference) { 63 printf("bad reference?: %p, %d, %p, %p %p\n", s, (int)callbackType, address, data, context); 64 return; 65 } 66 if ((context == &nfsinfo) || (context == &mountdinfo)) { 67 /* bail if there's a problem with the main browse connection */ 68 exit(1); 69 } 70 /* dump the troublesome service connection */ 71 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), info->rls, kCFRunLoopDefaultMode); 72 CFRelease(info->rls); 73 CFSocketInvalidate(info->sockref); 74 CFRelease(info->sockref); 75 DNSServiceRefDeallocate(info->sdref); 76 free(info); 77 } 78} 79 80#ifdef DEBUG 81#define Dused 82#else 83#define Dused __unused 84#endif 85 86static void 87resolve_callback( 88 __unused DNSServiceRef sdRef, 89 __unused DNSServiceFlags flags, 90 __unused uint32_t interfaceIndex, 91 __unused DNSServiceErrorType errorCode, 92 Dused const char *fullName, 93 const char *hostTarget, 94 Dused uint16_t port, 95 Dused uint16_t txtLen, 96 Dused const unsigned char *txtRecord, 97 void *context) 98{ 99 struct cbinfo *info = context; 100#ifdef DEBUG 101 const char *p; 102 char *q, *s; 103 int len; 104 105 printf("resolve: %s %s:%d TXT %d\n", fullName, hostTarget, port, txtLen); 106 if ((txtLen > 1) && ((s = malloc(txtLen+2)))) { 107 p = txtRecord; 108 q = s; 109 len = txtLen; 110 while (len > 0) { 111 strncpy(q, p+1, *p); 112 len -= *p + 1; 113 q += *p; 114 p += *p + 1; 115 } 116 *q = '\0'; 117 printf(" %s\n", s); 118 free(s); 119 } 120#endif 121 do_print(hostTarget); 122 123 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), info->rls, kCFRunLoopDefaultMode); 124 CFRelease(info->rls); 125 CFSocketInvalidate(info->sockref); 126 CFRelease(info->sockref); 127 DNSServiceRefDeallocate(info->sdref); 128 free(info); 129} 130 131/* 132 * Handle newly-discovered services 133 */ 134static void 135browser_callback( 136 __unused DNSServiceRef sdRef, 137 DNSServiceFlags servFlags, 138 uint32_t interfaceIndex, 139 DNSServiceErrorType errorCode, 140 const char *serviceName, 141 const char *regType, 142 const char *replyDomain, 143 __unused void *context) 144{ 145 DNSServiceErrorType err; 146 CFSocketContext ctx = { 0, NULL, NULL, NULL, NULL }; 147 struct cbinfo *info; 148 149 if (errorCode != kDNSServiceErr_NoError) { 150 printf("DNS service discovery error: %d\n", errorCode); 151 return; 152 } 153#ifdef DEBUG 154 printf("browse: %s: %s, %s, %s\n", 155 (servFlags & kDNSServiceFlagsAdd) ? "new" : "gone", 156 serviceName, regType, replyDomain); 157#endif 158 if (!(servFlags & kDNSServiceFlagsAdd)) 159 return; 160 161 info = malloc(sizeof(*info)); 162 if (!info) { 163 printf("browse: out of memeory\n"); 164 return; 165 } 166 167 err = DNSServiceResolve(&info->sdref, servFlags, interfaceIndex, serviceName, regType, replyDomain, resolve_callback, info); 168 if (err != kDNSServiceErr_NoError) { 169 printf("DNSServiceResolve failed: %d\n", err); 170 free(info); 171 return; 172 } 173 ctx.info = (void*)info; 174 info->sockref = CFSocketCreateWithNative(kCFAllocatorDefault, DNSServiceRefSockFD(info->sdref), 175 kCFSocketReadCallBack, socket_callback, &ctx); 176 if (!info->sockref) { 177 printf("CFSocketCreateWithNative failed\n"); 178 DNSServiceRefDeallocate(info->sdref); 179 free(info); 180 return; 181 } 182 info->rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, info->sockref, 1); 183 CFRunLoopAddSource(CFRunLoopGetCurrent(), info->rls, kCFRunLoopDefaultMode); 184} 185 186int 187browse(void) 188{ 189 DNSServiceErrorType err; 190 CFSocketContext ctx = { 0, NULL, NULL, NULL, NULL }; 191 192 err = DNSServiceBrowse(&nfsinfo.sdref, 0, 0, "_nfs._tcp", NULL, browser_callback, NULL); 193 if (err != kDNSServiceErr_NoError) 194 return (1); 195 ctx.info = (void*)&nfsinfo; 196 nfsinfo.sockref = CFSocketCreateWithNative(kCFAllocatorDefault, DNSServiceRefSockFD(nfsinfo.sdref), 197 kCFSocketReadCallBack, socket_callback, &ctx); 198 if (!nfsinfo.sockref) 199 return (1); 200 nfsinfo.rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, nfsinfo.sockref, 1); 201 CFRunLoopAddSource(CFRunLoopGetCurrent(), nfsinfo.rls, kCFRunLoopDefaultMode); 202 203 /* For backwards compatibility, browse for "mountd" services too */ 204 err = DNSServiceBrowse(&mountdinfo.sdref, 0, 0, "_mountd._tcp", NULL, browser_callback, NULL); 205 if (err != kDNSServiceErr_NoError) 206 return (1); 207 ctx.info = (void*)&mountdinfo; 208 mountdinfo.sockref = CFSocketCreateWithNative(kCFAllocatorDefault, DNSServiceRefSockFD(mountdinfo.sdref), 209 kCFSocketReadCallBack, socket_callback, &ctx); 210 if (!mountdinfo.sockref) 211 return (1); 212 mountdinfo.rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, mountdinfo.sockref, 1); 213 CFRunLoopAddSource(CFRunLoopGetCurrent(), mountdinfo.rls, kCFRunLoopDefaultMode); 214 215 CFRunLoopRun(); 216 217 CFRelease(nfsinfo.rls); 218 CFSocketInvalidate(nfsinfo.sockref); 219 CFRelease(nfsinfo.sockref); 220 DNSServiceRefDeallocate(nfsinfo.sdref); 221 CFRelease(mountdinfo.rls); 222 CFSocketInvalidate(mountdinfo.sockref); 223 CFRelease(mountdinfo.sockref); 224 DNSServiceRefDeallocate(mountdinfo.sdref); 225 return (0); 226} 227 228