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