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.44 2007/04/22 20:16:25 cheshire
21 Fix compiler errors (const parameter declarations)
23 Revision 1.43 2007/04/16 20:49:39 cheshire
24 Fix compile errors for mDNSPosix build
26 Revision 1.42 2007/03/06 22:45:53 cheshire
28 <rdar://problem/4138615> argv buffer overflow issues
30 Revision 1.41 2007/02/28 01:51:22 cheshire
31 Added comment about reverse-order IP address
33 Revision 1.40 2007/01/05 04:32:13 cheshire
34 Change "(domainname *)" cast to "(const domainname *)"
36 Revision 1.39 2006/08/14 23:24:46 cheshire
37 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
39 Revision 1.38 2006/06/12 18:22:42 cheshire
40 <rdar://problem/4580067> mDNSResponder building warnings under Red Hat 64-bit (LP64) Linux
42 Revision 1.37 2006/02/23 23:38:43 cheshire
43 <rdar://problem/4427969> On FreeBSD 4 "arpa/inet.h" requires "netinet/in.h" be included first
45 Revision 1.36 2005/08/04 03:12:47 mkrochma
46 <rdar://problem/4199236> Register reverse PTR record using multicast
48 Revision 1.35 2004/12/16 20:17:11 cheshire
49 <rdar://problem/3324626> Cache memory management improvements
51 Revision 1.34 2004/12/01 04:27:28 cheshire
52 <rdar://problem/3872803> Darwin patches for Solaris and Suse
53 Don't use uint32_t, etc. -- they require stdint.h, which doesn't exist on FreeBSD 4.x, Solaris, etc.
55 Revision 1.33 2004/11/30 22:37:01 cheshire
56 Update copyright dates and add "Mode: C; tab-width: 4" headers
58 Revision 1.32 2004/10/26 03:59:41 cheshire
61 Revision 1.31 2004/09/17 01:08:53 cheshire
62 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
63 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
64 declared in that file are ONLY appropriate to single-address-space embedded applications.
65 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
67 Revision 1.30 2004/09/17 00:31:52 cheshire
68 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
70 Revision 1.29 2004/09/16 01:58:22 cheshire
73 Revision 1.28 2004/06/25 00:26:27 rpantos
74 Changes to fix the Posix build on Solaris.
76 Revision 1.27 2004/03/12 08:03:14 cheshire
79 Revision 1.26 2004/01/25 00:00:39 cheshire
80 Change to use mDNSOpaque16fromIntVal() instead of shifting and masking
82 Revision 1.25 2003/12/08 20:47:02 rpantos
83 Add support for mDNSResponder on Linux.
85 Revision 1.24 2003/11/14 21:27:09 cheshire
86 <rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
87 Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers.
89 Revision 1.23 2003/10/30 19:39:28 cheshire
90 Fix warnings on certain compilers
92 Revision 1.22 2003/08/14 02:19:55 cheshire
93 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
95 Revision 1.21 2003/08/12 19:56:26 cheshire
98 Revision 1.20 2003/07/23 00:00:04 cheshire
101 Revision 1.19 2003/07/15 01:55:16 cheshire
102 <rdar://problem/3315777> Need to implement service registration with subtypes
104 Revision 1.18 2003/07/02 21:19:58 cheshire
105 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
107 Revision 1.17 2003/05/26 03:21:29 cheshire
108 Tidy up address structure naming:
109 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
110 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
111 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
113 Revision 1.16 2003/05/26 03:01:28 cheshire
114 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
116 Revision 1.15 2003/05/06 00:00:50 cheshire
117 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
119 Revision 1.14 2003/04/25 01:45:57 cheshire
120 <rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
122 Revision 1.13 2003/04/18 22:46:12 cheshire
123 Fix mistake in 1.8 -- INADDR_NONE is 0xFFFFFFFF, not 0
125 Revision 1.12 2003/04/16 02:11:07 cheshire
126 Fixed mDNS_RegisterNoSuchService non-existence function so that it works again
128 Revision 1.11 2003/03/31 22:49:35 cheshire
133 #include <stdio.h> // For printf()
134 #include <stdlib.h> // For exit() etc.
135 #include <string.h> // For strlen() etc.
136 #include <unistd.h> // For select()
137 #include <signal.h> // For SIGINT, SIGTERM
138 #include <errno.h> // For errno, EINTR
139 #include <netinet/in.h> // For INADDR_NONE
140 #include <arpa/inet.h> // For inet_addr()
141 #include <netdb.h> // For gethostbyname()
143 #include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above
144 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
145 #include "ExampleClientApp.h"
147 // Compatibility workaround: Solaris 2.5 has no INADDR_NONE
149 #define INADDR_NONE (mDNSu32)0xffffffff
152 //*************************************************************************************************************
154 static mDNS mDNSStorage
; // mDNS core uses this to store its globals
155 static mDNS_PlatformSupport PlatformStorage
; // Stores this platform's globals
156 mDNSexport
const char ProgramName
[] = "mDNSProxyResponderPosix";
158 //*************************************************************************************************************
159 // Proxy Host Registration
164 domainlabel hostlabel
; // Conforms to standard DNS letter-digit-hyphen host name rules
165 AuthRecord RR_A
; // 'A' (address) record for our ".local" name
166 AuthRecord RR_PTR
; // PTR (reverse lookup) record
169 mDNSlocal
void HostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
171 ProxyHost
*f
= (ProxyHost
*)rr
->RecordContext
;
172 if (result
== mStatus_NoError
)
173 debugf("Host name successfully registered: %##s", rr
->resrec
.name
->c
);
176 debugf("Host name conflict for %##s", rr
->resrec
.name
->c
);
177 mDNS_Deregister(m
, &f
->RR_A
);
178 mDNS_Deregister(m
, &f
->RR_PTR
);
183 mDNSlocal mStatus
mDNS_RegisterProxyHost(mDNS
*m
, ProxyHost
*p
)
187 mDNS_SetupResourceRecord(&p
->RR_A
, mDNSNULL
, mDNSInterface_Any
, kDNSType_A
, 60, kDNSRecordTypeUnique
, HostNameCallback
, p
);
188 mDNS_SetupResourceRecord(&p
->RR_PTR
, mDNSNULL
, mDNSInterface_Any
, kDNSType_PTR
, 60, kDNSRecordTypeKnownUnique
, HostNameCallback
, p
);
190 p
->RR_A
.namestorage
.c
[0] = 0;
191 AppendDomainLabel(&p
->RR_A
.namestorage
, &p
->hostlabel
);
192 AppendLiteralLabelString(&p
->RR_A
.namestorage
, "local");
194 // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
195 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]);
196 MakeDomainNameFromDNSNameString(&p
->RR_PTR
.namestorage
, buffer
);
197 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
199 p
->RR_A
. resrec
.rdata
->u
.ipv4
= p
->ip
;
200 AssignDomainName(&p
->RR_PTR
.resrec
.rdata
->u
.name
, p
->RR_A
.resrec
.name
);
202 mDNS_Register(m
, &p
->RR_A
);
203 mDNS_Register(m
, &p
->RR_PTR
);
205 debugf("Made Proxy Host Records for %##s", p
->RR_A
.resrec
.name
->c
);
207 return(mStatus_NoError
);
210 //*************************************************************************************************************
211 // Service Registration
213 // This sample ServiceCallback just calls mDNS_RenameAndReregisterService to automatically pick a new
214 // unique name for the service. For a device such as a printer, this may be appropriate.
215 // For a device with a user interface, and a screen, and a keyboard, the appropriate
216 // response may be to prompt the user and ask them to choose a new name for the service.
217 mDNSlocal
void ServiceCallback(mDNS
*const m
, ServiceRecordSet
*const sr
, mStatus result
)
221 case mStatus_NoError
: debugf("Callback: %##s Name Registered", sr
->RR_SRV
.resrec
.name
->c
); break;
222 case mStatus_NameConflict
: debugf("Callback: %##s Name Conflict", sr
->RR_SRV
.resrec
.name
->c
); break;
223 case mStatus_MemFree
: debugf("Callback: %##s Memory Free", sr
->RR_SRV
.resrec
.name
->c
); break;
224 default: debugf("Callback: %##s Unknown Result %ld", sr
->RR_SRV
.resrec
.name
->c
, result
); break;
227 if (result
== mStatus_NoError
)
229 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
230 ConvertDomainNameToCString(sr
->RR_SRV
.resrec
.name
, buffer
);
231 printf("Service %s now registered and active\n", buffer
);
234 if (result
== mStatus_NameConflict
)
236 char buffer1
[MAX_ESCAPED_DOMAIN_NAME
], buffer2
[MAX_ESCAPED_DOMAIN_NAME
];
237 ConvertDomainNameToCString(sr
->RR_SRV
.resrec
.name
, buffer1
);
238 mDNS_RenameAndReregisterService(m
, sr
, mDNSNULL
);
239 ConvertDomainNameToCString(sr
->RR_SRV
.resrec
.name
, buffer2
);
240 printf("Name Conflict! %s renamed as %s\n", buffer1
, buffer2
);
244 // RegisterService() is a simple wrapper function which takes C string
245 // parameters, converts them to domainname parameters, and calls mDNS_RegisterService()
246 mDNSlocal
void RegisterService(mDNS
*m
, ServiceRecordSet
*recordset
,
247 const char name
[], const char type
[], const char domain
[],
248 const domainname
*host
, mDNSu16 PortAsNumber
, int argc
, char **argv
)
252 unsigned char txtbuffer
[1024], *bptr
= txtbuffer
;
253 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
255 MakeDomainLabelFromLiteralString(&n
, name
);
256 MakeDomainNameFromDNSNameString(&t
, type
);
257 MakeDomainNameFromDNSNameString(&d
, domain
);
260 int len
= strlen(argv
[0]);
261 if (len
> 255 || bptr
+ 1 + len
>= txtbuffer
+ sizeof(txtbuffer
)) break;
262 printf("STR: %s\n", argv
[0]);
264 strcpy((char*)(bptr
+1), argv
[0]);
270 mDNS_RegisterService(m
, recordset
,
271 &n
, &t
, &d
, // Name, type, domain
272 host
, mDNSOpaque16fromIntVal(PortAsNumber
),
273 txtbuffer
, bptr
-txtbuffer
, // TXT data, length
274 mDNSNULL
, 0, // Subtypes
275 mDNSInterface_Any
, // Interface ID
276 ServiceCallback
, mDNSNULL
); // Callback and context
278 ConvertDomainNameToCString(recordset
->RR_SRV
.resrec
.name
, buffer
);
279 printf("Made Service Records for %s\n", buffer
);
282 //*************************************************************************************************************
283 // Service non-existence assertion
284 // (claiming a service name without actually providing a service at that name, to prevent anyone else using that name)
285 // This is useful to avoid confusion between similar services
286 // e.g. A printer that implements IPP printing service using the name "My Printer", but doesn't implement LPR service,
287 // should also claim the LPR service name "My Printer" to stop a different printer offering LPR service under the same name,
288 // since it would be confusing to users to have two equivalent services with the same name.
290 mDNSlocal
void NoSuchServiceCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
292 const domainname
*proxyhostname
= (const domainname
*)rr
->RecordContext
;
295 case mStatus_NoError
: debugf("Callback: %##s Name Registered", rr
->resrec
.name
->c
); break;
296 case mStatus_NameConflict
: debugf("Callback: %##s Name Conflict", rr
->resrec
.name
->c
); break;
297 case mStatus_MemFree
: debugf("Callback: %##s Memory Free", rr
->resrec
.name
->c
); break;
298 default: debugf("Callback: %##s Unknown Result %ld", rr
->resrec
.name
->c
, result
); break;
301 if (result
== mStatus_NoError
)
303 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
304 ConvertDomainNameToCString(rr
->resrec
.name
, buffer
);
305 printf("Non-existence assertion %s now registered and active\n", buffer
);
308 if (result
== mStatus_NameConflict
)
312 char buffer1
[MAX_ESCAPED_DOMAIN_NAME
], buffer2
[MAX_ESCAPED_DOMAIN_NAME
];
313 ConvertDomainNameToCString(rr
->resrec
.name
, buffer1
);
314 DeconstructServiceName(rr
->resrec
.name
, &n
, &t
, &d
);
315 IncrementLabelSuffix(&n
, mDNStrue
);
316 mDNS_RegisterNoSuchService(m
, rr
, &n
, &t
, &d
, proxyhostname
, mDNSInterface_Any
, NoSuchServiceCallback
, mDNSNULL
);
317 ConvertDomainNameToCString(rr
->resrec
.name
, buffer2
);
318 printf("Name Conflict! %s renamed as %s\n", buffer1
, buffer2
);
322 mDNSlocal
void RegisterNoSuchService(mDNS
*m
, AuthRecord
*const rr
, domainname
*proxyhostname
,
323 const char name
[], const char type
[], const char domain
[])
327 char buffer
[MAX_ESCAPED_DOMAIN_NAME
];
328 MakeDomainLabelFromLiteralString(&n
, name
);
329 MakeDomainNameFromDNSNameString(&t
, type
);
330 MakeDomainNameFromDNSNameString(&d
, domain
);
331 mDNS_RegisterNoSuchService(m
, rr
, &n
, &t
, &d
, proxyhostname
, mDNSInterface_Any
, NoSuchServiceCallback
, proxyhostname
);
332 ConvertDomainNameToCString(rr
->resrec
.name
, buffer
);
333 printf("Made Non-existence Record for %s\n", buffer
);
336 //*************************************************************************************************************
339 mDNSexport
int main(int argc
, char **argv
)
344 if (argc
< 3) goto usage
;
346 status
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
347 mDNS_Init_NoCache
, mDNS_Init_ZeroCacheSize
,
348 mDNS_Init_DontAdvertiseLocalAddresses
,
349 mDNS_Init_NoInitCallback
, mDNS_Init_NoInitCallbackContext
);
350 if (status
) { fprintf(stderr
, "Daemon start: mDNS_Init failed %d\n", (int)status
); return(status
); }
352 mDNSPosixListenForSignalInEventLoop(SIGINT
);
353 mDNSPosixListenForSignalInEventLoop(SIGTERM
);
355 if (!strcmp(argv
[1], "-"))
357 domainname proxyhostname
;
358 AuthRecord proxyrecord
;
359 if (argc
< 5) goto usage
;
360 proxyhostname
.c
[0] = 0;
361 AppendLiteralLabelString(&proxyhostname
, argv
[2]);
362 AppendLiteralLabelString(&proxyhostname
, "local");
363 RegisterNoSuchService(&mDNSStorage
, &proxyrecord
, &proxyhostname
, argv
[3], argv
[4], "local.");
368 ServiceRecordSet proxyservice
;
370 proxyhost
.ip
.NotAnInteger
= inet_addr(argv
[1]);
371 if (proxyhost
.ip
.NotAnInteger
== INADDR_NONE
) // INADDR_NONE is 0xFFFFFFFF
373 struct hostent
*h
= gethostbyname(argv
[1]);
374 if (h
) proxyhost
.ip
.NotAnInteger
= *(long*)h
->h_addr
;
376 if (proxyhost
.ip
.NotAnInteger
== INADDR_NONE
) // INADDR_NONE is 0xFFFFFFFF
378 fprintf(stderr
, "%s is not valid host address\n", argv
[1]);
382 MakeDomainLabelFromLiteralString(&proxyhost
.hostlabel
, argv
[2]);
384 mDNS_RegisterProxyHost(&mDNSStorage
, &proxyhost
);
387 RegisterService(&mDNSStorage
, &proxyservice
, argv
[3], argv
[4], "local.",
388 proxyhost
.RR_A
.resrec
.name
, atoi(argv
[5]), argc
-6, &argv
[6]);
393 struct timeval timeout
= { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM
394 mDNSBool gotSomething
;
395 mDNSPosixRunEventLoopOnce(&mDNSStorage
, &timeout
, &signals
, &gotSomething
);
397 while ( !( sigismember( &signals
, SIGINT
) || sigismember( &signals
, SIGTERM
)));
399 mDNS_Close(&mDNSStorage
);
404 fprintf(stderr
, "%s ip hostlabel [srvname srvtype port txt [txt ...]]\n", argv
[0]);
405 fprintf(stderr
, "ip Real IP address (or valid host name) of the host where the service actually resides\n");
406 fprintf(stderr
, "hostlabel First label of the dot-local host name to create for this host, e.g. \"foo\" for \"foo.local.\"\n");
407 fprintf(stderr
, "srvname Descriptive name of service, e.g. \"Stuart's Ink Jet Printer\"\n");
408 fprintf(stderr
, "srvtype IANA service type, e.g. \"_ipp._tcp\" or \"_ssh._tcp\", etc.\n");
409 fprintf(stderr
, "port Port number where the service resides (1-65535)\n");
410 fprintf(stderr
, "txt Additional name/value pairs specified in service definition, e.g. \"pdl=application/postscript\"\n");
411 fprintf(stderr
, "e.g. %s 169.254.12.34 thehost (just create a dot-local host name)\n", argv
[0]);
412 fprintf(stderr
, "or %s 169.254.12.34 thehost \"My Printer\" _printer._tcp. 515 rp=lpt1 pdl=application/postscript\n", argv
[0]);
413 fprintf(stderr
, "or %s - thehost \"My Printer\" _printer._tcp. (assertion of non-existence)\n", argv
[0]);