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