1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
24 Change History (most recent first):
26 $Log: ProxyResponder.c,v $
27 Revision 1.36 2005/08/04 03:12:47 mkrochma
28 <rdar://problem/4199236> Register reverse PTR record using multicast
30 Revision 1.35 2004/12/16 20:17:11 cheshire
31 <rdar://problem/3324626> Cache memory management improvements
33 Revision 1.34 2004/12/01 04:27:28 cheshire
34 <rdar://problem/3872803> Darwin patches for Solaris and Suse
35 Don't use uint32_t, etc. -- they require stdint.h, which doesn't exist on FreeBSD 4.x, Solaris, etc.
37 Revision 1.33 2004/11/30 22:37:01 cheshire
38 Update copyright dates and add "Mode: C; tab-width: 4" headers
40 Revision 1.32 2004/10/26 03:59:41 cheshire
43 Revision 1.31 2004/09/17 01:08:53 cheshire
44 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
45 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
46 declared in that file are ONLY appropriate to single-address-space embedded applications.
47 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
49 Revision 1.30 2004/09/17 00:31:52 cheshire
50 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
52 Revision 1.29 2004/09/16 01:58:22 cheshire
55 Revision 1.28 2004/06/25 00:26:27 rpantos
56 Changes to fix the Posix build on Solaris.
58 Revision 1.27 2004/03/12 08:03:14 cheshire
61 Revision 1.26 2004/01/25 00:00:39 cheshire
62 Change to use mDNSOpaque16fromIntVal() instead of shifting and masking
64 Revision 1.25 2003/12/08 20:47:02 rpantos
65 Add support for mDNSResponder on Linux.
67 Revision 1.24 2003/11/14 21:27:09 cheshire
68 <rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
69 Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers.
71 Revision 1.23 2003/10/30 19:39:28 cheshire
72 Fix warnings on certain compilers
74 Revision 1.22 2003/08/14 02:19:55 cheshire
75 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
77 Revision 1.21 2003/08/12 19:56:26 cheshire
80 Revision 1.20 2003/07/23 00:00:04 cheshire
83 Revision 1.19 2003/07/15 01:55:16 cheshire
84 <rdar://problem/3315777> Need to implement service registration with subtypes
86 Revision 1.18 2003/07/02 21:19:58 cheshire
87 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
89 Revision 1.17 2003/05/26 03:21:29 cheshire
90 Tidy up address structure naming:
91 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
92 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
93 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
95 Revision 1.16 2003/05/26 03:01:28 cheshire
96 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
98 Revision 1.15 2003/05/06 00:00:50 cheshire
99 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
101 Revision 1.14 2003/04/25 01:45:57 cheshire
102 <rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
104 Revision 1.13 2003/04/18 22:46:12 cheshire
105 Fix mistake in 1.8 -- INADDR_NONE is 0xFFFFFFFF, not 0
107 Revision 1.12 2003/04/16 02:11:07 cheshire
108 Fixed mDNS_RegisterNoSuchService non-existence function so that it works again
110 Revision 1.11 2003/03/31 22:49:35 cheshire
115 #include <stdio.h> // For printf()
116 #include <stdlib.h> // For exit() etc.
117 #include <string.h> // For strlen() etc.
118 #include <unistd.h> // For select()
119 #include <signal.h> // For SIGINT, SIGTERM
120 #include <errno.h> // For errno, EINTR
121 #include <arpa/inet.h> // For inet_addr()
122 #include <netinet/in.h> // For INADDR_NONE
123 #include <netdb.h> // For gethostbyname()
125 #include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above
126 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
127 #include "ExampleClientApp.h"
129 // Compatibility workaround: Solaris 2.5 has no INADDR_NONE
131 #define INADDR_NONE (mDNSu32)0xffffffff
134 //*************************************************************************************************************
136 static mDNS mDNSStorage
; // mDNS core uses this to store its globals
137 static mDNS_PlatformSupport PlatformStorage
; // Stores this platform's globals
139 //*************************************************************************************************************
140 // Proxy Host Registration
145 domainlabel hostlabel
; // Conforms to standard DNS letter-digit-hyphen host name rules
146 AuthRecord RR_A
; // 'A' (address) record for our ".local" name
147 AuthRecord RR_PTR
; // PTR (reverse lookup) record
150 mDNSlocal
void HostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
152 ProxyHost
*f
= (ProxyHost
*)rr
->RecordContext
;
153 if (result
== mStatus_NoError
)
154 debugf("Host name successfully registered: %##s", rr
->resrec
.name
->c
);
157 debugf("Host name conflict for %##s", rr
->resrec
.name
->c
);
158 mDNS_Deregister(m
, &f
->RR_A
);
159 mDNS_Deregister(m
, &f
->RR_PTR
);
164 mDNSlocal mStatus
mDNS_RegisterProxyHost(mDNS
*m
, ProxyHost
*p
)
168 mDNS_SetupResourceRecord(&p
->RR_A
, mDNSNULL
, mDNSInterface_Any
, kDNSType_A
, 60, kDNSRecordTypeUnique
, HostNameCallback
, p
);
169 mDNS_SetupResourceRecord(&p
->RR_PTR
, mDNSNULL
, mDNSInterface_Any
, kDNSType_PTR
, 60, kDNSRecordTypeKnownUnique
, HostNameCallback
, p
);
171 p
->RR_A
.resrec
.name
->c
[0] = 0;
172 AppendDomainLabel(p
->RR_A
.resrec
.name
, &p
->hostlabel
);
173 AppendLiteralLabelString(p
->RR_A
.resrec
.name
, "local");
175 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]);
176 MakeDomainNameFromDNSNameString(p
->RR_PTR
.resrec
.name
, buffer
);
177 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
179 p
->RR_A
. resrec
.rdata
->u
.ipv4
= p
->ip
;
180 AssignDomainName(&p
->RR_PTR
.resrec
.rdata
->u
.name
, p
->RR_A
.resrec
.name
);
182 mDNS_Register(m
, &p
->RR_A
);
183 mDNS_Register(m
, &p
->RR_PTR
);
185 debugf("Made Proxy Host Records for %##s", p
->RR_A
.resrec
.name
->c
);
187 return(mStatus_NoError
);
190 //*************************************************************************************************************
191 // Service Registration
193 // This sample ServiceCallback just calls mDNS_RenameAndReregisterService to automatically pick a new
194 // unique name for the service. For a device such as a printer, this may be appropriate.
195 // For a device with a user interface, and a screen, and a keyboard, the appropriate
196 // response may be to prompt the user and ask them to choose a new name for the service.
197 mDNSlocal
void ServiceCallback(mDNS
*const m
, ServiceRecordSet
*const sr
, mStatus result
)
201 case mStatus_NoError
: debugf("Callback: %##s Name Registered", sr
->RR_SRV
.resrec
.name
->c
); break;
202 case mStatus_NameConflict
: debugf("Callback: %##s Name Conflict", sr
->RR_SRV
.resrec
.name
->c
); break;
203 case mStatus_MemFree
: debugf("Callback: %##s Memory Free", sr
->RR_SRV
.resrec
.name
->c
); break;
204 default: debugf("Callback: %##s Unknown Result %ld", sr
->RR_SRV
.resrec
.name
->c
, result
); break;
207 if (result
== mStatus_NoError
)
209 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
210 ConvertDomainNameToCString(sr
->RR_SRV
.resrec
.name
, buffer
);
211 printf("Service %s now registered and active\n", buffer
);
214 if (result
== mStatus_NameConflict
)
216 char buffer1
[MAX_ESCAPED_DOMAIN_NAME
], buffer2
[MAX_ESCAPED_DOMAIN_NAME
];
217 ConvertDomainNameToCString(sr
->RR_SRV
.resrec
.name
, buffer1
);
218 mDNS_RenameAndReregisterService(m
, sr
, mDNSNULL
);
219 ConvertDomainNameToCString(sr
->RR_SRV
.resrec
.name
, buffer2
);
220 printf("Name Conflict! %s renamed as %s\n", buffer1
, buffer2
);
224 // RegisterService() is a simple wrapper function which takes C string
225 // parameters, converts them to domainname parameters, and calls mDNS_RegisterService()
226 mDNSlocal
void RegisterService(mDNS
*m
, ServiceRecordSet
*recordset
,
227 const char name
[], const char type
[], const char domain
[],
228 const domainname
*host
, mDNSu16 PortAsNumber
, int argc
, char **argv
)
232 unsigned char txtbuffer
[1024], *bptr
= txtbuffer
;
233 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
235 MakeDomainLabelFromLiteralString(&n
, name
);
236 MakeDomainNameFromDNSNameString(&t
, type
);
237 MakeDomainNameFromDNSNameString(&d
, domain
);
240 int len
= strlen(argv
[0]);
241 printf("STR: %s\n", argv
[0]);
243 strcpy((char*)(bptr
+1), argv
[0]);
249 mDNS_RegisterService(m
, recordset
,
250 &n
, &t
, &d
, // Name, type, domain
251 host
, mDNSOpaque16fromIntVal(PortAsNumber
),
252 txtbuffer
, bptr
-txtbuffer
, // TXT data, length
253 mDNSNULL
, 0, // Subtypes
254 mDNSInterface_Any
, // Interface ID
255 ServiceCallback
, mDNSNULL
); // Callback and context
257 ConvertDomainNameToCString(recordset
->RR_SRV
.resrec
.name
, buffer
);
258 printf("Made Service Records for %s\n", buffer
);
261 //*************************************************************************************************************
262 // Service non-existence assertion
263 // (claiming a service name without actually providing a service at that name, to prevent anyone else using that name)
264 // This is useful to avoid confusion between similar services
265 // e.g. A printer that implements IPP printing service using the name "My Printer", but doesn't implement LPR service,
266 // should also claim the LPR service name "My Printer" to stop a different printer offering LPR service under the same name,
267 // since it would be confusing to users to have two equivalent services with the same name.
269 mDNSlocal
void NoSuchServiceCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
271 domainname
*proxyhostname
= (domainname
*)rr
->RecordContext
;
274 case mStatus_NoError
: debugf("Callback: %##s Name Registered", rr
->resrec
.name
->c
); break;
275 case mStatus_NameConflict
: debugf("Callback: %##s Name Conflict", rr
->resrec
.name
->c
); break;
276 case mStatus_MemFree
: debugf("Callback: %##s Memory Free", rr
->resrec
.name
->c
); break;
277 default: debugf("Callback: %##s Unknown Result %ld", rr
->resrec
.name
->c
, result
); break;
280 if (result
== mStatus_NoError
)
282 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
283 ConvertDomainNameToCString(rr
->resrec
.name
, buffer
);
284 printf("Non-existence assertion %s now registered and active\n", buffer
);
287 if (result
== mStatus_NameConflict
)
291 char buffer1
[MAX_ESCAPED_DOMAIN_NAME
], buffer2
[MAX_ESCAPED_DOMAIN_NAME
];
292 ConvertDomainNameToCString(rr
->resrec
.name
, buffer1
);
293 DeconstructServiceName(rr
->resrec
.name
, &n
, &t
, &d
);
294 IncrementLabelSuffix(&n
, mDNStrue
);
295 mDNS_RegisterNoSuchService(m
, rr
, &n
, &t
, &d
, proxyhostname
, mDNSInterface_Any
, NoSuchServiceCallback
, mDNSNULL
);
296 ConvertDomainNameToCString(rr
->resrec
.name
, buffer2
);
297 printf("Name Conflict! %s renamed as %s\n", buffer1
, buffer2
);
301 mDNSlocal
void RegisterNoSuchService(mDNS
*m
, AuthRecord
*const rr
, domainname
*proxyhostname
,
302 const char name
[], const char type
[], const char domain
[])
306 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
307 MakeDomainLabelFromLiteralString(&n
, name
);
308 MakeDomainNameFromDNSNameString(&t
, type
);
309 MakeDomainNameFromDNSNameString(&d
, domain
);
310 mDNS_RegisterNoSuchService(m
, rr
, &n
, &t
, &d
, proxyhostname
, mDNSInterface_Any
, NoSuchServiceCallback
, proxyhostname
);
311 ConvertDomainNameToCString(rr
->resrec
.name
, buffer
);
312 printf("Made Non-existence Record for %s\n", buffer
);
315 //*************************************************************************************************************
318 mDNSexport
int main(int argc
, char **argv
)
323 if (argc
< 3) goto usage
;
325 status
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
326 mDNS_Init_NoCache
, mDNS_Init_ZeroCacheSize
,
327 mDNS_Init_DontAdvertiseLocalAddresses
,
328 mDNS_Init_NoInitCallback
, mDNS_Init_NoInitCallbackContext
);
329 if (status
) { fprintf(stderr
, "Daemon start: mDNS_Init failed %ld\n", status
); return(status
); }
331 mDNSPosixListenForSignalInEventLoop(SIGINT
);
332 mDNSPosixListenForSignalInEventLoop(SIGTERM
);
334 if (!strcmp(argv
[1], "-"))
336 domainname proxyhostname
;
337 AuthRecord proxyrecord
;
338 if (argc
< 5) goto usage
;
339 proxyhostname
.c
[0] = 0;
340 AppendLiteralLabelString(&proxyhostname
, argv
[2]);
341 AppendLiteralLabelString(&proxyhostname
, "local");
342 RegisterNoSuchService(&mDNSStorage
, &proxyrecord
, &proxyhostname
, argv
[3], argv
[4], "local.");
347 ServiceRecordSet proxyservice
;
349 proxyhost
.ip
.NotAnInteger
= inet_addr(argv
[1]);
350 if (proxyhost
.ip
.NotAnInteger
== INADDR_NONE
) // INADDR_NONE is 0xFFFFFFFF
352 struct hostent
*h
= gethostbyname(argv
[1]);
353 if (h
) proxyhost
.ip
.NotAnInteger
= *(long*)h
->h_addr
;
355 if (proxyhost
.ip
.NotAnInteger
== INADDR_NONE
) // INADDR_NONE is 0xFFFFFFFF
357 fprintf(stderr
, "%s is not valid host address\n", argv
[1]);
361 MakeDomainLabelFromLiteralString(&proxyhost
.hostlabel
, argv
[2]);
363 mDNS_RegisterProxyHost(&mDNSStorage
, &proxyhost
);
366 RegisterService(&mDNSStorage
, &proxyservice
, argv
[3], argv
[4], "local.",
367 proxyhost
.RR_A
.resrec
.name
, atoi(argv
[5]), argc
-6, &argv
[6]);
372 struct timeval timeout
= { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM
373 mDNSBool gotSomething
;
374 mDNSPosixRunEventLoopOnce(&mDNSStorage
, &timeout
, &signals
, &gotSomething
);
376 while ( !( sigismember( &signals
, SIGINT
) || sigismember( &signals
, SIGTERM
)));
378 mDNS_Close(&mDNSStorage
);
383 fprintf(stderr
, "%s ip hostlabel [srvname srvtype port txt [txt ...]]\n", argv
[0]);
384 fprintf(stderr
, "ip Real IP address (or valid host name) of the host where the service actually resides\n");
385 fprintf(stderr
, "hostlabel First label of the dot-local host name to create for this host, e.g. \"foo\" for \"foo.local.\"\n");
386 fprintf(stderr
, "srvname Descriptive name of service, e.g. \"Stuart's Ink Jet Printer\"\n");
387 fprintf(stderr
, "srvtype IANA service type, e.g. \"_ipp._tcp\" or \"_ssh._tcp\", etc.\n");
388 fprintf(stderr
, "port Port number where the service resides (1-65535)\n");
389 fprintf(stderr
, "txt Additional name/value pairs specified in service definition, e.g. \"pdl=application/postscript\"\n");
390 fprintf(stderr
, "e.g. %s 169.254.12.34 thehost (just create a dot-local host name)\n", argv
[0]);
391 fprintf(stderr
, "or %s 169.254.12.34 thehost \"My Printer\" _printer._tcp. 515 rp=lpt1 pdl=application/postscript\n", argv
[0]);
392 fprintf(stderr
, "or %s - thehost \"My Printer\" _printer._tcp. (assertion of non-existence)\n", argv
[0]);