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