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