1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
4 *
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
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
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.
16 */
17
18 #include <stdio.h> // For printf()
19 #include <stdlib.h> // For exit() etc.
20 #include <string.h> // For strlen() etc.
21 #include <unistd.h> // For select()
22 #include <signal.h> // For SIGINT, SIGTERM
23 #include <errno.h> // For errno, EINTR
24 #include <netinet/in.h> // For INADDR_NONE
25 #include <arpa/inet.h> // For inet_addr()
26 #include <netdb.h> // For gethostbyname()
27
28 #include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above
29 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
30 #include "ExampleClientApp.h"
31
32 // Compatibility workaround: Solaris 2.5 has no INADDR_NONE
33 #ifndef INADDR_NONE
34 #define INADDR_NONE (mDNSu32)0xffffffff
35 #endif
36
37 //*************************************************************************************************************
38 // Globals
39 static mDNS mDNSStorage; // mDNS core uses this to store its globals
40 static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
41 mDNSexport const char ProgramName[] = "mDNSProxyResponderPosix";
42
43 //*************************************************************************************************************
44 // Proxy Host Registration
45
46 typedef struct
47 {
48 mDNSv4Addr ip;
49 domainlabel hostlabel; // Conforms to standard DNS letter-digit-hyphen host name rules
50 AuthRecord RR_A; // 'A' (address) record for our ".local" name
51 AuthRecord RR_PTR; // PTR (reverse lookup) record
52 } ProxyHost;
53
HostNameCallback(mDNS * const m,AuthRecord * const rr,mStatus result)54 mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
55 {
56 ProxyHost *f = (ProxyHost*)rr->RecordContext;
57 if (result == mStatus_NoError)
58 debugf("Host name successfully registered: %##s", rr->resrec.name->c);
59 else
60 {
61 debugf("Host name conflict for %##s", rr->resrec.name->c);
62 mDNS_Deregister(m, &f->RR_A);
63 mDNS_Deregister(m, &f->RR_PTR);
64 exit(-1);
65 }
66 }
67
mDNS_RegisterProxyHost(mDNS * m,ProxyHost * p)68 mDNSlocal mStatus mDNS_RegisterProxyHost(mDNS *m, ProxyHost *p)
69 {
70 char buffer[32];
71
72 mDNS_SetupResourceRecord(&p->RR_A, mDNSNULL, mDNSInterface_Any, kDNSType_A, 60, kDNSRecordTypeUnique, AuthRecordAny, HostNameCallback, p);
73 mDNS_SetupResourceRecord(&p->RR_PTR, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, AuthRecordAny, HostNameCallback, p);
74
75 p->RR_A.namestorage.c[0] = 0;
76 AppendDomainLabel(&p->RR_A.namestorage, &p->hostlabel);
77 AppendLiteralLabelString(&p->RR_A.namestorage, "local");
78
79 // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
80 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]);
81 MakeDomainNameFromDNSNameString(&p->RR_PTR.namestorage, buffer);
82 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
83
84 p->RR_A. resrec.rdata->u.ipv4 = p->ip;
85 AssignDomainName(&p->RR_PTR.resrec.rdata->u.name, p->RR_A.resrec.name);
86
87 mDNS_Register(m, &p->RR_A);
88 mDNS_Register(m, &p->RR_PTR);
89
90 debugf("Made Proxy Host Records for %##s", p->RR_A.resrec.name->c);
91
92 return(mStatus_NoError);
93 }
94
95 //*************************************************************************************************************
96 // Service Registration
97
98 // This sample ServiceCallback just calls mDNS_RenameAndReregisterService to automatically pick a new
99 // unique name for the service. For a device such as a printer, this may be appropriate.
100 // For a device with a user interface, and a screen, and a keyboard, the appropriate
101 // response may be to prompt the user and ask them to choose a new name for the service.
ServiceCallback(mDNS * const m,ServiceRecordSet * const sr,mStatus result)102 mDNSlocal void ServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result)
103 {
104 switch (result)
105 {
106 case mStatus_NoError: debugf("Callback: %##s Name Registered", sr->RR_SRV.resrec.name->c); break;
107 case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", sr->RR_SRV.resrec.name->c); break;
108 case mStatus_MemFree: debugf("Callback: %##s Memory Free", sr->RR_SRV.resrec.name->c); break;
109 default: debugf("Callback: %##s Unknown Result %ld", sr->RR_SRV.resrec.name->c, result); break;
110 }
111
112 if (result == mStatus_NoError)
113 {
114 char buffer[MAX_ESCAPED_DOMAIN_NAME];
115 ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer);
116 printf("Service %s now registered and active\n", buffer);
117 }
118
119 if (result == mStatus_NameConflict)
120 {
121 char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME];
122 ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer1);
123 mDNS_RenameAndReregisterService(m, sr, mDNSNULL);
124 ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer2);
125 printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2);
126 }
127 }
128
129 // RegisterService() is a simple wrapper function which takes C string
130 // parameters, converts them to domainname parameters, and calls mDNS_RegisterService()
RegisterService(mDNS * m,ServiceRecordSet * recordset,const char name[],const char type[],const char domain[],const domainname * host,mDNSu16 PortAsNumber,int argc,char ** argv)131 mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset,
132 const char name[], const char type[], const char domain[],
133 const domainname *host, mDNSu16 PortAsNumber, int argc, char **argv)
134 {
135 domainlabel n;
136 domainname t, d;
137 unsigned char txtbuffer[1024], *bptr = txtbuffer;
138 char buffer[MAX_ESCAPED_DOMAIN_NAME];
139
140 MakeDomainLabelFromLiteralString(&n, name);
141 MakeDomainNameFromDNSNameString(&t, type);
142 MakeDomainNameFromDNSNameString(&d, domain);
143 while (argc)
144 {
145 int len = strlen(argv[0]);
146 if (len > 255 || bptr + 1 + len >= txtbuffer + sizeof(txtbuffer)) break;
147 printf("STR: %s\n", argv[0]);
148 bptr[0] = len;
149 strcpy((char*)(bptr+1), argv[0]);
150 bptr += 1 + len;
151 argc--;
152 argv++;
153 }
154
155 mDNS_RegisterService(m, recordset,
156 &n, &t, &d, // Name, type, domain
157 host, mDNSOpaque16fromIntVal(PortAsNumber),
158 txtbuffer, bptr-txtbuffer, // TXT data, length
159 mDNSNULL, 0, // Subtypes
160 mDNSInterface_Any, // Interface ID
161 ServiceCallback, mDNSNULL, 0); // Callback, context, flags
162
163 ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer);
164 printf("Made Service Records for %s\n", buffer);
165 }
166
167 //*************************************************************************************************************
168 // Service non-existence assertion
169 // (claiming a service name without actually providing a service at that name, to prevent anyone else using that name)
170 // This is useful to avoid confusion between similar services
171 // e.g. A printer that implements IPP printing service using the name "My Printer", but doesn't implement LPR service,
172 // should also claim the LPR service name "My Printer" to stop a different printer offering LPR service under the same name,
173 // since it would be confusing to users to have two equivalent services with the same name.
174
NoSuchServiceCallback(mDNS * const m,AuthRecord * const rr,mStatus result)175 mDNSlocal void NoSuchServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
176 {
177 const domainname *proxyhostname = (const domainname *)rr->RecordContext;
178 switch (result)
179 {
180 case mStatus_NoError: debugf("Callback: %##s Name Registered", rr->resrec.name->c); break;
181 case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", rr->resrec.name->c); break;
182 case mStatus_MemFree: debugf("Callback: %##s Memory Free", rr->resrec.name->c); break;
183 default: debugf("Callback: %##s Unknown Result %ld", rr->resrec.name->c, result); break;
184 }
185
186 if (result == mStatus_NoError)
187 {
188 char buffer[MAX_ESCAPED_DOMAIN_NAME];
189 ConvertDomainNameToCString(rr->resrec.name, buffer);
190 printf("Non-existence assertion %s now registered and active\n", buffer);
191 }
192
193 if (result == mStatus_NameConflict)
194 {
195 domainlabel n;
196 domainname t, d;
197 char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME];
198 ConvertDomainNameToCString(rr->resrec.name, buffer1);
199 DeconstructServiceName(rr->resrec.name, &n, &t, &d);
200 IncrementLabelSuffix(&n, mDNStrue);
201 mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, mDNSNULL, mDNSfalse);
202 ConvertDomainNameToCString(rr->resrec.name, buffer2);
203 printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2);
204 }
205 }
206
RegisterNoSuchService(mDNS * m,AuthRecord * const rr,domainname * proxyhostname,const char name[],const char type[],const char domain[])207 mDNSlocal void RegisterNoSuchService(mDNS *m, AuthRecord *const rr, domainname *proxyhostname,
208 const char name[], const char type[], const char domain[])
209 {
210 domainlabel n;
211 domainname t, d;
212 char buffer[MAX_ESCAPED_DOMAIN_NAME];
213 MakeDomainLabelFromLiteralString(&n, name);
214 MakeDomainNameFromDNSNameString(&t, type);
215 MakeDomainNameFromDNSNameString(&d, domain);
216 mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, proxyhostname, mDNSfalse);
217 ConvertDomainNameToCString(rr->resrec.name, buffer);
218 printf("Made Non-existence Record for %s\n", buffer);
219 }
220
221 //*************************************************************************************************************
222 // Main
223
main(int argc,char ** argv)224 mDNSexport int main(int argc, char **argv)
225 {
226 mStatus status;
227 sigset_t signals;
228
229 if (argc < 3) goto usage;
230
231 status = mDNS_Init(&mDNSStorage, &PlatformStorage,
232 mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
233 mDNS_Init_DontAdvertiseLocalAddresses,
234 mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
235 if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %d\n", (int)status); return(status); }
236
237 mDNSPosixListenForSignalInEventLoop(SIGINT);
238 mDNSPosixListenForSignalInEventLoop(SIGTERM);
239
240 if (!strcmp(argv[1], "-"))
241 {
242 domainname proxyhostname;
243 AuthRecord proxyrecord;
244 if (argc < 5) goto usage;
245 proxyhostname.c[0] = 0;
246 AppendLiteralLabelString(&proxyhostname, argv[2]);
247 AppendLiteralLabelString(&proxyhostname, "local");
248 RegisterNoSuchService(&mDNSStorage, &proxyrecord, &proxyhostname, argv[3], argv[4], "local.");
249 }
250 else
251 {
252 ProxyHost proxyhost;
253 ServiceRecordSet proxyservice;
254
255 proxyhost.ip.NotAnInteger = inet_addr(argv[1]);
256 if (proxyhost.ip.NotAnInteger == INADDR_NONE) // INADDR_NONE is 0xFFFFFFFF
257 {
258 struct hostent *h = gethostbyname(argv[1]);
259 if (h) proxyhost.ip.NotAnInteger = *(long*)h->h_addr;
260 }
261 if (proxyhost.ip.NotAnInteger == INADDR_NONE) // INADDR_NONE is 0xFFFFFFFF
262 {
263 fprintf(stderr, "%s is not valid host address\n", argv[1]);
264 return(-1);
265 }
266
267 MakeDomainLabelFromLiteralString(&proxyhost.hostlabel, argv[2]);
268
269 mDNS_RegisterProxyHost(&mDNSStorage, &proxyhost);
270
271 if (argc >=6)
272 RegisterService(&mDNSStorage, &proxyservice, argv[3], argv[4], "local.",
273 proxyhost.RR_A.resrec.name, atoi(argv[5]), argc-6, &argv[6]);
274 }
275
276 do
277 {
278 struct timeval timeout = { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM
279 mDNSBool gotSomething;
280 mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething);
281 }
282 while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM)));
283
284 mDNS_Close(&mDNSStorage);
285
286 return(0);
287
288 usage:
289 fprintf(stderr, "%s ip hostlabel [srvname srvtype port txt [txt ...]]\n", argv[0]);
290 fprintf(stderr, "ip Real IP address (or valid host name) of the host where the service actually resides\n");
291 fprintf(stderr, "hostlabel First label of the dot-local host name to create for this host, e.g. \"foo\" for \"foo.local.\"\n");
292 fprintf(stderr, "srvname Descriptive name of service, e.g. \"Stuart's Ink Jet Printer\"\n");
293 fprintf(stderr, "srvtype IANA service type, e.g. \"_ipp._tcp\" or \"_ssh._tcp\", etc.\n");
294 fprintf(stderr, "port Port number where the service resides (1-65535)\n");
295 fprintf(stderr, "txt Additional name/value pairs specified in service definition, e.g. \"pdl=application/postscript\"\n");
296 fprintf(stderr, "e.g. %s 169.254.12.34 thehost (just create a dot-local host name)\n", argv[0]);
297 fprintf(stderr, "or %s 169.254.12.34 thehost \"My Printer\" _printer._tcp. 515 rp=lpt1 pdl=application/postscript\n", argv[0]);
298 fprintf(stderr, "or %s - thehost \"My Printer\" _printer._tcp. (assertion of non-existence)\n", argv[0]);
299 return(-1);
300 }
301