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