]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSPosix/ProxyResponder.c
bca7356dd133b0fa649c737e9ea48b496e65ce7c
[apple/mdnsresponder.git] / mDNSPosix / ProxyResponder.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23
24 Change History (most recent first):
25
26 $Log: ProxyResponder.c,v $
27 Revision 1.34 2004/12/01 04:27:28 cheshire
28 <rdar://problem/3872803> Darwin patches for Solaris and Suse
29 Don't use uint32_t, etc. -- they require stdint.h, which doesn't exist on FreeBSD 4.x, Solaris, etc.
30
31 Revision 1.33 2004/11/30 22:37:01 cheshire
32 Update copyright dates and add "Mode: C; tab-width: 4" headers
33
34 Revision 1.32 2004/10/26 03:59:41 cheshire
35 Update comments
36
37 Revision 1.31 2004/09/17 01:08:53 cheshire
38 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
39 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
40 declared in that file are ONLY appropriate to single-address-space embedded applications.
41 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
42
43 Revision 1.30 2004/09/17 00:31:52 cheshire
44 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
45
46 Revision 1.29 2004/09/16 01:58:22 cheshire
47 Fix compiler warnings
48
49 Revision 1.28 2004/06/25 00:26:27 rpantos
50 Changes to fix the Posix build on Solaris.
51
52 Revision 1.27 2004/03/12 08:03:14 cheshire
53 Update comments
54
55 Revision 1.26 2004/01/25 00:00:39 cheshire
56 Change to use mDNSOpaque16fromIntVal() instead of shifting and masking
57
58 Revision 1.25 2003/12/08 20:47:02 rpantos
59 Add support for mDNSResponder on Linux.
60
61 Revision 1.24 2003/11/14 21:27:09 cheshire
62 <rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
63 Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers.
64
65 Revision 1.23 2003/10/30 19:39:28 cheshire
66 Fix warnings on certain compilers
67
68 Revision 1.22 2003/08/14 02:19:55 cheshire
69 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
70
71 Revision 1.21 2003/08/12 19:56:26 cheshire
72 Update to APSL 2.0
73
74 Revision 1.20 2003/07/23 00:00:04 cheshire
75 Add comments
76
77 Revision 1.19 2003/07/15 01:55:16 cheshire
78 <rdar://problem/3315777> Need to implement service registration with subtypes
79
80 Revision 1.18 2003/07/02 21:19:58 cheshire
81 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
82
83 Revision 1.17 2003/05/26 03:21:29 cheshire
84 Tidy up address structure naming:
85 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
86 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
87 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
88
89 Revision 1.16 2003/05/26 03:01:28 cheshire
90 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
91
92 Revision 1.15 2003/05/06 00:00:50 cheshire
93 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
94
95 Revision 1.14 2003/04/25 01:45:57 cheshire
96 <rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
97
98 Revision 1.13 2003/04/18 22:46:12 cheshire
99 Fix mistake in 1.8 -- INADDR_NONE is 0xFFFFFFFF, not 0
100
101 Revision 1.12 2003/04/16 02:11:07 cheshire
102 Fixed mDNS_RegisterNoSuchService non-existence function so that it works again
103
104 Revision 1.11 2003/03/31 22:49:35 cheshire
105 Add "$Log" header
106
107 */
108
109 #include <stdio.h> // For printf()
110 #include <stdlib.h> // For exit() etc.
111 #include <string.h> // For strlen() etc.
112 #include <unistd.h> // For select()
113 #include <signal.h> // For SIGINT, SIGTERM
114 #include <errno.h> // For errno, EINTR
115 #include <arpa/inet.h> // For inet_addr()
116 #include <netinet/in.h> // For INADDR_NONE
117 #include <netdb.h> // For gethostbyname()
118
119 #include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above
120 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
121 #include "ExampleClientApp.h"
122
123 // Compatibility workaround: Solaris 2.5 has no INADDR_NONE
124 #ifndef INADDR_NONE
125 #define INADDR_NONE (mDNSu32)0xffffffff
126 #endif
127
128 //*************************************************************************************************************
129 // Globals
130 static mDNS mDNSStorage; // mDNS core uses this to store its globals
131 static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
132
133 //*************************************************************************************************************
134 // Proxy Host Registration
135
136 typedef struct
137 {
138 mDNSv4Addr ip;
139 domainlabel hostlabel; // Conforms to standard DNS letter-digit-hyphen host name rules
140 AuthRecord RR_A; // 'A' (address) record for our ".local" name
141 AuthRecord RR_PTR; // PTR (reverse lookup) record
142 } ProxyHost;
143
144 mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
145 {
146 ProxyHost *f = (ProxyHost*)rr->RecordContext;
147 if (result == mStatus_NoError)
148 debugf("Host name successfully registered: %##s", rr->resrec.name.c);
149 else
150 {
151 debugf("Host name conflict for %##s", rr->resrec.name.c);
152 mDNS_Deregister(m, &f->RR_A);
153 mDNS_Deregister(m, &f->RR_PTR);
154 exit(-1);
155 }
156 }
157
158 mDNSlocal mStatus mDNS_RegisterProxyHost(mDNS *m, ProxyHost *p)
159 {
160 char buffer[32];
161
162 mDNS_SetupResourceRecord(&p->RR_A, mDNSNULL, mDNSInterface_Any, kDNSType_A, 60, kDNSRecordTypeUnique, HostNameCallback, p);
163 mDNS_SetupResourceRecord(&p->RR_PTR, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, HostNameCallback, p);
164
165 p->RR_A.resrec.name.c[0] = 0;
166 AppendDomainLabel(&p->RR_A.resrec.name, &p->hostlabel);
167 AppendLiteralLabelString(&p->RR_A.resrec.name, "local");
168
169 mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p->ip.b[3], p->ip.b[2], p->ip.b[1], p->ip.b[0]);
170 MakeDomainNameFromDNSNameString(&p->RR_PTR.resrec.name, buffer);
171
172 p->RR_A. resrec.rdata->u.ipv4 = p->ip;
173 p->RR_PTR.resrec.rdata->u.name = p->RR_A.resrec.name;
174
175 mDNS_Register(m, &p->RR_A);
176 mDNS_Register(m, &p->RR_PTR);
177
178 debugf("Made Proxy Host Records for %##s", p->RR_A.resrec.name.c);
179
180 return(mStatus_NoError);
181 }
182
183 //*************************************************************************************************************
184 // Service Registration
185
186 // This sample ServiceCallback just calls mDNS_RenameAndReregisterService to automatically pick a new
187 // unique name for the service. For a device such as a printer, this may be appropriate.
188 // For a device with a user interface, and a screen, and a keyboard, the appropriate
189 // response may be to prompt the user and ask them to choose a new name for the service.
190 mDNSlocal void ServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result)
191 {
192 switch (result)
193 {
194 case mStatus_NoError: debugf("Callback: %##s Name Registered", sr->RR_SRV.resrec.name.c); break;
195 case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", sr->RR_SRV.resrec.name.c); break;
196 case mStatus_MemFree: debugf("Callback: %##s Memory Free", sr->RR_SRV.resrec.name.c); break;
197 default: debugf("Callback: %##s Unknown Result %ld", sr->RR_SRV.resrec.name.c, result); break;
198 }
199
200 if (result == mStatus_NoError)
201 {
202 char buffer[MAX_ESCAPED_DOMAIN_NAME];
203 ConvertDomainNameToCString(&sr->RR_SRV.resrec.name, buffer);
204 printf("Service %s now registered and active\n", buffer);
205 }
206
207 if (result == mStatus_NameConflict)
208 {
209 char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME];
210 ConvertDomainNameToCString(&sr->RR_SRV.resrec.name, buffer1);
211 mDNS_RenameAndReregisterService(m, sr, mDNSNULL);
212 ConvertDomainNameToCString(&sr->RR_SRV.resrec.name, buffer2);
213 printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2);
214 }
215 }
216
217 // RegisterService() is a simple wrapper function which takes C string
218 // parameters, converts them to domainname parameters, and calls mDNS_RegisterService()
219 mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset,
220 const char name[], const char type[], const char domain[],
221 const domainname *host, mDNSu16 PortAsNumber, int argc, char **argv)
222 {
223 domainlabel n;
224 domainname t, d;
225 unsigned char txtbuffer[1024], *bptr = txtbuffer;
226 char buffer[MAX_ESCAPED_DOMAIN_NAME];
227
228 MakeDomainLabelFromLiteralString(&n, name);
229 MakeDomainNameFromDNSNameString(&t, type);
230 MakeDomainNameFromDNSNameString(&d, domain);
231 while (argc)
232 {
233 int len = strlen(argv[0]);
234 printf("STR: %s\n", argv[0]);
235 bptr[0] = len;
236 strcpy((char*)(bptr+1), argv[0]);
237 bptr += 1 + len;
238 argc--;
239 argv++;
240 }
241
242 mDNS_RegisterService(m, recordset,
243 &n, &t, &d, // Name, type, domain
244 host, mDNSOpaque16fromIntVal(PortAsNumber),
245 txtbuffer, bptr-txtbuffer, // TXT data, length
246 mDNSNULL, 0, // Subtypes
247 mDNSInterface_Any, // Interface ID
248 ServiceCallback, mDNSNULL); // Callback and context
249
250 ConvertDomainNameToCString(&recordset->RR_SRV.resrec.name, buffer);
251 printf("Made Service Records for %s\n", buffer);
252 }
253
254 //*************************************************************************************************************
255 // Service non-existence assertion
256 // (claiming a service name without actually providing a service at that name, to prevent anyone else using that name)
257 // This is useful to avoid confusion between similar services
258 // e.g. A printer that implements IPP printing service using the name "My Printer", but doesn't implement LPR service,
259 // should also claim the LPR service name "My Printer" to stop a different printer offering LPR service under the same name,
260 // since it would be confusing to users to have two equivalent services with the same name.
261
262 mDNSlocal void NoSuchServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
263 {
264 domainname *proxyhostname = (domainname *)rr->RecordContext;
265 switch (result)
266 {
267 case mStatus_NoError: debugf("Callback: %##s Name Registered", rr->resrec.name.c); break;
268 case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", rr->resrec.name.c); break;
269 case mStatus_MemFree: debugf("Callback: %##s Memory Free", rr->resrec.name.c); break;
270 default: debugf("Callback: %##s Unknown Result %ld", rr->resrec.name.c, result); break;
271 }
272
273 if (result == mStatus_NoError)
274 {
275 char buffer[MAX_ESCAPED_DOMAIN_NAME];
276 ConvertDomainNameToCString(&rr->resrec.name, buffer);
277 printf("Non-existence assertion %s now registered and active\n", buffer);
278 }
279
280 if (result == mStatus_NameConflict)
281 {
282 domainlabel n;
283 domainname t, d;
284 char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME];
285 ConvertDomainNameToCString(&rr->resrec.name, buffer1);
286 DeconstructServiceName(&rr->resrec.name, &n, &t, &d);
287 IncrementLabelSuffix(&n, mDNStrue);
288 mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, mDNSNULL);
289 ConvertDomainNameToCString(&rr->resrec.name, buffer2);
290 printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2);
291 }
292 }
293
294 mDNSlocal void RegisterNoSuchService(mDNS *m, AuthRecord *const rr, domainname *proxyhostname,
295 const char name[], const char type[], const char domain[])
296 {
297 domainlabel n;
298 domainname t, d;
299 char buffer[MAX_ESCAPED_DOMAIN_NAME];
300 MakeDomainLabelFromLiteralString(&n, name);
301 MakeDomainNameFromDNSNameString(&t, type);
302 MakeDomainNameFromDNSNameString(&d, domain);
303 mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, proxyhostname);
304 ConvertDomainNameToCString(&rr->resrec.name, buffer);
305 printf("Made Non-existence Record for %s\n", buffer);
306 }
307
308 //*************************************************************************************************************
309 // Main
310
311 mDNSexport int main(int argc, char **argv)
312 {
313 mStatus status;
314 sigset_t signals;
315
316 if (argc < 3) goto usage;
317
318 status = mDNS_Init(&mDNSStorage, &PlatformStorage,
319 mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
320 mDNS_Init_DontAdvertiseLocalAddresses,
321 mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
322 if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %ld\n", status); return(status); }
323
324 mDNSPosixListenForSignalInEventLoop(SIGINT);
325 mDNSPosixListenForSignalInEventLoop(SIGTERM);
326
327 if (!strcmp(argv[1], "-"))
328 {
329 domainname proxyhostname;
330 AuthRecord proxyrecord;
331 if (argc < 5) goto usage;
332 proxyhostname.c[0] = 0;
333 AppendLiteralLabelString(&proxyhostname, argv[2]);
334 AppendLiteralLabelString(&proxyhostname, "local");
335 RegisterNoSuchService(&mDNSStorage, &proxyrecord, &proxyhostname, argv[3], argv[4], "local.");
336 }
337 else
338 {
339 ProxyHost proxyhost;
340 ServiceRecordSet proxyservice;
341
342 proxyhost.ip.NotAnInteger = inet_addr(argv[1]);
343 if (proxyhost.ip.NotAnInteger == INADDR_NONE) // INADDR_NONE is 0xFFFFFFFF
344 {
345 struct hostent *h = gethostbyname(argv[1]);
346 if (h) proxyhost.ip.NotAnInteger = *(long*)h->h_addr;
347 }
348 if (proxyhost.ip.NotAnInteger == INADDR_NONE) // INADDR_NONE is 0xFFFFFFFF
349 {
350 fprintf(stderr, "%s is not valid host address\n", argv[1]);
351 return(-1);
352 }
353
354 MakeDomainLabelFromLiteralString(&proxyhost.hostlabel, argv[2]);
355
356 mDNS_RegisterProxyHost(&mDNSStorage, &proxyhost);
357
358 if (argc >=6)
359 RegisterService(&mDNSStorage, &proxyservice, argv[3], argv[4], "local.",
360 &proxyhost.RR_A.resrec.name, atoi(argv[5]), argc-6, &argv[6]);
361 }
362
363 do
364 {
365 struct timeval timeout = { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM
366 mDNSBool gotSomething;
367 mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething);
368 }
369 while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM)));
370
371 mDNS_Close(&mDNSStorage);
372
373 return(0);
374
375 usage:
376 fprintf(stderr, "%s ip hostlabel [srvname srvtype port txt [txt ...]]\n", argv[0]);
377 fprintf(stderr, "ip Real IP address (or valid host name) of the host where the service actually resides\n");
378 fprintf(stderr, "hostlabel First label of the dot-local host name to create for this host, e.g. \"foo\" for \"foo.local.\"\n");
379 fprintf(stderr, "srvname Descriptive name of service, e.g. \"Stuart's Ink Jet Printer\"\n");
380 fprintf(stderr, "srvtype IANA service type, e.g. \"_ipp._tcp\" or \"_ssh._tcp\", etc.\n");
381 fprintf(stderr, "port Port number where the service resides (1-65535)\n");
382 fprintf(stderr, "txt Additional name/value pairs specified in service definition, e.g. \"pdl=application/postscript\"\n");
383 fprintf(stderr, "e.g. %s 169.254.12.34 thehost (just create a dot-local host name)\n", argv[0]);
384 fprintf(stderr, "or %s 169.254.12.34 thehost \"My Printer\" _printer._tcp. 515 rp=lpt1 pdl=application/postscript\n", argv[0]);
385 fprintf(stderr, "or %s - thehost \"My Printer\" _printer._tcp. (assertion of non-existence)\n", argv[0]);
386 return(-1);
387 }