]>
Commit | Line | Data |
---|---|---|
7f0064bd A |
1 | /* -*- Mode: C; tab-width: 4 -*- |
2 | * | |
3 | * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. | |
c9b9ae52 | 4 | * |
67c8f8a1 A |
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 | |
83fb1e36 | 8 | * |
67c8f8a1 | 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
83fb1e36 | 10 | * |
67c8f8a1 A |
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 | |
c9b9ae52 | 15 | * limitations under the License. |
c9b9ae52 A |
16 | */ |
17 | ||
83fb1e36 A |
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 | |
c9b9ae52 A |
30 | #include "ExampleClientApp.h" |
31 | ||
7f0064bd | 32 | // Compatibility workaround: Solaris 2.5 has no INADDR_NONE |
83fb1e36 A |
33 | #ifndef INADDR_NONE |
34 | #define INADDR_NONE (mDNSu32)0xffffffff | |
7f0064bd A |
35 | #endif |
36 | ||
c9b9ae52 A |
37 | //************************************************************************************************************* |
38 | // Globals | |
f0cc3e7b | 39 | mDNS mDNSStorage; // mDNS core uses this to store its globals |
c9b9ae52 | 40 | static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals |
67c8f8a1 | 41 | mDNSexport const char ProgramName[] = "mDNSProxyResponderPosix"; |
c9b9ae52 A |
42 | |
43 | //************************************************************************************************************* | |
44 | // Proxy Host Registration | |
45 | ||
46 | typedef struct | |
83fb1e36 A |
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; | |
c9b9ae52 A |
53 | |
54 | mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) | |
83fb1e36 A |
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 | } | |
c9b9ae52 A |
67 | |
68 | mDNSlocal mStatus mDNS_RegisterProxyHost(mDNS *m, ProxyHost *p) | |
83fb1e36 A |
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"); | |
c9b9ae52 | 78 | |
83fb1e36 A |
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 | |
c9b9ae52 | 83 | |
83fb1e36 A |
84 | p->RR_A.resrec.rdata->u.ipv4 = p->ip; |
85 | AssignDomainName(&p->RR_PTR.resrec.rdata->u.name, p->RR_A.resrec.name); | |
c9b9ae52 | 86 | |
83fb1e36 A |
87 | mDNS_Register(m, &p->RR_A); |
88 | mDNS_Register(m, &p->RR_PTR); | |
c9b9ae52 | 89 | |
83fb1e36 | 90 | debugf("Made Proxy Host Records for %##s", p->RR_A.resrec.name->c); |
c9b9ae52 | 91 | |
83fb1e36 A |
92 | return(mStatus_NoError); |
93 | } | |
c9b9ae52 A |
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. | |
102 | mDNSlocal void ServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) | |
83fb1e36 A |
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 | } | |
c9b9ae52 A |
128 | |
129 | // RegisterService() is a simple wrapper function which takes C string | |
130 | // parameters, converts them to domainname parameters, and calls mDNS_RegisterService() | |
131 | mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, | |
83fb1e36 A |
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), | |
2682e09e | 158 | mDNSNULL, txtbuffer, bptr-txtbuffer, // TXT data, length |
83fb1e36 A |
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 | } | |
c9b9ae52 A |
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 | ||
175 | mDNSlocal void NoSuchServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result) | |
83fb1e36 A |
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, 0); | |
202 | ConvertDomainNameToCString(rr->resrec.name, buffer2); | |
203 | printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2); | |
204 | } | |
205 | } | |
c9b9ae52 A |
206 | |
207 | mDNSlocal void RegisterNoSuchService(mDNS *m, AuthRecord *const rr, domainname *proxyhostname, | |
83fb1e36 A |
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, 0); | |
217 | ConvertDomainNameToCString(rr->resrec.name, buffer); | |
218 | printf("Made Non-existence Record for %s\n", buffer); | |
219 | } | |
c9b9ae52 A |
220 | |
221 | //************************************************************************************************************* | |
222 | // Main | |
223 | ||
224 | mDNSexport int main(int argc, char **argv) | |
83fb1e36 A |
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 | { | |
12c5fa7a | 278 | struct timeval timeout = { FutureTime, 0 }; // wait until SIGINT or SIGTERM |
83fb1e36 A |
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); | |
c9b9ae52 A |
287 | |
288 | usage: | |
83fb1e36 A |
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 | } |