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