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