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