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