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