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