1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 Change History (most recent first):
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)
23 Revision 1.44 2007/04/22 20:16:25 cheshire
24 Fix compiler errors (const parameter declarations)
26 Revision 1.43 2007/04/16 20:49:39 cheshire
27 Fix compile errors for mDNSPosix build
29 Revision 1.42 2007/03/06 22:45:53 cheshire
31 <rdar://problem/4138615> argv buffer overflow issues
33 Revision 1.41 2007/02/28 01:51:22 cheshire
34 Added comment about reverse-order IP address
36 Revision 1.40 2007/01/05 04:32:13 cheshire
37 Change "(domainname *)" cast to "(const domainname *)"
39 Revision 1.39 2006/08/14 23:24:46 cheshire
40 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
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
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
48 Revision 1.36 2005/08/04 03:12:47 mkrochma
49 <rdar://problem/4199236> Register reverse PTR record using multicast
51 Revision 1.35 2004/12/16 20:17:11 cheshire
52 <rdar://problem/3324626> Cache memory management improvements
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.
58 Revision 1.33 2004/11/30 22:37:01 cheshire
59 Update copyright dates and add "Mode: C; tab-width: 4" headers
61 Revision 1.32 2004/10/26 03:59:41 cheshire
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.
70 Revision 1.30 2004/09/17 00:31:52 cheshire
71 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
73 Revision 1.29 2004/09/16 01:58:22 cheshire
76 Revision 1.28 2004/06/25 00:26:27 rpantos
77 Changes to fix the Posix build on Solaris.
79 Revision 1.27 2004/03/12 08:03:14 cheshire
82 Revision 1.26 2004/01/25 00:00:39 cheshire
83 Change to use mDNSOpaque16fromIntVal() instead of shifting and masking
85 Revision 1.25 2003/12/08 20:47:02 rpantos
86 Add support for mDNSResponder on Linux.
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.
92 Revision 1.23 2003/10/30 19:39:28 cheshire
93 Fix warnings on certain compilers
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
98 Revision 1.21 2003/08/12 19:56:26 cheshire
101 Revision 1.20 2003/07/23 00:00:04 cheshire
104 Revision 1.19 2003/07/15 01:55:16 cheshire
105 <rdar://problem/3315777> Need to implement service registration with subtypes
107 Revision 1.18 2003/07/02 21:19:58 cheshire
108 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
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
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
119 Revision 1.15 2003/05/06 00:00:50 cheshire
120 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
122 Revision 1.14 2003/04/25 01:45:57 cheshire
123 <rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
125 Revision 1.13 2003/04/18 22:46:12 cheshire
126 Fix mistake in 1.8 -- INADDR_NONE is 0xFFFFFFFF, not 0
128 Revision 1.12 2003/04/16 02:11:07 cheshire
129 Fixed mDNS_RegisterNoSuchService non-existence function so that it works again
131 Revision 1.11 2003/03/31 22:49:35 cheshire
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()
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"
150 // Compatibility workaround: Solaris 2.5 has no INADDR_NONE
152 #define INADDR_NONE (mDNSu32)0xffffffff
155 //*************************************************************************************************************
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";
161 //*************************************************************************************************************
162 // Proxy Host Registration
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
172 mDNSlocal
void HostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
174 ProxyHost
*f
= (ProxyHost
*)rr
->RecordContext
;
175 if (result
== mStatus_NoError
)
176 debugf("Host name successfully registered: %##s", rr
->resrec
.name
->c
);
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
);
186 mDNSlocal mStatus
mDNS_RegisterProxyHost(mDNS
*m
, ProxyHost
*p
)
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
);
193 p
->RR_A
.namestorage
.c
[0] = 0;
194 AppendDomainLabel(&p
->RR_A
.namestorage
, &p
->hostlabel
);
195 AppendLiteralLabelString(&p
->RR_A
.namestorage
, "local");
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
202 p
->RR_A
. resrec
.rdata
->u
.ipv4
= p
->ip
;
203 AssignDomainName(&p
->RR_PTR
.resrec
.rdata
->u
.name
, p
->RR_A
.resrec
.name
);
205 mDNS_Register(m
, &p
->RR_A
);
206 mDNS_Register(m
, &p
->RR_PTR
);
208 debugf("Made Proxy Host Records for %##s", p
->RR_A
.resrec
.name
->c
);
210 return(mStatus_NoError
);
213 //*************************************************************************************************************
214 // Service Registration
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
)
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;
230 if (result
== mStatus_NoError
)
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
);
237 if (result
== mStatus_NameConflict
)
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
);
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
)
255 unsigned char txtbuffer
[1024], *bptr
= txtbuffer
;
256 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
258 MakeDomainLabelFromLiteralString(&n
, name
);
259 MakeDomainNameFromDNSNameString(&t
, type
);
260 MakeDomainNameFromDNSNameString(&d
, domain
);
263 int len
= strlen(argv
[0]);
264 if (len
> 255 || bptr
+ 1 + len
>= txtbuffer
+ sizeof(txtbuffer
)) break;
265 printf("STR: %s\n", argv
[0]);
267 strcpy((char*)(bptr
+1), argv
[0]);
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
281 ConvertDomainNameToCString(recordset
->RR_SRV
.resrec
.name
, buffer
);
282 printf("Made Service Records for %s\n", buffer
);
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.
293 mDNSlocal
void NoSuchServiceCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
295 const domainname
*proxyhostname
= (const domainname
*)rr
->RecordContext
;
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;
304 if (result
== mStatus_NoError
)
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
);
311 if (result
== mStatus_NameConflict
)
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
);
325 mDNSlocal
void RegisterNoSuchService(mDNS
*m
, AuthRecord
*const rr
, domainname
*proxyhostname
,
326 const char name
[], const char type
[], const char domain
[])
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
);
339 //*************************************************************************************************************
342 mDNSexport
int main(int argc
, char **argv
)
347 if (argc
< 3) goto usage
;
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
); }
355 mDNSPosixListenForSignalInEventLoop(SIGINT
);
356 mDNSPosixListenForSignalInEventLoop(SIGTERM
);
358 if (!strcmp(argv
[1], "-"))
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.");
371 ServiceRecordSet proxyservice
;
373 proxyhost
.ip
.NotAnInteger
= inet_addr(argv
[1]);
374 if (proxyhost
.ip
.NotAnInteger
== INADDR_NONE
) // INADDR_NONE is 0xFFFFFFFF
376 struct hostent
*h
= gethostbyname(argv
[1]);
377 if (h
) proxyhost
.ip
.NotAnInteger
= *(long*)h
->h_addr
;
379 if (proxyhost
.ip
.NotAnInteger
== INADDR_NONE
) // INADDR_NONE is 0xFFFFFFFF
381 fprintf(stderr
, "%s is not valid host address\n", argv
[1]);
385 MakeDomainLabelFromLiteralString(&proxyhost
.hostlabel
, argv
[2]);
387 mDNS_RegisterProxyHost(&mDNSStorage
, &proxyhost
);
390 RegisterService(&mDNSStorage
, &proxyservice
, argv
[3], argv
[4], "local.",
391 proxyhost
.RR_A
.resrec
.name
, atoi(argv
[5]), argc
-6, &argv
[6]);
396 struct timeval timeout
= { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM
397 mDNSBool gotSomething
;
398 mDNSPosixRunEventLoopOnce(&mDNSStorage
, &timeout
, &signals
, &gotSomething
);
400 while ( !( sigismember( &signals
, SIGINT
) || sigismember( &signals
, SIGTERM
)));
402 mDNS_Close(&mDNSStorage
);
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]);