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