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