]>
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 | |
c9b9ae52 | 8 | * |
67c8f8a1 | 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
c9b9ae52 | 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 | ||
7f0064bd 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 | |
c9d2d929 | 24 | #include <netinet/in.h> // For INADDR_NONE |
67c8f8a1 | 25 | #include <arpa/inet.h> // For inet_addr() |
7f0064bd A |
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 A |
32 | // Compatibility workaround: Solaris 2.5 has no INADDR_NONE |
33 | #ifndef INADDR_NONE | |
34 | #define INADDR_NONE (mDNSu32)0xffffffff | |
35 | #endif | |
36 | ||
c9b9ae52 A |
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 | |
67c8f8a1 | 41 | mDNSexport const char ProgramName[] = "mDNSProxyResponderPosix"; |
c9b9ae52 A |
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 | ||
54 | mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) | |
55 | { | |
56 | ProxyHost *f = (ProxyHost*)rr->RecordContext; | |
57 | if (result == mStatus_NoError) | |
283ee3ff | 58 | debugf("Host name successfully registered: %##s", rr->resrec.name->c); |
c9b9ae52 A |
59 | else |
60 | { | |
283ee3ff | 61 | debugf("Host name conflict for %##s", rr->resrec.name->c); |
c9b9ae52 A |
62 | mDNS_Deregister(m, &f->RR_A); |
63 | mDNS_Deregister(m, &f->RR_PTR); | |
64 | exit(-1); | |
65 | } | |
66 | } | |
67 | ||
68 | mDNSlocal mStatus mDNS_RegisterProxyHost(mDNS *m, ProxyHost *p) | |
69 | { | |
70 | char buffer[32]; | |
71 | ||
294beb6e A |
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); | |
c9b9ae52 | 74 | |
67c8f8a1 A |
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 | |
67c8f8a1 | 79 | // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code |
c9b9ae52 | 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]); |
67c8f8a1 | 81 | MakeDomainNameFromDNSNameString(&p->RR_PTR.namestorage, buffer); |
4aea607d | 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 | |
7f0064bd | 84 | p->RR_A. resrec.rdata->u.ipv4 = p->ip; |
283ee3ff | 85 | AssignDomainName(&p->RR_PTR.resrec.rdata->u.name, p->RR_A.resrec.name); |
c9b9ae52 A |
86 | |
87 | mDNS_Register(m, &p->RR_A); | |
88 | mDNS_Register(m, &p->RR_PTR); | |
89 | ||
283ee3ff | 90 | debugf("Made Proxy Host Records for %##s", p->RR_A.resrec.name->c); |
c9b9ae52 A |
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. | |
102 | mDNSlocal void ServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) | |
103 | { | |
104 | switch (result) | |
105 | { | |
283ee3ff A |
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; | |
c9b9ae52 A |
110 | } |
111 | ||
112 | if (result == mStatus_NoError) | |
113 | { | |
716635cc | 114 | char buffer[MAX_ESCAPED_DOMAIN_NAME]; |
283ee3ff | 115 | ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer); |
c9b9ae52 A |
116 | printf("Service %s now registered and active\n", buffer); |
117 | } | |
118 | ||
119 | if (result == mStatus_NameConflict) | |
120 | { | |
716635cc | 121 | char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME]; |
283ee3ff | 122 | ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer1); |
c9b9ae52 | 123 | mDNS_RenameAndReregisterService(m, sr, mDNSNULL); |
283ee3ff | 124 | ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer2); |
c9b9ae52 A |
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() | |
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; | |
716635cc A |
137 | unsigned char txtbuffer[1024], *bptr = txtbuffer; |
138 | char buffer[MAX_ESCAPED_DOMAIN_NAME]; | |
c9b9ae52 A |
139 | |
140 | MakeDomainLabelFromLiteralString(&n, name); | |
141 | MakeDomainNameFromDNSNameString(&t, type); | |
142 | MakeDomainNameFromDNSNameString(&d, domain); | |
c9b9ae52 A |
143 | while (argc) |
144 | { | |
145 | int len = strlen(argv[0]); | |
67c8f8a1 | 146 | if (len > 255 || bptr + 1 + len >= txtbuffer + sizeof(txtbuffer)) break; |
c9b9ae52 A |
147 | printf("STR: %s\n", argv[0]); |
148 | bptr[0] = len; | |
716635cc | 149 | strcpy((char*)(bptr+1), argv[0]); |
c9b9ae52 A |
150 | bptr += 1 + len; |
151 | argc--; | |
152 | argv++; | |
153 | } | |
154 | ||
155 | mDNS_RegisterService(m, recordset, | |
156 | &n, &t, &d, // Name, type, domain | |
8e92c31c | 157 | host, mDNSOpaque16fromIntVal(PortAsNumber), |
716635cc | 158 | txtbuffer, bptr-txtbuffer, // TXT data, length |
c9b9ae52 | 159 | mDNSNULL, 0, // Subtypes |
8e92c31c | 160 | mDNSInterface_Any, // Interface ID |
294beb6e | 161 | ServiceCallback, mDNSNULL, 0); // Callback, context, flags |
c9b9ae52 | 162 | |
283ee3ff | 163 | ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer); |
c9b9ae52 A |
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 | ||
175 | mDNSlocal void NoSuchServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result) | |
176 | { | |
67c8f8a1 | 177 | const domainname *proxyhostname = (const domainname *)rr->RecordContext; |
c9b9ae52 A |
178 | switch (result) |
179 | { | |
283ee3ff A |
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; | |
c9b9ae52 A |
184 | } |
185 | ||
186 | if (result == mStatus_NoError) | |
187 | { | |
716635cc | 188 | char buffer[MAX_ESCAPED_DOMAIN_NAME]; |
283ee3ff | 189 | ConvertDomainNameToCString(rr->resrec.name, buffer); |
c9b9ae52 A |
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; | |
716635cc | 197 | char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME]; |
283ee3ff A |
198 | ConvertDomainNameToCString(rr->resrec.name, buffer1); |
199 | DeconstructServiceName(rr->resrec.name, &n, &t, &d); | |
c9b9ae52 | 200 | IncrementLabelSuffix(&n, mDNStrue); |
294beb6e | 201 | mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, mDNSNULL, mDNSfalse); |
283ee3ff | 202 | ConvertDomainNameToCString(rr->resrec.name, buffer2); |
c9b9ae52 A |
203 | printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2); |
204 | } | |
205 | } | |
206 | ||
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; | |
716635cc | 212 | char buffer[MAX_ESCAPED_DOMAIN_NAME]; |
c9b9ae52 A |
213 | MakeDomainLabelFromLiteralString(&n, name); |
214 | MakeDomainNameFromDNSNameString(&t, type); | |
215 | MakeDomainNameFromDNSNameString(&d, domain); | |
294beb6e | 216 | mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, proxyhostname, mDNSfalse); |
283ee3ff | 217 | ConvertDomainNameToCString(rr->resrec.name, buffer); |
c9b9ae52 A |
218 | printf("Made Non-existence Record for %s\n", buffer); |
219 | } | |
220 | ||
221 | //************************************************************************************************************* | |
222 | // Main | |
223 | ||
224 | mDNSexport int main(int argc, char **argv) | |
225 | { | |
8e92c31c A |
226 | mStatus status; |
227 | sigset_t signals; | |
c9b9ae52 A |
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); | |
67c8f8a1 | 235 | if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %d\n", (int)status); return(status); } |
c9b9ae52 | 236 | |
8e92c31c A |
237 | mDNSPosixListenForSignalInEventLoop(SIGINT); |
238 | mDNSPosixListenForSignalInEventLoop(SIGTERM); | |
239 | ||
c9b9ae52 A |
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."); | |
c9b9ae52 A |
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.", | |
283ee3ff | 273 | proxyhost.RR_A.resrec.name, atoi(argv[5]), argc-6, &argv[6]); |
8e92c31c | 274 | } |
c9b9ae52 | 275 | |
8e92c31c A |
276 | do |
277 | { | |
278 | struct timeval timeout = { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM | |
279 | mDNSBool gotSomething; | |
280 | mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething); | |
c9b9ae52 | 281 | } |
8e92c31c A |
282 | while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM))); |
283 | ||
284 | mDNS_Close(&mDNSStorage); | |
c9b9ae52 A |
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 | } |