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