]>
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 | Change History (most recent first): | |
18 | ||
19 | $Log: Responder.c,v $ | |
32bb7e43 A |
20 | Revision 1.36 2009/01/15 03:39:08 mkrochma |
21 | Fix warning about ignoring return value of daemon | |
22 | ||
23 | Revision 1.35 2009/01/13 05:31:34 mkrochma | |
24 | <rdar://problem/6491367> Replace bzero, bcopy with mDNSPlatformMemZero, mDNSPlatformMemCopy, memset, memcpy | |
25 | ||
26 | Revision 1.34 2009/01/11 03:20:06 mkrochma | |
27 | <rdar://problem/5797526> Fixes from Igor Seleznev to get mdnsd working on Solaris | |
28 | ||
67c8f8a1 A |
29 | Revision 1.33 2007/04/16 20:49:39 cheshire |
30 | Fix compile errors for mDNSPosix build | |
31 | ||
32 | Revision 1.32 2006/08/14 23:24:46 cheshire | |
33 | Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 | |
34 | ||
35 | Revision 1.31 2006/06/12 18:22:42 cheshire | |
36 | <rdar://problem/4580067> mDNSResponder building warnings under Red Hat 64-bit (LP64) Linux | |
37 | ||
4aea607d A |
38 | Revision 1.30 2005/10/26 22:21:16 cheshire |
39 | <rdar://problem/4149841> Potential buffer overflow in mDNSResponderPosix | |
40 | ||
7cb34e5c A |
41 | Revision 1.29 2005/03/04 21:35:33 cheshire |
42 | <rdar://problem/4037201> Services.txt file not parsed properly when it contains more than one service | |
43 | ||
283ee3ff A |
44 | Revision 1.28 2005/01/11 01:55:26 ksekar |
45 | Fix compile errors in Posix debug build | |
46 | ||
7f0064bd A |
47 | Revision 1.27 2004/12/01 04:28:43 cheshire |
48 | <rdar://problem/3872803> Darwin patches for Solaris and Suse | |
49 | Use version of daemon() provided in mDNSUNP.c instead of local copy | |
50 | ||
51 | Revision 1.26 2004/11/30 22:37:01 cheshire | |
52 | Update copyright dates and add "Mode: C; tab-width: 4" headers | |
53 | ||
54 | Revision 1.25 2004/11/11 02:00:51 cheshire | |
55 | Minor fixes to getopt, error message | |
56 | ||
57 | Revision 1.24 2004/11/09 19:32:10 rpantos | |
58 | Suggestion from Ademar de Souza Reis Jr. to allow comments in services file | |
59 | ||
60 | Revision 1.23 2004/09/17 01:08:54 cheshire | |
61 | Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h | |
62 | The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces | |
63 | declared in that file are ONLY appropriate to single-address-space embedded applications. | |
64 | For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. | |
65 | ||
66 | Revision 1.22 2004/09/16 01:58:22 cheshire | |
67 | Fix compiler warnings | |
68 | ||
69 | Revision 1.21 2004/06/15 03:48:07 cheshire | |
70 | Update mDNSResponderPosix to take multiple name=val arguments in a sane way | |
71 | ||
8e92c31c A |
72 | Revision 1.20 2004/05/18 23:51:26 cheshire |
73 | Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers | |
74 | ||
75 | Revision 1.19 2004/03/12 08:03:14 cheshire | |
76 | Update comments | |
77 | ||
78 | Revision 1.18 2004/01/25 00:00:55 cheshire | |
79 | Change to use mDNSOpaque16fromIntVal() instead of shifting and masking | |
80 | ||
81 | Revision 1.17 2003/12/11 19:11:55 cheshire | |
82 | Fix compiler warning | |
73792575 | 83 | |
c9b9ae52 A |
84 | Revision 1.16 2003/08/14 02:19:55 cheshire |
85 | <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord | |
86 | ||
87 | Revision 1.15 2003/08/12 19:56:26 cheshire | |
88 | Update to APSL 2.0 | |
89 | ||
90 | Revision 1.14 2003/08/06 18:20:51 cheshire | |
91 | Makefile cleanup | |
92 | ||
93 | Revision 1.13 2003/07/23 00:00:04 cheshire | |
94 | Add comments | |
95 | ||
96 | Revision 1.12 2003/07/15 01:55:16 cheshire | |
97 | <rdar://problem/3315777> Need to implement service registration with subtypes | |
98 | ||
99 | Revision 1.11 2003/07/14 18:11:54 cheshire | |
100 | Fix stricter compiler warnings | |
101 | ||
102 | Revision 1.10 2003/07/10 20:27:31 cheshire | |
103 | <rdar://problem/3318717> mDNSResponder Posix version is missing a 'b' in the getopt option string | |
104 | ||
105 | Revision 1.9 2003/07/02 21:19:59 cheshire | |
106 | <rdar://problem/3313413> Update copyright notices, etc., in source code comments | |
107 | ||
108 | Revision 1.8 2003/06/18 05:48:41 cheshire | |
109 | Fix warnings | |
110 | ||
111 | Revision 1.7 2003/05/06 00:00:50 cheshire | |
112 | <rdar://problem/3248914> Rationalize naming of domainname manipulation functions | |
113 | ||
114 | Revision 1.6 2003/03/08 00:35:56 cheshire | |
115 | Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt") | |
116 | ||
117 | Revision 1.5 2003/02/20 06:48:36 cheshire | |
8e92c31c | 118 | <rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations |
c9b9ae52 A |
119 | Reviewed by: Josh Graessley, Bob Bradley |
120 | ||
121 | Revision 1.4 2003/01/28 03:07:46 cheshire | |
122 | Add extra parameter to mDNS_RenameAndReregisterService(), | |
123 | and add support for specifying a domain other than dot-local. | |
124 | ||
125 | Revision 1.3 2002/09/21 20:44:53 zarzycki | |
126 | Added APSL info | |
127 | ||
128 | Revision 1.2 2002/09/19 04:20:44 cheshire | |
129 | Remove high-ascii characters that confuse some systems | |
130 | ||
131 | Revision 1.1 2002/09/17 06:24:35 cheshire | |
132 | First checkin | |
133 | ||
134 | */ | |
135 | ||
7f0064bd | 136 | #include "mDNSEmbeddedAPI.h"// Defines the interface to the client layer above |
c9b9ae52 | 137 | #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform |
32bb7e43 | 138 | #include "mDNSUNP.h" // For daemon() |
c9b9ae52 A |
139 | |
140 | #include <assert.h> | |
141 | #include <stdio.h> // For printf() | |
142 | #include <stdlib.h> // For exit() etc. | |
143 | #include <string.h> // For strlen() etc. | |
144 | #include <unistd.h> // For select() | |
145 | #include <errno.h> // For errno, EINTR | |
146 | #include <signal.h> | |
147 | #include <fcntl.h> | |
148 | ||
149 | #if COMPILER_LIKES_PRAGMA_MARK | |
150 | #pragma mark ***** Globals | |
151 | #endif | |
152 | ||
153 | static mDNS mDNSStorage; // mDNS core uses this to store its globals | |
154 | static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals | |
155 | ||
67c8f8a1 A |
156 | mDNSexport const char ProgramName[] = "mDNSResponderPosix"; |
157 | ||
158 | static const char *gProgramName = ProgramName; | |
c9b9ae52 A |
159 | |
160 | #if COMPILER_LIKES_PRAGMA_MARK | |
161 | #pragma mark ***** Signals | |
162 | #endif | |
163 | ||
164 | static volatile mDNSBool gReceivedSigUsr1; | |
165 | static volatile mDNSBool gReceivedSigHup; | |
166 | static volatile mDNSBool gStopNow; | |
167 | ||
168 | // We support 4 signals. | |
169 | // | |
170 | // o SIGUSR1 toggles verbose mode on and off in debug builds | |
171 | // o SIGHUP triggers the program to re-read its preferences. | |
172 | // o SIGINT causes an orderly shutdown of the program. | |
173 | // o SIGQUIT causes a somewhat orderly shutdown (direct but dangerous) | |
174 | // o SIGKILL kills us dead (easy to implement :-) | |
175 | // | |
176 | // There are fatal race conditions in our signal handling, but there's not much | |
177 | // we can do about them while remaining within the Posix space. Specifically, | |
178 | // if a signal arrives after we test the globals its sets but before we call | |
179 | // select, the signal will be dropped. The user will have to send the signal | |
180 | // again. Unfortunately, Posix does not have a "sigselect" to atomically | |
181 | // modify the signal mask and start a select. | |
182 | ||
183 | static void HandleSigUsr1(int sigraised) | |
184 | // If we get a SIGUSR1 we toggle the state of the | |
185 | // verbose mode. | |
186 | { | |
187 | assert(sigraised == SIGUSR1); | |
188 | gReceivedSigUsr1 = mDNStrue; | |
189 | } | |
190 | ||
191 | static void HandleSigHup(int sigraised) | |
192 | // A handler for SIGHUP that causes us to break out of the | |
193 | // main event loop when the user kill 1's us. This has the | |
194 | // effect of triggered the main loop to deregister the | |
195 | // current services and re-read the preferences. | |
196 | { | |
197 | assert(sigraised == SIGHUP); | |
198 | gReceivedSigHup = mDNStrue; | |
199 | } | |
200 | ||
201 | static void HandleSigInt(int sigraised) | |
202 | // A handler for SIGINT that causes us to break out of the | |
203 | // main event loop when the user types ^C. This has the | |
204 | // effect of quitting the program. | |
205 | { | |
206 | assert(sigraised == SIGINT); | |
207 | ||
208 | if (gMDNSPlatformPosixVerboseLevel > 0) { | |
209 | fprintf(stderr, "\nSIGINT\n"); | |
210 | } | |
211 | gStopNow = mDNStrue; | |
212 | } | |
213 | ||
214 | static void HandleSigQuit(int sigraised) | |
215 | // If we get a SIGQUIT the user is desperate and we | |
216 | // just call mDNS_Close directly. This is definitely | |
217 | // not safe (because it could reenter mDNS), but | |
218 | // we presume that the user has already tried the safe | |
219 | // alternatives. | |
220 | { | |
221 | assert(sigraised == SIGQUIT); | |
222 | ||
223 | if (gMDNSPlatformPosixVerboseLevel > 0) { | |
224 | fprintf(stderr, "\nSIGQUIT\n"); | |
225 | } | |
226 | mDNS_Close(&mDNSStorage); | |
227 | exit(0); | |
228 | } | |
229 | ||
230 | #if COMPILER_LIKES_PRAGMA_MARK | |
231 | #pragma mark ***** Parameter Checking | |
232 | #endif | |
233 | ||
7cb34e5c A |
234 | static mDNSBool CheckThatRichTextNameIsUsable(const char *richTextName, mDNSBool printExplanation) |
235 | // Checks that richTextName is reasonable | |
c9b9ae52 A |
236 | // label and, if it isn't and printExplanation is true, prints |
237 | // an explanation of why not. | |
238 | { | |
7cb34e5c A |
239 | mDNSBool result = mDNStrue; |
240 | if (result && strlen(richTextName) > 63) { | |
c9b9ae52 A |
241 | if (printExplanation) { |
242 | fprintf(stderr, | |
7cb34e5c | 243 | "%s: Service name is too long (must be 63 characters or less)\n", |
c9b9ae52 A |
244 | gProgramName); |
245 | } | |
246 | result = mDNSfalse; | |
247 | } | |
7cb34e5c | 248 | if (result && richTextName[0] == 0) { |
c9b9ae52 | 249 | if (printExplanation) { |
7cb34e5c | 250 | fprintf(stderr, "%s: Service name can't be empty\n", gProgramName); |
c9b9ae52 A |
251 | } |
252 | result = mDNSfalse; | |
253 | } | |
c9b9ae52 A |
254 | return result; |
255 | } | |
256 | ||
257 | static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation) | |
258 | // Checks that serviceType is a reasonable service type | |
259 | // label and, if it isn't and printExplanation is true, prints | |
260 | // an explanation of why not. | |
261 | { | |
262 | mDNSBool result; | |
263 | ||
264 | result = mDNStrue; | |
265 | if (result && strlen(serviceType) > 63) { | |
266 | if (printExplanation) { | |
267 | fprintf(stderr, | |
268 | "%s: Service type is too long (must be 63 characters or less)\n", | |
269 | gProgramName); | |
270 | } | |
271 | result = mDNSfalse; | |
272 | } | |
273 | if (result && serviceType[0] == 0) { | |
274 | if (printExplanation) { | |
275 | fprintf(stderr, | |
276 | "%s: Service type can't be empty\n", | |
277 | gProgramName); | |
278 | } | |
279 | result = mDNSfalse; | |
280 | } | |
281 | return result; | |
282 | } | |
283 | ||
c9b9ae52 A |
284 | static mDNSBool CheckThatPortNumberIsUsable(long portNumber, mDNSBool printExplanation) |
285 | // Checks that portNumber is a reasonable port number | |
286 | // and, if it isn't and printExplanation is true, prints | |
287 | // an explanation of why not. | |
288 | { | |
289 | mDNSBool result; | |
290 | ||
291 | result = mDNStrue; | |
292 | if (result && (portNumber <= 0 || portNumber > 65535)) { | |
293 | if (printExplanation) { | |
294 | fprintf(stderr, | |
295 | "%s: Port number specified by -p must be in range 1..65535\n", | |
296 | gProgramName); | |
297 | } | |
298 | result = mDNSfalse; | |
299 | } | |
300 | return result; | |
301 | } | |
302 | ||
303 | #if COMPILER_LIKES_PRAGMA_MARK | |
304 | #pragma mark ***** Command Line Arguments | |
305 | #endif | |
306 | ||
307 | static const char kDefaultPIDFile[] = "/var/run/mDNSResponder.pid"; | |
308 | static const char kDefaultServiceType[] = "_afpovertcp._tcp."; | |
309 | static const char kDefaultServiceDomain[] = "local."; | |
310 | enum { | |
311 | kDefaultPortNumber = 548 | |
312 | }; | |
313 | ||
314 | static void PrintUsage() | |
315 | { | |
316 | fprintf(stderr, | |
7f0064bd | 317 | "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-p port] [-f file] [-b] [-P pidfile] [-x name=val ...]\n", |
c9b9ae52 A |
318 | gProgramName); |
319 | fprintf(stderr, " -v verbose mode, level is a number from 0 to 2\n"); | |
320 | fprintf(stderr, " 0 = no debugging info (default)\n"); | |
321 | fprintf(stderr, " 1 = standard debugging info\n"); | |
322 | fprintf(stderr, " 2 = intense debugging info\n"); | |
323 | fprintf(stderr, " can be cycled kill -USR1\n"); | |
8e92c31c | 324 | fprintf(stderr, " -r also bind to port 53 (port 5353 is always bound)\n"); |
7cb34e5c | 325 | fprintf(stderr, " -n uses 'name' as the service name (required)\n"); |
c9b9ae52 A |
326 | fprintf(stderr, " -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType); |
327 | fprintf(stderr, " -d uses 'domain' as the service domain (default is '%s')\n", kDefaultServiceDomain); | |
c9b9ae52 A |
328 | fprintf(stderr, " -p uses 'port' as the port number (default is '%d')\n", kDefaultPortNumber); |
329 | fprintf(stderr, " -f reads a service list from 'file'\n"); | |
330 | fprintf(stderr, " -b forces daemon (background) mode\n"); | |
331 | fprintf(stderr, " -P uses 'pidfile' as the PID file\n"); | |
332 | fprintf(stderr, " (default is '%s')\n", kDefaultPIDFile); | |
333 | fprintf(stderr, " only meaningful if -b also specified\n"); | |
7f0064bd A |
334 | fprintf(stderr, " -x stores name=val in TXT record (default is empty).\n"); |
335 | fprintf(stderr, " MUST be the last command-line argument;\n"); | |
336 | fprintf(stderr, " all subsequent arguments after -x are treated as name=val pairs.\n"); | |
c9b9ae52 A |
337 | } |
338 | ||
8e92c31c | 339 | static mDNSBool gAvoidPort53 = mDNStrue; |
7cb34e5c | 340 | static const char *gServiceName = ""; |
c9b9ae52 A |
341 | static const char *gServiceType = kDefaultServiceType; |
342 | static const char *gServiceDomain = kDefaultServiceDomain; | |
343 | static mDNSu8 gServiceText[sizeof(RDataBody)]; | |
344 | static mDNSu16 gServiceTextLen = 0; | |
345 | static int gPortNumber = kDefaultPortNumber; | |
346 | static const char *gServiceFile = ""; | |
347 | static mDNSBool gDaemon = mDNSfalse; | |
348 | static const char *gPIDFile = kDefaultPIDFile; | |
349 | ||
350 | static void ParseArguments(int argc, char **argv) | |
351 | // Parses our command line arguments into the global variables | |
352 | // listed above. | |
353 | { | |
354 | int ch; | |
355 | ||
356 | // Set gProgramName to the last path component of argv[0] | |
357 | ||
358 | gProgramName = strrchr(argv[0], '/'); | |
359 | if (gProgramName == NULL) { | |
360 | gProgramName = argv[0]; | |
361 | } else { | |
362 | gProgramName += 1; | |
363 | } | |
364 | ||
365 | // Parse command line options using getopt. | |
366 | ||
367 | do { | |
7f0064bd | 368 | ch = getopt(argc, argv, "v:rn:t:d:p:f:dP:bx"); |
c9b9ae52 A |
369 | if (ch != -1) { |
370 | switch (ch) { | |
371 | case 'v': | |
372 | gMDNSPlatformPosixVerboseLevel = atoi(optarg); | |
373 | if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) { | |
374 | fprintf(stderr, | |
375 | "%s: Verbose mode must be in the range 0..2\n", | |
376 | gProgramName); | |
377 | exit(1); | |
378 | } | |
379 | break; | |
8e92c31c A |
380 | case 'r': |
381 | gAvoidPort53 = mDNSfalse; | |
382 | break; | |
c9b9ae52 | 383 | case 'n': |
7cb34e5c A |
384 | gServiceName = optarg; |
385 | if ( ! CheckThatRichTextNameIsUsable(gServiceName, mDNStrue) ) { | |
c9b9ae52 A |
386 | exit(1); |
387 | } | |
388 | break; | |
389 | case 't': | |
390 | gServiceType = optarg; | |
391 | if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) { | |
392 | exit(1); | |
393 | } | |
394 | break; | |
395 | case 'd': | |
396 | gServiceDomain = optarg; | |
397 | break; | |
c9b9ae52 A |
398 | case 'p': |
399 | gPortNumber = atol(optarg); | |
400 | if ( ! CheckThatPortNumberIsUsable(gPortNumber, mDNStrue) ) { | |
401 | exit(1); | |
402 | } | |
403 | break; | |
404 | case 'f': | |
405 | gServiceFile = optarg; | |
406 | break; | |
407 | case 'b': | |
408 | gDaemon = mDNStrue; | |
409 | break; | |
410 | case 'P': | |
411 | gPIDFile = optarg; | |
412 | break; | |
7f0064bd A |
413 | case 'x': |
414 | while (optind < argc) | |
415 | { | |
416 | gServiceText[gServiceTextLen] = strlen(argv[optind]); | |
32bb7e43 | 417 | mDNSPlatformMemCopy(gServiceText+gServiceTextLen+1, argv[optind], gServiceText[gServiceTextLen]); |
7f0064bd A |
418 | gServiceTextLen += 1 + gServiceText[gServiceTextLen]; |
419 | optind++; | |
420 | } | |
421 | ch = -1; | |
422 | break; | |
c9b9ae52 A |
423 | case '?': |
424 | default: | |
425 | PrintUsage(); | |
426 | exit(1); | |
427 | break; | |
428 | } | |
429 | } | |
430 | } while (ch != -1); | |
431 | ||
432 | // Check for any left over command line arguments. | |
433 | ||
434 | if (optind != argc) { | |
435 | PrintUsage(); | |
436 | fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]); | |
437 | exit(1); | |
438 | } | |
439 | ||
440 | // Check for inconsistency between the arguments. | |
441 | ||
7cb34e5c | 442 | if ( (gServiceName[0] == 0) && (gServiceFile[0] == 0) ) { |
c9b9ae52 | 443 | PrintUsage(); |
7cb34e5c | 444 | fprintf(stderr, "%s: You must specify a service name to register (-n) or a service file (-f).\n", gProgramName); |
c9b9ae52 A |
445 | exit(1); |
446 | } | |
447 | } | |
448 | ||
449 | #if COMPILER_LIKES_PRAGMA_MARK | |
450 | #pragma mark ***** Registration | |
451 | #endif | |
452 | ||
453 | typedef struct PosixService PosixService; | |
454 | ||
455 | struct PosixService { | |
456 | ServiceRecordSet coreServ; | |
457 | PosixService *next; | |
458 | int serviceID; | |
459 | }; | |
460 | ||
461 | static PosixService *gServiceList = NULL; | |
462 | ||
463 | static void RegistrationCallback(mDNS *const m, ServiceRecordSet *const thisRegistration, mStatus status) | |
464 | // mDNS core calls this routine to tell us about the status of | |
465 | // our registration. The appropriate action to take depends | |
466 | // entirely on the value of status. | |
467 | { | |
468 | switch (status) { | |
469 | ||
470 | case mStatus_NoError: | |
283ee3ff | 471 | debugf("Callback: %##s Name Registered", thisRegistration->RR_SRV.resrec.name->c); |
c9b9ae52 A |
472 | // Do nothing; our name was successfully registered. We may |
473 | // get more call backs in the future. | |
474 | break; | |
475 | ||
476 | case mStatus_NameConflict: | |
283ee3ff | 477 | debugf("Callback: %##s Name Conflict", thisRegistration->RR_SRV.resrec.name->c); |
c9b9ae52 A |
478 | |
479 | // In the event of a conflict, this sample RegistrationCallback | |
480 | // just calls mDNS_RenameAndReregisterService to automatically | |
481 | // pick a new unique name for the service. For a device such as a | |
482 | // printer, this may be appropriate. For a device with a user | |
483 | // interface, and a screen, and a keyboard, the appropriate response | |
484 | // may be to prompt the user and ask them to choose a new name for | |
485 | // the service. | |
486 | // | |
487 | // Also, what do we do if mDNS_RenameAndReregisterService returns an | |
488 | // error. Right now I have no place to send that error to. | |
489 | ||
490 | status = mDNS_RenameAndReregisterService(m, thisRegistration, mDNSNULL); | |
491 | assert(status == mStatus_NoError); | |
492 | break; | |
493 | ||
494 | case mStatus_MemFree: | |
283ee3ff | 495 | debugf("Callback: %##s Memory Free", thisRegistration->RR_SRV.resrec.name->c); |
c9b9ae52 A |
496 | |
497 | // When debugging is enabled, make sure that thisRegistration | |
498 | // is not on our gServiceList. | |
499 | ||
500 | #if !defined(NDEBUG) | |
501 | { | |
502 | PosixService *cursor; | |
503 | ||
504 | cursor = gServiceList; | |
505 | while (cursor != NULL) { | |
506 | assert(&cursor->coreServ != thisRegistration); | |
507 | cursor = cursor->next; | |
508 | } | |
509 | } | |
510 | #endif | |
511 | free(thisRegistration); | |
512 | break; | |
513 | ||
514 | default: | |
283ee3ff | 515 | debugf("Callback: %##s Unknown Status %ld", thisRegistration->RR_SRV.resrec.name->c, status); |
c9b9ae52 A |
516 | break; |
517 | } | |
518 | } | |
519 | ||
520 | static int gServiceID = 0; | |
521 | ||
7cb34e5c | 522 | static mStatus RegisterOneService(const char * richTextName, |
c9b9ae52 A |
523 | const char * serviceType, |
524 | const char * serviceDomain, | |
525 | const mDNSu8 text[], | |
526 | mDNSu16 textLen, | |
527 | long portNumber) | |
528 | { | |
529 | mStatus status; | |
530 | PosixService * thisServ; | |
c9b9ae52 A |
531 | domainlabel name; |
532 | domainname type; | |
533 | domainname domain; | |
534 | ||
535 | status = mStatus_NoError; | |
536 | thisServ = (PosixService *) malloc(sizeof(*thisServ)); | |
537 | if (thisServ == NULL) { | |
538 | status = mStatus_NoMemoryErr; | |
539 | } | |
540 | if (status == mStatus_NoError) { | |
7cb34e5c | 541 | MakeDomainLabelFromLiteralString(&name, richTextName); |
c9b9ae52 A |
542 | MakeDomainNameFromDNSNameString(&type, serviceType); |
543 | MakeDomainNameFromDNSNameString(&domain, serviceDomain); | |
c9b9ae52 A |
544 | status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ, |
545 | &name, &type, &domain, // Name, type, domain | |
8e92c31c | 546 | NULL, mDNSOpaque16fromIntVal(portNumber), |
c9b9ae52 A |
547 | text, textLen, // TXT data, length |
548 | NULL, 0, // Subtypes | |
8e92c31c | 549 | mDNSInterface_Any, // Interface ID |
c9b9ae52 A |
550 | RegistrationCallback, thisServ); // Callback and context |
551 | } | |
552 | if (status == mStatus_NoError) { | |
553 | thisServ->serviceID = gServiceID; | |
554 | gServiceID += 1; | |
555 | ||
556 | thisServ->next = gServiceList; | |
557 | gServiceList = thisServ; | |
558 | ||
559 | if (gMDNSPlatformPosixVerboseLevel > 0) { | |
560 | fprintf(stderr, | |
561 | "%s: Registered service %d, name '%s', type '%s', port %ld\n", | |
562 | gProgramName, | |
563 | thisServ->serviceID, | |
7cb34e5c | 564 | richTextName, |
c9b9ae52 A |
565 | serviceType, |
566 | portNumber); | |
567 | } | |
568 | } else { | |
569 | if (thisServ != NULL) { | |
570 | free(thisServ); | |
571 | } | |
572 | } | |
573 | return status; | |
574 | } | |
575 | ||
576 | static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp) | |
7f0064bd A |
577 | // Read a line, skipping over any blank lines or lines starting with '#' |
578 | { | |
579 | mDNSBool good, skip; | |
580 | do { | |
581 | good = (fgets(buf, bufSize, fp) != NULL); | |
7cb34e5c | 582 | skip = (good && (buf[0] == '#')); |
7f0064bd A |
583 | } while (good && skip); |
584 | if (good) | |
8e92c31c | 585 | { |
7f0064bd A |
586 | int len = strlen( buf); |
587 | if ( buf[len - 1] == '\r' || buf[len - 1] == '\n') | |
588 | buf[len - 1] = '\0'; | |
8e92c31c | 589 | } |
7f0064bd A |
590 | return good; |
591 | } | |
c9b9ae52 A |
592 | |
593 | static mStatus RegisterServicesInFile(const char *filePath) | |
594 | { | |
7f0064bd A |
595 | mStatus status = mStatus_NoError; |
596 | FILE * fp = fopen(filePath, "r"); | |
c9b9ae52 | 597 | int junk; |
c9b9ae52 | 598 | |
c9b9ae52 A |
599 | if (fp == NULL) { |
600 | status = mStatus_UnknownErr; | |
601 | } | |
602 | if (status == mStatus_NoError) { | |
7f0064bd | 603 | mDNSBool good = mDNStrue; |
c9b9ae52 | 604 | do { |
7cb34e5c | 605 | int ch; |
7f0064bd A |
606 | char name[256]; |
607 | char type[256]; | |
608 | const char *dom = kDefaultServiceDomain; | |
609 | char rawText[1024]; | |
610 | mDNSu8 text[sizeof(RDataBody)]; | |
4aea607d | 611 | unsigned int textLen = 0; |
7f0064bd | 612 | char port[256]; |
7cb34e5c A |
613 | |
614 | // Skip over any blank lines. | |
615 | do ch = fgetc(fp); while ( ch == '\n' || ch == '\r' ); | |
616 | if (ch != EOF) good = (ungetc(ch, fp) == ch); | |
c9b9ae52 A |
617 | |
618 | // Read three lines, check them for validity, and register the service. | |
7f0064bd A |
619 | good = ReadALine(name, sizeof(name), fp); |
620 | if (good) { | |
621 | good = ReadALine(type, sizeof(type), fp); | |
622 | } | |
623 | if (good) { | |
624 | char *p = type; | |
625 | while (*p && *p != ' ') p++; | |
626 | if (*p) { | |
627 | *p = 0; | |
628 | dom = p+1; | |
629 | } | |
630 | } | |
631 | if (good) { | |
632 | good = ReadALine(port, sizeof(port), fp); | |
633 | } | |
634 | if (good) { | |
7cb34e5c | 635 | good = CheckThatRichTextNameIsUsable(name, mDNSfalse) |
7f0064bd A |
636 | && CheckThatServiceTypeIsUsable(type, mDNSfalse) |
637 | && CheckThatPortNumberIsUsable(atol(port), mDNSfalse); | |
638 | } | |
639 | if (good) { | |
640 | while (1) { | |
7cb34e5c | 641 | int len; |
7f0064bd | 642 | if (!ReadALine(rawText, sizeof(rawText), fp)) break; |
7cb34e5c A |
643 | len = strlen(rawText); |
644 | if (len <= 255) | |
645 | { | |
4aea607d A |
646 | unsigned int newlen = textLen + 1 + len; |
647 | if (len == 0 || newlen >= sizeof(text)) break; | |
7cb34e5c | 648 | text[textLen] = len; |
32bb7e43 | 649 | mDNSPlatformMemCopy(text + textLen + 1, rawText, len); |
4aea607d | 650 | textLen = newlen; |
7cb34e5c A |
651 | } |
652 | else | |
653 | fprintf(stderr, "%s: TXT attribute too long for name = %s, type = %s, port = %s\n", | |
654 | gProgramName, name, type, port); | |
7f0064bd A |
655 | } |
656 | } | |
657 | if (good) { | |
658 | status = RegisterOneService(name, type, dom, text, textLen, atol(port)); | |
659 | if (status != mStatus_NoError) { | |
7cb34e5c A |
660 | fprintf(stderr, "%s: Failed to register service, name = %s, type = %s, port = %s\n", |
661 | gProgramName, name, type, port); | |
7f0064bd A |
662 | status = mStatus_NoError; // keep reading |
663 | } | |
664 | } | |
c9b9ae52 A |
665 | } while (good && !feof(fp)); |
666 | ||
667 | if ( ! good ) { | |
7f0064bd | 668 | fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, filePath); |
c9b9ae52 A |
669 | } |
670 | } | |
671 | ||
672 | if (fp != NULL) { | |
673 | junk = fclose(fp); | |
674 | assert(junk == 0); | |
675 | } | |
676 | ||
677 | return status; | |
678 | } | |
679 | ||
680 | static mStatus RegisterOurServices(void) | |
681 | { | |
682 | mStatus status; | |
683 | ||
684 | status = mStatus_NoError; | |
7cb34e5c A |
685 | if (gServiceName[0] != 0) { |
686 | status = RegisterOneService(gServiceName, | |
c9b9ae52 A |
687 | gServiceType, |
688 | gServiceDomain, | |
689 | gServiceText, gServiceTextLen, | |
690 | gPortNumber); | |
691 | } | |
692 | if (status == mStatus_NoError && gServiceFile[0] != 0) { | |
693 | status = RegisterServicesInFile(gServiceFile); | |
694 | } | |
695 | return status; | |
696 | } | |
697 | ||
698 | static void DeregisterOurServices(void) | |
699 | { | |
700 | PosixService *thisServ; | |
701 | int thisServID; | |
702 | ||
703 | while (gServiceList != NULL) { | |
704 | thisServ = gServiceList; | |
705 | gServiceList = thisServ->next; | |
706 | ||
707 | thisServID = thisServ->serviceID; | |
708 | ||
709 | mDNS_DeregisterService(&mDNSStorage, &thisServ->coreServ); | |
710 | ||
711 | if (gMDNSPlatformPosixVerboseLevel > 0) { | |
712 | fprintf(stderr, | |
713 | "%s: Deregistered service %d\n", | |
714 | gProgramName, | |
715 | thisServ->serviceID); | |
716 | } | |
717 | } | |
718 | } | |
719 | ||
720 | #if COMPILER_LIKES_PRAGMA_MARK | |
721 | #pragma mark **** Main | |
722 | #endif | |
723 | ||
c9b9ae52 A |
724 | int main(int argc, char **argv) |
725 | { | |
726 | mStatus status; | |
727 | int result; | |
728 | ||
729 | // Parse our command line arguments. This won't come back if there's an error. | |
730 | ||
731 | ParseArguments(argc, argv); | |
732 | ||
733 | // If we're told to run as a daemon, then do that straight away. | |
734 | // Note that we don't treat the inability to create our PID | |
735 | // file as an error. Also note that we assign getpid to a long | |
736 | // because printf has no format specified for pid_t. | |
737 | ||
738 | if (gDaemon) { | |
32bb7e43 | 739 | int result; |
c9b9ae52 A |
740 | if (gMDNSPlatformPosixVerboseLevel > 0) { |
741 | fprintf(stderr, "%s: Starting in daemon mode\n", gProgramName); | |
742 | } | |
32bb7e43 A |
743 | result = daemon(0,0); |
744 | if (result == 0) { | |
c9b9ae52 A |
745 | FILE *fp; |
746 | int junk; | |
747 | ||
748 | fp = fopen(gPIDFile, "w"); | |
749 | if (fp != NULL) { | |
750 | fprintf(fp, "%ld\n", (long) getpid()); | |
751 | junk = fclose(fp); | |
752 | assert(junk == 0); | |
753 | } | |
32bb7e43 A |
754 | } else { |
755 | fprintf(stderr, "%s: Could not run as daemon - exiting\n", gProgramName); | |
756 | exit(result); | |
c9b9ae52 A |
757 | } |
758 | } else { | |
759 | if (gMDNSPlatformPosixVerboseLevel > 0) { | |
760 | fprintf(stderr, "%s: Starting in foreground mode, PID %ld\n", gProgramName, (long) getpid()); | |
761 | } | |
762 | } | |
763 | ||
764 | status = mDNS_Init(&mDNSStorage, &PlatformStorage, | |
765 | mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, | |
766 | mDNS_Init_AdvertiseLocalAddresses, | |
767 | mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); | |
768 | if (status != mStatus_NoError) return(2); | |
769 | ||
770 | status = RegisterOurServices(); | |
771 | if (status != mStatus_NoError) return(2); | |
772 | ||
773 | signal(SIGHUP, HandleSigHup); // SIGHUP has to be sent by kill -HUP <pid> | |
774 | signal(SIGINT, HandleSigInt); // SIGINT is what you get for a Ctrl-C | |
775 | signal(SIGQUIT, HandleSigQuit); // SIGQUIT is what you get for a Ctrl-\ (indeed) | |
776 | signal(SIGUSR1, HandleSigUsr1); // SIGUSR1 has to be sent by kill -USR1 <pid> | |
777 | ||
778 | while (!gStopNow) | |
779 | { | |
780 | int nfds = 0; | |
781 | fd_set readfds; | |
782 | struct timeval timeout; | |
783 | int result; | |
784 | ||
785 | // 1. Set up the fd_set as usual here. | |
786 | // This example client has no file descriptors of its own, | |
787 | // but a real application would call FD_SET to add them to the set here | |
788 | FD_ZERO(&readfds); | |
789 | ||
790 | // 2. Set up the timeout. | |
791 | // This example client has no other work it needs to be doing, | |
792 | // so we set an effectively infinite timeout | |
793 | timeout.tv_sec = 0x3FFFFFFF; | |
794 | timeout.tv_usec = 0; | |
795 | ||
796 | // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout | |
797 | mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &timeout); | |
798 | ||
799 | // 4. Call select as normal | |
800 | verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec); | |
801 | result = select(nfds, &readfds, NULL, NULL, &timeout); | |
802 | ||
803 | if (result < 0) | |
804 | { | |
805 | verbosedebugf("select() returned %d errno %d", result, errno); | |
806 | if (errno != EINTR) gStopNow = mDNStrue; | |
807 | else | |
808 | { | |
809 | if (gReceivedSigUsr1) | |
810 | { | |
811 | gReceivedSigUsr1 = mDNSfalse; | |
812 | gMDNSPlatformPosixVerboseLevel += 1; | |
813 | if (gMDNSPlatformPosixVerboseLevel > 2) | |
814 | gMDNSPlatformPosixVerboseLevel = 0; | |
815 | if ( gMDNSPlatformPosixVerboseLevel > 0 ) | |
816 | fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel); | |
817 | } | |
818 | if (gReceivedSigHup) | |
819 | { | |
820 | if (gMDNSPlatformPosixVerboseLevel > 0) | |
821 | fprintf(stderr, "\nSIGHUP\n"); | |
822 | gReceivedSigHup = mDNSfalse; | |
823 | DeregisterOurServices(); | |
824 | status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage); | |
825 | if (status != mStatus_NoError) break; | |
826 | status = RegisterOurServices(); | |
827 | if (status != mStatus_NoError) break; | |
828 | } | |
829 | } | |
830 | } | |
831 | else | |
832 | { | |
833 | // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work | |
834 | mDNSPosixProcessFDSet(&mDNSStorage, &readfds); | |
835 | ||
836 | // 6. This example client has no other work it needs to be doing, | |
837 | // but a real client would do its work here | |
838 | // ... (do work) ... | |
839 | } | |
840 | } | |
841 | ||
842 | debugf("Exiting"); | |
843 | ||
844 | DeregisterOurServices(); | |
845 | mDNS_Close(&mDNSStorage); | |
846 | ||
847 | if (status == mStatus_NoError) { | |
848 | result = 0; | |
849 | } else { | |
850 | result = 2; | |
851 | } | |
852 | if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) { | |
67c8f8a1 | 853 | fprintf(stderr, "%s: Finished with status %d, result %d\n", gProgramName, (int)status, result); |
c9b9ae52 A |
854 | } |
855 | ||
856 | return result; | |
857 | } |