2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
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
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.
23 * @APPLE_LICENSE_HEADER_END@
25 Change History (most recent first):
27 $Log: ProxyResponder.c,v $
28 Revision 1.27 2004/03/12 08:03:14 cheshire
31 Revision 1.26 2004/01/25 00:00:39 cheshire
32 Change to use mDNSOpaque16fromIntVal() instead of shifting and masking
34 Revision 1.25 2003/12/08 20:47:02 rpantos
35 Add support for mDNSResponder on Linux.
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.
41 Revision 1.23 2003/10/30 19:39:28 cheshire
42 Fix warnings on certain compilers
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
47 Revision 1.21 2003/08/12 19:56:26 cheshire
50 Revision 1.20 2003/07/23 00:00:04 cheshire
53 Revision 1.19 2003/07/15 01:55:16 cheshire
54 <rdar://problem/3315777> Need to implement service registration with subtypes
56 Revision 1.18 2003/07/02 21:19:58 cheshire
57 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
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
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
68 Revision 1.15 2003/05/06 00:00:50 cheshire
69 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
71 Revision 1.14 2003/04/25 01:45:57 cheshire
72 <rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
74 Revision 1.13 2003/04/18 22:46:12 cheshire
75 Fix mistake in 1.8 -- INADDR_NONE is 0xFFFFFFFF, not 0
77 Revision 1.12 2003/04/16 02:11:07 cheshire
78 Fixed mDNS_RegisterNoSuchService non-existance function so that it works again
80 Revision 1.11 2003/03/31 22:49:35 cheshire
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()
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"
99 //*************************************************************************************************************
101 static mDNS mDNSStorage
; // mDNS core uses this to store its globals
102 static mDNS_PlatformSupport PlatformStorage
; // Stores this platform's globals
104 //*************************************************************************************************************
105 // Proxy Host Registration
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
115 mDNSlocal
void HostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
117 ProxyHost
*f
= (ProxyHost
*)rr
->RecordContext
;
118 if (result
== mStatus_NoError
)
119 debugf("Host name successfully registered: %##s", &rr
->resrec
.name
);
122 debugf("Host name conflict for %##s", &rr
->resrec
.name
);
123 mDNS_Deregister(m
, &f
->RR_A
);
124 mDNS_Deregister(m
, &f
->RR_PTR
);
129 mDNSlocal mStatus
mDNS_RegisterProxyHost(mDNS
*m
, ProxyHost
*p
)
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
);
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");
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
);
143 p
->RR_A
. resrec
.rdata
->u
.ip
= p
->ip
;
144 p
->RR_PTR
.resrec
.rdata
->u
.name
= p
->RR_A
.resrec
.name
;
146 mDNS_Register(m
, &p
->RR_A
);
147 mDNS_Register(m
, &p
->RR_PTR
);
149 debugf("Made Proxy Host Records for %##s", &p
->RR_A
.resrec
.name
);
151 return(mStatus_NoError
);
154 //*************************************************************************************************************
155 // Service Registration
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
)
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;
171 if (result
== mStatus_NoError
)
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
);
178 if (result
== mStatus_NameConflict
)
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
);
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
)
196 unsigned char txtbuffer
[1024], *bptr
= txtbuffer
;
197 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
199 MakeDomainLabelFromLiteralString(&n
, name
);
200 MakeDomainNameFromDNSNameString(&t
, type
);
201 MakeDomainNameFromDNSNameString(&d
, domain
);
204 int len
= strlen(argv
[0]);
205 printf("STR: %s\n", argv
[0]);
207 strcpy((char*)(bptr
+1), argv
[0]);
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
221 ConvertDomainNameToCString(&recordset
->RR_SRV
.resrec
.name
, buffer
);
222 printf("Made Service Records for %s\n", buffer
);
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.
233 mDNSlocal
void NoSuchServiceCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
235 domainname
*proxyhostname
= (domainname
*)rr
->RecordContext
;
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;
244 if (result
== mStatus_NoError
)
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
);
251 if (result
== mStatus_NameConflict
)
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
);
265 mDNSlocal
void RegisterNoSuchService(mDNS
*m
, AuthRecord
*const rr
, domainname
*proxyhostname
,
266 const char name
[], const char type
[], const char domain
[])
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
);
279 //*************************************************************************************************************
282 mDNSexport
int main(int argc
, char **argv
)
287 if (argc
< 3) goto usage
;
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
); }
295 mDNSPosixListenForSignalInEventLoop(SIGINT
);
296 mDNSPosixListenForSignalInEventLoop(SIGTERM
);
298 if (!strcmp(argv
[1], "-"))
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.");
311 ServiceRecordSet proxyservice
;
313 proxyhost
.ip
.NotAnInteger
= inet_addr(argv
[1]);
314 if (proxyhost
.ip
.NotAnInteger
== INADDR_NONE
) // INADDR_NONE is 0xFFFFFFFF
316 struct hostent
*h
= gethostbyname(argv
[1]);
317 if (h
) proxyhost
.ip
.NotAnInteger
= *(long*)h
->h_addr
;
319 if (proxyhost
.ip
.NotAnInteger
== INADDR_NONE
) // INADDR_NONE is 0xFFFFFFFF
321 fprintf(stderr
, "%s is not valid host address\n", argv
[1]);
325 MakeDomainLabelFromLiteralString(&proxyhost
.hostlabel
, argv
[2]);
327 mDNS_RegisterProxyHost(&mDNSStorage
, &proxyhost
);
330 RegisterService(&mDNSStorage
, &proxyservice
, argv
[3], argv
[4], "local.",
331 &proxyhost
.RR_A
.resrec
.name
, atoi(argv
[5]), argc
-6, &argv
[6]);
336 struct timeval timeout
= { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM
337 mDNSBool gotSomething
;
338 mDNSPosixRunEventLoopOnce(&mDNSStorage
, &timeout
, &signals
, &gotSomething
);
340 while ( !( sigismember( &signals
, SIGINT
) || sigismember( &signals
, SIGTERM
)));
342 mDNS_Close(&mDNSStorage
);
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]);