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.39 2006/08/14 23:24:46 cheshire
21 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
23 Revision 1.38 2006/06/12 18:22:42 cheshire
24 <rdar://problem/4580067> mDNSResponder building warnings under Red Hat 64-bit (LP64) Linux
26 Revision 1.37 2006/02/23 23:38:43 cheshire
27 <rdar://problem/4427969> On FreeBSD 4 "arpa/inet.h" requires "netinet/in.h" be included first
29 Revision 1.36 2005/08/04 03:12:47 mkrochma
30 <rdar://problem/4199236> Register reverse PTR record using multicast
32 Revision 1.35 2004/12/16 20:17:11 cheshire
33 <rdar://problem/3324626> Cache memory management improvements
35 Revision 1.34 2004/12/01 04:27:28 cheshire
36 <rdar://problem/3872803> Darwin patches for Solaris and Suse
37 Don't use uint32_t, etc. -- they require stdint.h, which doesn't exist on FreeBSD 4.x, Solaris, etc.
39 Revision 1.33 2004/11/30 22:37:01 cheshire
40 Update copyright dates and add "Mode: C; tab-width: 4" headers
42 Revision 1.32 2004/10/26 03:59:41 cheshire
45 Revision 1.31 2004/09/17 01:08:53 cheshire
46 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
47 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
48 declared in that file are ONLY appropriate to single-address-space embedded applications.
49 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
51 Revision 1.30 2004/09/17 00:31:52 cheshire
52 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
54 Revision 1.29 2004/09/16 01:58:22 cheshire
57 Revision 1.28 2004/06/25 00:26:27 rpantos
58 Changes to fix the Posix build on Solaris.
60 Revision 1.27 2004/03/12 08:03:14 cheshire
63 Revision 1.26 2004/01/25 00:00:39 cheshire
64 Change to use mDNSOpaque16fromIntVal() instead of shifting and masking
66 Revision 1.25 2003/12/08 20:47:02 rpantos
67 Add support for mDNSResponder on Linux.
69 Revision 1.24 2003/11/14 21:27:09 cheshire
70 <rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
71 Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers.
73 Revision 1.23 2003/10/30 19:39:28 cheshire
74 Fix warnings on certain compilers
76 Revision 1.22 2003/08/14 02:19:55 cheshire
77 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
79 Revision 1.21 2003/08/12 19:56:26 cheshire
82 Revision 1.20 2003/07/23 00:00:04 cheshire
85 Revision 1.19 2003/07/15 01:55:16 cheshire
86 <rdar://problem/3315777> Need to implement service registration with subtypes
88 Revision 1.18 2003/07/02 21:19:58 cheshire
89 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
91 Revision 1.17 2003/05/26 03:21:29 cheshire
92 Tidy up address structure naming:
93 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
94 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
95 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
97 Revision 1.16 2003/05/26 03:01:28 cheshire
98 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
100 Revision 1.15 2003/05/06 00:00:50 cheshire
101 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
103 Revision 1.14 2003/04/25 01:45:57 cheshire
104 <rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
106 Revision 1.13 2003/04/18 22:46:12 cheshire
107 Fix mistake in 1.8 -- INADDR_NONE is 0xFFFFFFFF, not 0
109 Revision 1.12 2003/04/16 02:11:07 cheshire
110 Fixed mDNS_RegisterNoSuchService non-existence function so that it works again
112 Revision 1.11 2003/03/31 22:49:35 cheshire
117 #include <stdio.h> // For printf()
118 #include <stdlib.h> // For exit() etc.
119 #include <string.h> // For strlen() etc.
120 #include <unistd.h> // For select()
121 #include <signal.h> // For SIGINT, SIGTERM
122 #include <errno.h> // For errno, EINTR
123 #include <netinet/in.h> // For INADDR_NONE
124 #include <arpa/inet.h> // For inet_addr()
125 #include <netdb.h> // For gethostbyname()
127 #include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above
128 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
129 #include "ExampleClientApp.h"
131 // Compatibility workaround: Solaris 2.5 has no INADDR_NONE
133 #define INADDR_NONE (mDNSu32)0xffffffff
136 //*************************************************************************************************************
138 static mDNS mDNSStorage
; // mDNS core uses this to store its globals
139 static mDNS_PlatformSupport PlatformStorage
; // Stores this platform's globals
141 //*************************************************************************************************************
142 // Proxy Host Registration
147 domainlabel hostlabel
; // Conforms to standard DNS letter-digit-hyphen host name rules
148 AuthRecord RR_A
; // 'A' (address) record for our ".local" name
149 AuthRecord RR_PTR
; // PTR (reverse lookup) record
152 mDNSlocal
void HostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
154 ProxyHost
*f
= (ProxyHost
*)rr
->RecordContext
;
155 if (result
== mStatus_NoError
)
156 debugf("Host name successfully registered: %##s", rr
->resrec
.name
->c
);
159 debugf("Host name conflict for %##s", rr
->resrec
.name
->c
);
160 mDNS_Deregister(m
, &f
->RR_A
);
161 mDNS_Deregister(m
, &f
->RR_PTR
);
166 mDNSlocal mStatus
mDNS_RegisterProxyHost(mDNS
*m
, ProxyHost
*p
)
170 mDNS_SetupResourceRecord(&p
->RR_A
, mDNSNULL
, mDNSInterface_Any
, kDNSType_A
, 60, kDNSRecordTypeUnique
, HostNameCallback
, p
);
171 mDNS_SetupResourceRecord(&p
->RR_PTR
, mDNSNULL
, mDNSInterface_Any
, kDNSType_PTR
, 60, kDNSRecordTypeKnownUnique
, HostNameCallback
, p
);
173 p
->RR_A
.resrec
.name
->c
[0] = 0;
174 AppendDomainLabel(p
->RR_A
.resrec
.name
, &p
->hostlabel
);
175 AppendLiteralLabelString(p
->RR_A
.resrec
.name
, "local");
177 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]);
178 MakeDomainNameFromDNSNameString(p
->RR_PTR
.resrec
.name
, buffer
);
179 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
181 p
->RR_A
. resrec
.rdata
->u
.ipv4
= p
->ip
;
182 AssignDomainName(&p
->RR_PTR
.resrec
.rdata
->u
.name
, p
->RR_A
.resrec
.name
);
184 mDNS_Register(m
, &p
->RR_A
);
185 mDNS_Register(m
, &p
->RR_PTR
);
187 debugf("Made Proxy Host Records for %##s", p
->RR_A
.resrec
.name
->c
);
189 return(mStatus_NoError
);
192 //*************************************************************************************************************
193 // Service Registration
195 // This sample ServiceCallback just calls mDNS_RenameAndReregisterService to automatically pick a new
196 // unique name for the service. For a device such as a printer, this may be appropriate.
197 // For a device with a user interface, and a screen, and a keyboard, the appropriate
198 // response may be to prompt the user and ask them to choose a new name for the service.
199 mDNSlocal
void ServiceCallback(mDNS
*const m
, ServiceRecordSet
*const sr
, mStatus result
)
203 case mStatus_NoError
: debugf("Callback: %##s Name Registered", sr
->RR_SRV
.resrec
.name
->c
); break;
204 case mStatus_NameConflict
: debugf("Callback: %##s Name Conflict", sr
->RR_SRV
.resrec
.name
->c
); break;
205 case mStatus_MemFree
: debugf("Callback: %##s Memory Free", sr
->RR_SRV
.resrec
.name
->c
); break;
206 default: debugf("Callback: %##s Unknown Result %ld", sr
->RR_SRV
.resrec
.name
->c
, result
); break;
209 if (result
== mStatus_NoError
)
211 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
212 ConvertDomainNameToCString(sr
->RR_SRV
.resrec
.name
, buffer
);
213 printf("Service %s now registered and active\n", buffer
);
216 if (result
== mStatus_NameConflict
)
218 char buffer1
[MAX_ESCAPED_DOMAIN_NAME
], buffer2
[MAX_ESCAPED_DOMAIN_NAME
];
219 ConvertDomainNameToCString(sr
->RR_SRV
.resrec
.name
, buffer1
);
220 mDNS_RenameAndReregisterService(m
, sr
, mDNSNULL
);
221 ConvertDomainNameToCString(sr
->RR_SRV
.resrec
.name
, buffer2
);
222 printf("Name Conflict! %s renamed as %s\n", buffer1
, buffer2
);
226 // RegisterService() is a simple wrapper function which takes C string
227 // parameters, converts them to domainname parameters, and calls mDNS_RegisterService()
228 mDNSlocal
void RegisterService(mDNS
*m
, ServiceRecordSet
*recordset
,
229 const char name
[], const char type
[], const char domain
[],
230 const domainname
*host
, mDNSu16 PortAsNumber
, int argc
, char **argv
)
234 unsigned char txtbuffer
[1024], *bptr
= txtbuffer
;
235 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
237 MakeDomainLabelFromLiteralString(&n
, name
);
238 MakeDomainNameFromDNSNameString(&t
, type
);
239 MakeDomainNameFromDNSNameString(&d
, domain
);
242 int len
= strlen(argv
[0]);
243 printf("STR: %s\n", argv
[0]);
245 strcpy((char*)(bptr
+1), argv
[0]);
251 mDNS_RegisterService(m
, recordset
,
252 &n
, &t
, &d
, // Name, type, domain
253 host
, mDNSOpaque16fromIntVal(PortAsNumber
),
254 txtbuffer
, bptr
-txtbuffer
, // TXT data, length
255 mDNSNULL
, 0, // Subtypes
256 mDNSInterface_Any
, // Interface ID
257 ServiceCallback
, mDNSNULL
); // Callback and context
259 ConvertDomainNameToCString(recordset
->RR_SRV
.resrec
.name
, buffer
);
260 printf("Made Service Records for %s\n", buffer
);
263 //*************************************************************************************************************
264 // Service non-existence assertion
265 // (claiming a service name without actually providing a service at that name, to prevent anyone else using that name)
266 // This is useful to avoid confusion between similar services
267 // e.g. A printer that implements IPP printing service using the name "My Printer", but doesn't implement LPR service,
268 // should also claim the LPR service name "My Printer" to stop a different printer offering LPR service under the same name,
269 // since it would be confusing to users to have two equivalent services with the same name.
271 mDNSlocal
void NoSuchServiceCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
273 domainname
*proxyhostname
= (domainname
*)rr
->RecordContext
;
276 case mStatus_NoError
: debugf("Callback: %##s Name Registered", rr
->resrec
.name
->c
); break;
277 case mStatus_NameConflict
: debugf("Callback: %##s Name Conflict", rr
->resrec
.name
->c
); break;
278 case mStatus_MemFree
: debugf("Callback: %##s Memory Free", rr
->resrec
.name
->c
); break;
279 default: debugf("Callback: %##s Unknown Result %ld", rr
->resrec
.name
->c
, result
); break;
282 if (result
== mStatus_NoError
)
284 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
285 ConvertDomainNameToCString(rr
->resrec
.name
, buffer
);
286 printf("Non-existence assertion %s now registered and active\n", buffer
);
289 if (result
== mStatus_NameConflict
)
293 char buffer1
[MAX_ESCAPED_DOMAIN_NAME
], buffer2
[MAX_ESCAPED_DOMAIN_NAME
];
294 ConvertDomainNameToCString(rr
->resrec
.name
, buffer1
);
295 DeconstructServiceName(rr
->resrec
.name
, &n
, &t
, &d
);
296 IncrementLabelSuffix(&n
, mDNStrue
);
297 mDNS_RegisterNoSuchService(m
, rr
, &n
, &t
, &d
, proxyhostname
, mDNSInterface_Any
, NoSuchServiceCallback
, mDNSNULL
);
298 ConvertDomainNameToCString(rr
->resrec
.name
, buffer2
);
299 printf("Name Conflict! %s renamed as %s\n", buffer1
, buffer2
);
303 mDNSlocal
void RegisterNoSuchService(mDNS
*m
, AuthRecord
*const rr
, domainname
*proxyhostname
,
304 const char name
[], const char type
[], const char domain
[])
308 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
309 MakeDomainLabelFromLiteralString(&n
, name
);
310 MakeDomainNameFromDNSNameString(&t
, type
);
311 MakeDomainNameFromDNSNameString(&d
, domain
);
312 mDNS_RegisterNoSuchService(m
, rr
, &n
, &t
, &d
, proxyhostname
, mDNSInterface_Any
, NoSuchServiceCallback
, proxyhostname
);
313 ConvertDomainNameToCString(rr
->resrec
.name
, buffer
);
314 printf("Made Non-existence Record for %s\n", buffer
);
317 //*************************************************************************************************************
320 mDNSexport
int main(int argc
, char **argv
)
325 if (argc
< 3) goto usage
;
327 status
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
328 mDNS_Init_NoCache
, mDNS_Init_ZeroCacheSize
,
329 mDNS_Init_DontAdvertiseLocalAddresses
,
330 mDNS_Init_NoInitCallback
, mDNS_Init_NoInitCallbackContext
);
331 if (status
) { fprintf(stderr
, "Daemon start: mDNS_Init failed %d\n", (int)status
); return(status
); }
333 mDNSPosixListenForSignalInEventLoop(SIGINT
);
334 mDNSPosixListenForSignalInEventLoop(SIGTERM
);
336 if (!strcmp(argv
[1], "-"))
338 domainname proxyhostname
;
339 AuthRecord proxyrecord
;
340 if (argc
< 5) goto usage
;
341 proxyhostname
.c
[0] = 0;
342 AppendLiteralLabelString(&proxyhostname
, argv
[2]);
343 AppendLiteralLabelString(&proxyhostname
, "local");
344 RegisterNoSuchService(&mDNSStorage
, &proxyrecord
, &proxyhostname
, argv
[3], argv
[4], "local.");
349 ServiceRecordSet proxyservice
;
351 proxyhost
.ip
.NotAnInteger
= inet_addr(argv
[1]);
352 if (proxyhost
.ip
.NotAnInteger
== INADDR_NONE
) // INADDR_NONE is 0xFFFFFFFF
354 struct hostent
*h
= gethostbyname(argv
[1]);
355 if (h
) proxyhost
.ip
.NotAnInteger
= *(long*)h
->h_addr
;
357 if (proxyhost
.ip
.NotAnInteger
== INADDR_NONE
) // INADDR_NONE is 0xFFFFFFFF
359 fprintf(stderr
, "%s is not valid host address\n", argv
[1]);
363 MakeDomainLabelFromLiteralString(&proxyhost
.hostlabel
, argv
[2]);
365 mDNS_RegisterProxyHost(&mDNSStorage
, &proxyhost
);
368 RegisterService(&mDNSStorage
, &proxyservice
, argv
[3], argv
[4], "local.",
369 proxyhost
.RR_A
.resrec
.name
, atoi(argv
[5]), argc
-6, &argv
[6]);
374 struct timeval timeout
= { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM
375 mDNSBool gotSomething
;
376 mDNSPosixRunEventLoopOnce(&mDNSStorage
, &timeout
, &signals
, &gotSomething
);
378 while ( !( sigismember( &signals
, SIGINT
) || sigismember( &signals
, SIGTERM
)));
380 mDNS_Close(&mDNSStorage
);
385 fprintf(stderr
, "%s ip hostlabel [srvname srvtype port txt [txt ...]]\n", argv
[0]);
386 fprintf(stderr
, "ip Real IP address (or valid host name) of the host where the service actually resides\n");
387 fprintf(stderr
, "hostlabel First label of the dot-local host name to create for this host, e.g. \"foo\" for \"foo.local.\"\n");
388 fprintf(stderr
, "srvname Descriptive name of service, e.g. \"Stuart's Ink Jet Printer\"\n");
389 fprintf(stderr
, "srvtype IANA service type, e.g. \"_ipp._tcp\" or \"_ssh._tcp\", etc.\n");
390 fprintf(stderr
, "port Port number where the service resides (1-65535)\n");
391 fprintf(stderr
, "txt Additional name/value pairs specified in service definition, e.g. \"pdl=application/postscript\"\n");
392 fprintf(stderr
, "e.g. %s 169.254.12.34 thehost (just create a dot-local host name)\n", argv
[0]);
393 fprintf(stderr
, "or %s 169.254.12.34 thehost \"My Printer\" _printer._tcp. 515 rp=lpt1 pdl=application/postscript\n", argv
[0]);
394 fprintf(stderr
, "or %s - thehost \"My Printer\" _printer._tcp. (assertion of non-existence)\n", argv
[0]);