*
* Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
*
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
* limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
-
- Change History (most recent first):
-
-$Log: Responder.c,v $
-Revision 1.30 2005/10/26 22:21:16 cheshire
-<rdar://problem/4149841> Potential buffer overflow in mDNSResponderPosix
-
-Revision 1.29 2005/03/04 21:35:33 cheshire
-<rdar://problem/4037201> Services.txt file not parsed properly when it contains more than one service
-
-Revision 1.28 2005/01/11 01:55:26 ksekar
-Fix compile errors in Posix debug build
-
-Revision 1.27 2004/12/01 04:28:43 cheshire
-<rdar://problem/3872803> Darwin patches for Solaris and Suse
-Use version of daemon() provided in mDNSUNP.c instead of local copy
-
-Revision 1.26 2004/11/30 22:37:01 cheshire
-Update copyright dates and add "Mode: C; tab-width: 4" headers
-
-Revision 1.25 2004/11/11 02:00:51 cheshire
-Minor fixes to getopt, error message
-
-Revision 1.24 2004/11/09 19:32:10 rpantos
-Suggestion from Ademar de Souza Reis Jr. to allow comments in services file
-
-Revision 1.23 2004/09/17 01:08:54 cheshire
-Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
- The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
- declared in that file are ONLY appropriate to single-address-space embedded applications.
- For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
-
-Revision 1.22 2004/09/16 01:58:22 cheshire
-Fix compiler warnings
-
-Revision 1.21 2004/06/15 03:48:07 cheshire
-Update mDNSResponderPosix to take multiple name=val arguments in a sane way
-
-Revision 1.20 2004/05/18 23:51:26 cheshire
-Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
-
-Revision 1.19 2004/03/12 08:03:14 cheshire
-Update comments
-
-Revision 1.18 2004/01/25 00:00:55 cheshire
-Change to use mDNSOpaque16fromIntVal() instead of shifting and masking
-
-Revision 1.17 2003/12/11 19:11:55 cheshire
-Fix compiler warning
-
-Revision 1.16 2003/08/14 02:19:55 cheshire
-<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
-
-Revision 1.15 2003/08/12 19:56:26 cheshire
-Update to APSL 2.0
-
-Revision 1.14 2003/08/06 18:20:51 cheshire
-Makefile cleanup
-
-Revision 1.13 2003/07/23 00:00:04 cheshire
-Add comments
-
-Revision 1.12 2003/07/15 01:55:16 cheshire
-<rdar://problem/3315777> Need to implement service registration with subtypes
-
-Revision 1.11 2003/07/14 18:11:54 cheshire
-Fix stricter compiler warnings
-
-Revision 1.10 2003/07/10 20:27:31 cheshire
-<rdar://problem/3318717> mDNSResponder Posix version is missing a 'b' in the getopt option string
-
-Revision 1.9 2003/07/02 21:19:59 cheshire
-<rdar://problem/3313413> Update copyright notices, etc., in source code comments
-
-Revision 1.8 2003/06/18 05:48:41 cheshire
-Fix warnings
-
-Revision 1.7 2003/05/06 00:00:50 cheshire
-<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
-
-Revision 1.6 2003/03/08 00:35:56 cheshire
-Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt")
-
-Revision 1.5 2003/02/20 06:48:36 cheshire
-<rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations
-Reviewed by: Josh Graessley, Bob Bradley
-
-Revision 1.4 2003/01/28 03:07:46 cheshire
-Add extra parameter to mDNS_RenameAndReregisterService(),
-and add support for specifying a domain other than dot-local.
-
-Revision 1.3 2002/09/21 20:44:53 zarzycki
-Added APSL info
-
-Revision 1.2 2002/09/19 04:20:44 cheshire
-Remove high-ascii characters that confuse some systems
-
-Revision 1.1 2002/09/17 06:24:35 cheshire
-First checkin
-
-*/
-
-#include "mDNSEmbeddedAPI.h"// Defines the interface to the client layer above
-#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
+ */
+
+#if __APPLE__
+// In Mac OS X 10.5 and later trying to use the daemon function gives a “‘daemon’ is deprecated”
+// error, which prevents compilation because we build with "-Werror".
+// Since this is supposed to be portable cross-platform code, we don't care that daemon is
+// deprecated on Mac OS X 10.5, so we use this preprocessor trick to eliminate the error message.
+#define daemon yes_we_know_that_daemon_is_deprecated_in_os_x_10_5_thankyou
+#endif
#include <assert.h>
-#include <stdio.h> // For printf()
-#include <stdlib.h> // For exit() etc.
-#include <string.h> // For strlen() etc.
-#include <unistd.h> // For select()
-#include <errno.h> // For errno, EINTR
+#include <stdio.h> // For printf()
+#include <stdlib.h> // For exit() etc.
+#include <string.h> // For strlen() etc.
+#include <unistd.h> // For select()
+#include <errno.h> // For errno, EINTR
#include <signal.h>
#include <fcntl.h>
+#include <sys/socket.h>
+
+#if __APPLE__
+#undef daemon
+extern int daemon(int, int);
+#endif
+
+#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above
+#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
+#include "mDNSUNP.h" // For daemon()
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark ***** Globals
#endif
-static mDNS mDNSStorage; // mDNS core uses this to store its globals
+mDNS mDNSStorage; // mDNS core uses this to store its globals
static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
-static const char *gProgramName = "mDNSResponderPosix";
+mDNSexport const char ProgramName[] = "mDNSResponderPosix";
+
+static const char *gProgramName = ProgramName;
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark ***** Signals
// o SIGQUIT causes a somewhat orderly shutdown (direct but dangerous)
// o SIGKILL kills us dead (easy to implement :-)
//
-// There are fatal race conditions in our signal handling, but there's not much
-// we can do about them while remaining within the Posix space. Specifically,
-// if a signal arrives after we test the globals its sets but before we call
-// select, the signal will be dropped. The user will have to send the signal
-// again. Unfortunately, Posix does not have a "sigselect" to atomically
+// There are fatal race conditions in our signal handling, but there's not much
+// we can do about them while remaining within the Posix space. Specifically,
+// if a signal arrives after we test the globals its sets but before we call
+// select, the signal will be dropped. The user will have to send the signal
+// again. Unfortunately, Posix does not have a "sigselect" to atomically
// modify the signal mask and start a select.
static void HandleSigUsr1(int sigraised)
- // If we get a SIGUSR1 we toggle the state of the
- // verbose mode.
+// If we get a SIGUSR1 we toggle the state of the
+// verbose mode.
{
assert(sigraised == SIGUSR1);
gReceivedSigUsr1 = mDNStrue;
}
static void HandleSigHup(int sigraised)
- // A handler for SIGHUP that causes us to break out of the
- // main event loop when the user kill 1's us. This has the
- // effect of triggered the main loop to deregister the
- // current services and re-read the preferences.
+// A handler for SIGHUP that causes us to break out of the
+// main event loop when the user kill 1's us. This has the
+// effect of triggered the main loop to deregister the
+// current services and re-read the preferences.
{
assert(sigraised == SIGHUP);
- gReceivedSigHup = mDNStrue;
+ gReceivedSigHup = mDNStrue;
}
static void HandleSigInt(int sigraised)
- // A handler for SIGINT that causes us to break out of the
- // main event loop when the user types ^C. This has the
- // effect of quitting the program.
+// A handler for SIGINT that causes us to break out of the
+// main event loop when the user types ^C. This has the
+// effect of quitting the program.
{
assert(sigraised == SIGINT);
-
+
if (gMDNSPlatformPosixVerboseLevel > 0) {
fprintf(stderr, "\nSIGINT\n");
}
}
static void HandleSigQuit(int sigraised)
- // If we get a SIGQUIT the user is desperate and we
- // just call mDNS_Close directly. This is definitely
- // not safe (because it could reenter mDNS), but
- // we presume that the user has already tried the safe
- // alternatives.
+// If we get a SIGQUIT the user is desperate and we
+// just call mDNS_Close directly. This is definitely
+// not safe (because it could reenter mDNS), but
+// we presume that the user has already tried the safe
+// alternatives.
{
assert(sigraised == SIGQUIT);
#endif
static mDNSBool CheckThatRichTextNameIsUsable(const char *richTextName, mDNSBool printExplanation)
- // Checks that richTextName is reasonable
- // label and, if it isn't and printExplanation is true, prints
- // an explanation of why not.
+// Checks that richTextName is reasonable
+// label and, if it isn't and printExplanation is true, prints
+// an explanation of why not.
{
mDNSBool result = mDNStrue;
if (result && strlen(richTextName) > 63) {
if (printExplanation) {
- fprintf(stderr,
- "%s: Service name is too long (must be 63 characters or less)\n",
+ fprintf(stderr,
+ "%s: Service name is too long (must be 63 characters or less)\n",
gProgramName);
}
result = mDNSfalse;
}
static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation)
- // Checks that serviceType is a reasonable service type
- // label and, if it isn't and printExplanation is true, prints
- // an explanation of why not.
+// Checks that serviceType is a reasonable service type
+// label and, if it isn't and printExplanation is true, prints
+// an explanation of why not.
{
mDNSBool result;
-
+
result = mDNStrue;
if (result && strlen(serviceType) > 63) {
if (printExplanation) {
- fprintf(stderr,
- "%s: Service type is too long (must be 63 characters or less)\n",
+ fprintf(stderr,
+ "%s: Service type is too long (must be 63 characters or less)\n",
gProgramName);
}
result = mDNSfalse;
}
if (result && serviceType[0] == 0) {
if (printExplanation) {
- fprintf(stderr,
- "%s: Service type can't be empty\n",
+ fprintf(stderr,
+ "%s: Service type can't be empty\n",
gProgramName);
}
result = mDNSfalse;
}
static mDNSBool CheckThatPortNumberIsUsable(long portNumber, mDNSBool printExplanation)
- // Checks that portNumber is a reasonable port number
- // and, if it isn't and printExplanation is true, prints
- // an explanation of why not.
+// Checks that portNumber is a reasonable port number
+// and, if it isn't and printExplanation is true, prints
+// an explanation of why not.
{
mDNSBool result;
-
+
result = mDNStrue;
if (result && (portNumber <= 0 || portNumber > 65535)) {
if (printExplanation) {
- fprintf(stderr,
- "%s: Port number specified by -p must be in range 1..65535\n",
+ fprintf(stderr,
+ "%s: Port number specified by -p must be in range 1..65535\n",
gProgramName);
}
result = mDNSfalse;
static void PrintUsage()
{
- fprintf(stderr,
- "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-p port] [-f file] [-b] [-P pidfile] [-x name=val ...]\n",
+ fprintf(stderr,
+ "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-p port] [-f file] [-b] [-P pidfile] [-x name=val ...]\n",
gProgramName);
fprintf(stderr, " -v verbose mode, level is a number from 0 to 2\n");
fprintf(stderr, " 0 = no debugging info (default)\n");
fprintf(stderr, " all subsequent arguments after -x are treated as name=val pairs.\n");
}
-static mDNSBool gAvoidPort53 = mDNStrue;
+static mDNSBool gAvoidPort53 = mDNStrue;
static const char *gServiceName = "";
static const char *gServiceType = kDefaultServiceType;
static const char *gServiceDomain = kDefaultServiceDomain;
-static mDNSu8 gServiceText[sizeof(RDataBody)];
-static mDNSu16 gServiceTextLen = 0;
-static int gPortNumber = kDefaultPortNumber;
+static mDNSu8 gServiceText[sizeof(RDataBody)];
+static mDNSu16 gServiceTextLen = 0;
+static int gPortNumber = kDefaultPortNumber;
static const char *gServiceFile = "";
-static mDNSBool gDaemon = mDNSfalse;
+static mDNSBool gDaemon = mDNSfalse;
static const char *gPIDFile = kDefaultPIDFile;
static void ParseArguments(int argc, char **argv)
- // Parses our command line arguments into the global variables
- // listed above.
+// Parses our command line arguments into the global variables
+// listed above.
{
int ch;
-
+
// Set gProgramName to the last path component of argv[0]
-
+
gProgramName = strrchr(argv[0], '/');
if (gProgramName == NULL) {
gProgramName = argv[0];
} else {
gProgramName += 1;
}
-
+
// Parse command line options using getopt.
-
+
do {
ch = getopt(argc, argv, "v:rn:t:d:p:f:dP:bx");
if (ch != -1) {
switch (ch) {
- case 'v':
- gMDNSPlatformPosixVerboseLevel = atoi(optarg);
- if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) {
- fprintf(stderr,
- "%s: Verbose mode must be in the range 0..2\n",
- gProgramName);
- exit(1);
- }
- break;
- case 'r':
- gAvoidPort53 = mDNSfalse;
- break;
- case 'n':
- gServiceName = optarg;
- if ( ! CheckThatRichTextNameIsUsable(gServiceName, mDNStrue) ) {
- exit(1);
- }
- break;
- case 't':
- gServiceType = optarg;
- if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) {
- exit(1);
- }
- break;
- case 'd':
- gServiceDomain = optarg;
- break;
- case 'p':
- gPortNumber = atol(optarg);
- if ( ! CheckThatPortNumberIsUsable(gPortNumber, mDNStrue) ) {
- exit(1);
- }
- break;
- case 'f':
- gServiceFile = optarg;
- break;
- case 'b':
- gDaemon = mDNStrue;
- break;
- case 'P':
- gPIDFile = optarg;
- break;
- case 'x':
- while (optind < argc)
- {
- gServiceText[gServiceTextLen] = strlen(argv[optind]);
- memcpy(gServiceText+gServiceTextLen+1, argv[optind], gServiceText[gServiceTextLen]);
- gServiceTextLen += 1 + gServiceText[gServiceTextLen];
- optind++;
- }
- ch = -1;
- break;
- case '?':
- default:
- PrintUsage();
+ case 'v':
+ gMDNSPlatformPosixVerboseLevel = atoi(optarg);
+ if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) {
+ fprintf(stderr,
+ "%s: Verbose mode must be in the range 0..2\n",
+ gProgramName);
+ exit(1);
+ }
+ break;
+ case 'r':
+ gAvoidPort53 = mDNSfalse;
+ break;
+ case 'n':
+ gServiceName = optarg;
+ if ( !CheckThatRichTextNameIsUsable(gServiceName, mDNStrue) ) {
+ exit(1);
+ }
+ break;
+ case 't':
+ gServiceType = optarg;
+ if ( !CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) {
exit(1);
- break;
+ }
+ break;
+ case 'd':
+ gServiceDomain = optarg;
+ break;
+ case 'p':
+ gPortNumber = atol(optarg);
+ if ( !CheckThatPortNumberIsUsable(gPortNumber, mDNStrue) ) {
+ exit(1);
+ }
+ break;
+ case 'f':
+ gServiceFile = optarg;
+ break;
+ case 'b':
+ gDaemon = mDNStrue;
+ break;
+ case 'P':
+ gPIDFile = optarg;
+ break;
+ case 'x':
+ while (optind < argc)
+ {
+ gServiceText[gServiceTextLen] = strlen(argv[optind]);
+ mDNSPlatformMemCopy(gServiceText+gServiceTextLen+1, argv[optind], gServiceText[gServiceTextLen]);
+ gServiceTextLen += 1 + gServiceText[gServiceTextLen];
+ optind++;
+ }
+ ch = -1;
+ break;
+ case '?':
+ default:
+ PrintUsage();
+ exit(1);
+ break;
}
}
} while (ch != -1);
// Check for any left over command line arguments.
-
+
if (optind != argc) {
- PrintUsage();
+ PrintUsage();
fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]);
exit(1);
}
-
+
// Check for inconsistency between the arguments.
-
+
if ( (gServiceName[0] == 0) && (gServiceFile[0] == 0) ) {
- PrintUsage();
+ PrintUsage();
fprintf(stderr, "%s: You must specify a service name to register (-n) or a service file (-f).\n", gProgramName);
exit(1);
}
static PosixService *gServiceList = NULL;
static void RegistrationCallback(mDNS *const m, ServiceRecordSet *const thisRegistration, mStatus status)
- // mDNS core calls this routine to tell us about the status of
- // our registration. The appropriate action to take depends
- // entirely on the value of status.
+// mDNS core calls this routine to tell us about the status of
+// our registration. The appropriate action to take depends
+// entirely on the value of status.
{
switch (status) {
- case mStatus_NoError:
- debugf("Callback: %##s Name Registered", thisRegistration->RR_SRV.resrec.name->c);
- // Do nothing; our name was successfully registered. We may
- // get more call backs in the future.
- break;
+ case mStatus_NoError:
+ debugf("Callback: %##s Name Registered", thisRegistration->RR_SRV.resrec.name->c);
+ // Do nothing; our name was successfully registered. We may
+ // get more call backs in the future.
+ break;
- case mStatus_NameConflict:
- debugf("Callback: %##s Name Conflict", thisRegistration->RR_SRV.resrec.name->c);
-
- // In the event of a conflict, this sample RegistrationCallback
- // just calls mDNS_RenameAndReregisterService to automatically
- // pick a new unique name for the service. For a device such as a
- // printer, this may be appropriate. For a device with a user
- // interface, and a screen, and a keyboard, the appropriate response
- // may be to prompt the user and ask them to choose a new name for
- // the service.
- //
- // Also, what do we do if mDNS_RenameAndReregisterService returns an
- // error. Right now I have no place to send that error to.
-
- status = mDNS_RenameAndReregisterService(m, thisRegistration, mDNSNULL);
- assert(status == mStatus_NoError);
- break;
+ case mStatus_NameConflict:
+ debugf("Callback: %##s Name Conflict", thisRegistration->RR_SRV.resrec.name->c);
+
+ // In the event of a conflict, this sample RegistrationCallback
+ // just calls mDNS_RenameAndReregisterService to automatically
+ // pick a new unique name for the service. For a device such as a
+ // printer, this may be appropriate. For a device with a user
+ // interface, and a screen, and a keyboard, the appropriate response
+ // may be to prompt the user and ask them to choose a new name for
+ // the service.
+ //
+ // Also, what do we do if mDNS_RenameAndReregisterService returns an
+ // error. Right now I have no place to send that error to.
+
+ status = mDNS_RenameAndReregisterService(m, thisRegistration, mDNSNULL);
+ assert(status == mStatus_NoError);
+ break;
+
+ case mStatus_MemFree:
+ debugf("Callback: %##s Memory Free", thisRegistration->RR_SRV.resrec.name->c);
+
+ // When debugging is enabled, make sure that thisRegistration
+ // is not on our gServiceList.
- case mStatus_MemFree:
- debugf("Callback: %##s Memory Free", thisRegistration->RR_SRV.resrec.name->c);
-
- // When debugging is enabled, make sure that thisRegistration
- // is not on our gServiceList.
-
#if !defined(NDEBUG)
- {
- PosixService *cursor;
-
- cursor = gServiceList;
- while (cursor != NULL) {
- assert(&cursor->coreServ != thisRegistration);
- cursor = cursor->next;
- }
- }
+ {
+ PosixService *cursor;
+
+ cursor = gServiceList;
+ while (cursor != NULL) {
+ assert(&cursor->coreServ != thisRegistration);
+ cursor = cursor->next;
+ }
+ }
#endif
- free(thisRegistration);
- break;
+ free(thisRegistration);
+ break;
- default:
- debugf("Callback: %##s Unknown Status %ld", thisRegistration->RR_SRV.resrec.name->c, status);
- break;
+ default:
+ debugf("Callback: %##s Unknown Status %ld", thisRegistration->RR_SRV.resrec.name->c, status);
+ break;
}
}
static int gServiceID = 0;
-static mStatus RegisterOneService(const char * richTextName,
- const char * serviceType,
- const char * serviceDomain,
- const mDNSu8 text[],
- mDNSu16 textLen,
- long portNumber)
+static mStatus RegisterOneService(const char * richTextName,
+ const char * serviceType,
+ const char * serviceDomain,
+ const mDNSu8 text[],
+ mDNSu16 textLen,
+ long portNumber)
{
- mStatus status;
+ mStatus status;
PosixService * thisServ;
- domainlabel name;
- domainname type;
- domainname domain;
-
+ domainlabel name;
+ domainname type;
+ domainname domain;
+
status = mStatus_NoError;
- thisServ = (PosixService *) malloc(sizeof(*thisServ));
+ thisServ = (PosixService *) calloc(sizeof(*thisServ), 1);
if (thisServ == NULL) {
status = mStatus_NoMemoryErr;
}
MakeDomainNameFromDNSNameString(&type, serviceType);
MakeDomainNameFromDNSNameString(&domain, serviceDomain);
status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ,
- &name, &type, &domain, // Name, type, domain
- NULL, mDNSOpaque16fromIntVal(portNumber),
- text, textLen, // TXT data, length
- NULL, 0, // Subtypes
- mDNSInterface_Any, // Interface ID
- RegistrationCallback, thisServ); // Callback and context
+ &name, &type, &domain, // Name, type, domain
+ NULL, mDNSOpaque16fromIntVal(portNumber),
+ NULL, text, textLen, // TXT data, length
+ NULL, 0, // Subtypes
+ mDNSInterface_Any, // Interface ID
+ RegistrationCallback, thisServ, 0); // Callback, context, flags
}
if (status == mStatus_NoError) {
thisServ->serviceID = gServiceID;
gServiceList = thisServ;
if (gMDNSPlatformPosixVerboseLevel > 0) {
- fprintf(stderr,
- "%s: Registered service %d, name '%s', type '%s', port %ld\n",
- gProgramName,
- thisServ->serviceID,
+ fprintf(stderr,
+ "%s: Registered service %d, name \"%s\", type \"%s\", domain \"%s\", port %ld\n",
+ gProgramName,
+ thisServ->serviceID,
richTextName,
serviceType,
+ serviceDomain,
portNumber);
}
} else {
return status;
}
-static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp)
-// Read a line, skipping over any blank lines or lines starting with '#'
+static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp, mDNSBool skipBlankLines)
{
- mDNSBool good, skip;
- do {
- good = (fgets(buf, bufSize, fp) != NULL);
- skip = (good && (buf[0] == '#'));
- } while (good && skip);
- if (good)
- {
- int len = strlen( buf);
- if ( buf[len - 1] == '\r' || buf[len - 1] == '\n')
- buf[len - 1] = '\0';
- }
- return good;
+ size_t len;
+ mDNSBool readNextLine;
+
+ do {
+ readNextLine = mDNSfalse;
+
+ if (fgets(buf, bufSize, fp) == NULL)
+ return mDNSfalse; // encountered EOF or an error condition
+
+ // These first characters indicate a blank line.
+ if (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\r' || buf[0] == '\n') {
+ if (!skipBlankLines)
+ return mDNSfalse;
+ readNextLine = mDNStrue;
+ }
+ // always skip comment lines
+ if (buf[0] == '#')
+ readNextLine = mDNStrue;
+
+ } while (readNextLine);
+
+ len = strlen( buf);
+ if ( buf[len - 1] == '\r' || buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+
+ return mDNStrue;
}
static mStatus RegisterServicesInFile(const char *filePath)
{
- mStatus status = mStatus_NoError;
+ mStatus status = mStatus_NoError;
FILE * fp = fopen(filePath, "r");
- int junk;
-
+ int rv;
+
if (fp == NULL) {
- status = mStatus_UnknownErr;
+ return mStatus_UnknownErr;
}
- if (status == mStatus_NoError) {
- mDNSBool good = mDNStrue;
- do {
- int ch;
- char name[256];
- char type[256];
- const char *dom = kDefaultServiceDomain;
- char rawText[1024];
- mDNSu8 text[sizeof(RDataBody)];
- unsigned int textLen = 0;
- char port[256];
-
- // Skip over any blank lines.
- do ch = fgetc(fp); while ( ch == '\n' || ch == '\r' );
- if (ch != EOF) good = (ungetc(ch, fp) == ch);
-
- // Read three lines, check them for validity, and register the service.
- good = ReadALine(name, sizeof(name), fp);
- if (good) {
- good = ReadALine(type, sizeof(type), fp);
- }
- if (good) {
- char *p = type;
- while (*p && *p != ' ') p++;
- if (*p) {
- *p = 0;
- dom = p+1;
- }
- }
- if (good) {
- good = ReadALine(port, sizeof(port), fp);
- }
- if (good) {
- good = CheckThatRichTextNameIsUsable(name, mDNSfalse)
- && CheckThatServiceTypeIsUsable(type, mDNSfalse)
- && CheckThatPortNumberIsUsable(atol(port), mDNSfalse);
- }
- if (good) {
- while (1) {
- int len;
- if (!ReadALine(rawText, sizeof(rawText), fp)) break;
- len = strlen(rawText);
- if (len <= 255)
- {
- unsigned int newlen = textLen + 1 + len;
- if (len == 0 || newlen >= sizeof(text)) break;
- text[textLen] = len;
- memcpy(text + textLen + 1, rawText, len);
- textLen = newlen;
- }
- else
- fprintf(stderr, "%s: TXT attribute too long for name = %s, type = %s, port = %s\n",
- gProgramName, name, type, port);
- }
- }
- if (good) {
- status = RegisterOneService(name, type, dom, text, textLen, atol(port));
- if (status != mStatus_NoError) {
- fprintf(stderr, "%s: Failed to register service, name = %s, type = %s, port = %s\n",
- gProgramName, name, type, port);
- status = mStatus_NoError; // keep reading
- }
- }
- } while (good && !feof(fp));
-
- if ( ! good ) {
- fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, filePath);
+
+ if (gMDNSPlatformPosixVerboseLevel > 1)
+ fprintf(stderr, "Parsing %s for services\n", filePath);
+
+ do {
+ char nameBuf[256];
+ char * name = nameBuf;
+ char type[256];
+ const char *dom = kDefaultServiceDomain;
+ char rawText[1024];
+ mDNSu8 text[sizeof(RDataBody)];
+ unsigned int textLen = 0;
+ char port[256];
+ char *p;
+
+ // Read the service name, type, port, and optional text record fields.
+ // Skip blank lines while looking for the next service name.
+ if (!ReadALine(name, sizeof(nameBuf), fp, mDNStrue))
+ break;
+
+ // Special case that allows service name to begin with a '#'
+ // character by escaping it with a '\' to distiguish it from
+ // a comment line. Remove the leading '\' here before
+ // registering the service.
+ if (name[0] == '\\' && name[1] == '#')
+ name++;
+
+ if (gMDNSPlatformPosixVerboseLevel > 1)
+ fprintf(stderr, "Service name: \"%s\"\n", name);
+
+ // Don't skip blank lines in calls to ReadAline() after finding the
+ // service name since the next blank line indicates the end
+ // of this service record.
+ if (!ReadALine(type, sizeof(type), fp, mDNSfalse))
+ break;
+
+ // see if a domain name is specified
+ p = type;
+ while (*p && *p != ' ' && *p != '\t') p++;
+ if (*p) {
+ *p = 0; // NULL terminate the <type>.<protocol> string
+ // skip any leading whitespace before domain name
+ p++;
+ while (*p && (*p == ' ' || *p == '\t')) p++;
+ if (*p)
+ dom = p;
}
+ if (gMDNSPlatformPosixVerboseLevel > 1) {
+ fprintf(stderr, "Service type: \"%s\"\n", type);
+ fprintf(stderr, "Service domain: \"%s\"\n", dom);
+ }
+
+ if (!ReadALine(port, sizeof(port), fp, mDNSfalse))
+ break;
+ if (gMDNSPlatformPosixVerboseLevel > 1)
+ fprintf(stderr, "Service port: %s\n", port);
+
+ if ( !CheckThatRichTextNameIsUsable(name, mDNStrue)
+ || !CheckThatServiceTypeIsUsable(type, mDNStrue)
+ || !CheckThatPortNumberIsUsable(atol(port), mDNStrue))
+ break;
+
+ // read the TXT record fields
+ while (1) {
+ int len;
+ if (!ReadALine(rawText, sizeof(rawText), fp, mDNSfalse)) break;
+ if (gMDNSPlatformPosixVerboseLevel > 1)
+ fprintf(stderr, "Text string: \"%s\"\n", rawText);
+ len = strlen(rawText);
+ if (len <= 255)
+ {
+ unsigned int newlen = textLen + 1 + len;
+ if (len == 0 || newlen >= sizeof(text)) break;
+ text[textLen] = len;
+ mDNSPlatformMemCopy(text + textLen + 1, rawText, len);
+ textLen = newlen;
+ }
+ else
+ fprintf(stderr, "%s: TXT attribute too long for name = %s, type = %s, port = %s\n",
+ gProgramName, name, type, port);
+ }
+
+ status = RegisterOneService(name, type, dom, text, textLen, atol(port));
+ if (status != mStatus_NoError) {
+ // print error, but try to read and register other services in the file
+ fprintf(stderr, "%s: Failed to register service, name \"%s\", type \"%s\", domain \"%s\", port %s\n",
+ gProgramName, name, type, dom, port);
+ }
+
+ } while (!feof(fp));
+
+ if (!feof(fp)) {
+ fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, filePath);
+ status = mStatus_UnknownErr;
}
-
- if (fp != NULL) {
- junk = fclose(fp);
- assert(junk == 0);
- }
-
+
+ rv = fclose(fp);
+ assert(rv == 0);
+
return status;
}
static mStatus RegisterOurServices(void)
{
mStatus status;
-
+
status = mStatus_NoError;
if (gServiceName[0] != 0) {
- status = RegisterOneService(gServiceName,
- gServiceType,
- gServiceDomain,
- gServiceText, gServiceTextLen,
+ status = RegisterOneService(gServiceName,
+ gServiceType,
+ gServiceDomain,
+ gServiceText, gServiceTextLen,
gPortNumber);
}
if (status == mStatus_NoError && gServiceFile[0] != 0) {
static void DeregisterOurServices(void)
{
PosixService *thisServ;
- int thisServID;
-
+
while (gServiceList != NULL) {
thisServ = gServiceList;
gServiceList = thisServ->next;
- thisServID = thisServ->serviceID;
-
mDNS_DeregisterService(&mDNSStorage, &thisServ->coreServ);
if (gMDNSPlatformPosixVerboseLevel > 0) {
- fprintf(stderr,
+ fprintf(stderr,
"%s: Deregistered service %d\n",
- gProgramName,
+ gProgramName,
thisServ->serviceID);
}
}
int main(int argc, char **argv)
{
mStatus status;
- int result;
+ int result;
// Parse our command line arguments. This won't come back if there's an error.
-
+
ParseArguments(argc, argv);
// If we're told to run as a daemon, then do that straight away.
- // Note that we don't treat the inability to create our PID
- // file as an error. Also note that we assign getpid to a long
+ // Note that we don't treat the inability to create our PID
+ // file as an error. Also note that we assign getpid to a long
// because printf has no format specified for pid_t.
-
+
if (gDaemon) {
+ int result;
if (gMDNSPlatformPosixVerboseLevel > 0) {
fprintf(stderr, "%s: Starting in daemon mode\n", gProgramName);
}
- daemon(0,0);
- {
+ result = daemon(0,0);
+ if (result == 0) {
FILE *fp;
- int junk;
-
+ int junk;
+
fp = fopen(gPIDFile, "w");
if (fp != NULL) {
fprintf(fp, "%ld\n", (long) getpid());
junk = fclose(fp);
assert(junk == 0);
}
+ } else {
+ fprintf(stderr, "%s: Could not run as daemon - exiting\n", gProgramName);
+ exit(result);
}
} else {
if (gMDNSPlatformPosixVerboseLevel > 0) {
}
status = mDNS_Init(&mDNSStorage, &PlatformStorage,
- mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
- mDNS_Init_AdvertiseLocalAddresses,
- mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+ mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
+ mDNS_Init_AdvertiseLocalAddresses,
+ mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
if (status != mStatus_NoError) return(2);
- status = RegisterOurServices();
+ status = RegisterOurServices();
if (status != mStatus_NoError) return(2);
-
+
signal(SIGHUP, HandleSigHup); // SIGHUP has to be sent by kill -HUP <pid>
signal(SIGINT, HandleSigInt); // SIGINT is what you get for a Ctrl-C
signal(SIGQUIT, HandleSigQuit); // SIGQUIT is what you get for a Ctrl-\ (indeed)
signal(SIGUSR1, HandleSigUsr1); // SIGUSR1 has to be sent by kill -USR1 <pid>
- while (!gStopNow)
- {
- int nfds = 0;
- fd_set readfds;
- struct timeval timeout;
- int result;
-
- // 1. Set up the fd_set as usual here.
- // This example client has no file descriptors of its own,
- // but a real application would call FD_SET to add them to the set here
- FD_ZERO(&readfds);
-
- // 2. Set up the timeout.
- // This example client has no other work it needs to be doing,
- // so we set an effectively infinite timeout
- timeout.tv_sec = 0x3FFFFFFF;
- timeout.tv_usec = 0;
-
- // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout
- mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &timeout);
-
- // 4. Call select as normal
- verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec);
- result = select(nfds, &readfds, NULL, NULL, &timeout);
-
- if (result < 0)
- {
- verbosedebugf("select() returned %d errno %d", result, errno);
- if (errno != EINTR) gStopNow = mDNStrue;
- else
- {
- if (gReceivedSigUsr1)
- {
- gReceivedSigUsr1 = mDNSfalse;
- gMDNSPlatformPosixVerboseLevel += 1;
- if (gMDNSPlatformPosixVerboseLevel > 2)
- gMDNSPlatformPosixVerboseLevel = 0;
- if ( gMDNSPlatformPosixVerboseLevel > 0 )
- fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel);
- }
- if (gReceivedSigHup)
- {
- if (gMDNSPlatformPosixVerboseLevel > 0)
- fprintf(stderr, "\nSIGHUP\n");
- gReceivedSigHup = mDNSfalse;
- DeregisterOurServices();
- status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage);
- if (status != mStatus_NoError) break;
- status = RegisterOurServices();
- if (status != mStatus_NoError) break;
- }
- }
- }
- else
- {
- // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work
- mDNSPosixProcessFDSet(&mDNSStorage, &readfds);
-
- // 6. This example client has no other work it needs to be doing,
- // but a real client would do its work here
- // ... (do work) ...
- }
- }
-
- debugf("Exiting");
-
- DeregisterOurServices();
- mDNS_Close(&mDNSStorage);
+ while (!gStopNow)
+ {
+ int nfds = 0;
+ fd_set readfds, writefds;
+ struct timeval timeout;
+ int result;
+
+ // 1. Set up the fd_set as usual here.
+ // This example client has no file descriptors of its own,
+ // but a real application would call FD_SET to add them to the set here
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+
+ // 2. Set up the timeout.
+ // This example client has no other work it needs to be doing,
+ // so we set an effectively infinite timeout
+ timeout.tv_sec = FutureTime;
+ timeout.tv_usec = 0;
+
+ // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout
+ mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &writefds, &timeout);
+
+ // 4. Call select as normal
+ verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec);
+ result = select(nfds, &readfds, NULL, NULL, &timeout);
+
+ if (result < 0)
+ {
+ verbosedebugf("select() returned %d errno %d", result, errno);
+ if (errno != EINTR) gStopNow = mDNStrue;
+ else
+ {
+ if (gReceivedSigUsr1)
+ {
+ gReceivedSigUsr1 = mDNSfalse;
+ gMDNSPlatformPosixVerboseLevel += 1;
+ if (gMDNSPlatformPosixVerboseLevel > 2)
+ gMDNSPlatformPosixVerboseLevel = 0;
+ if ( gMDNSPlatformPosixVerboseLevel > 0 )
+ fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel);
+ }
+ if (gReceivedSigHup)
+ {
+ if (gMDNSPlatformPosixVerboseLevel > 0)
+ fprintf(stderr, "\nSIGHUP\n");
+ gReceivedSigHup = mDNSfalse;
+ DeregisterOurServices();
+ status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage);
+ if (status != mStatus_NoError) break;
+ status = RegisterOurServices();
+ if (status != mStatus_NoError) break;
+ }
+ }
+ }
+ else
+ {
+ // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work
+ mDNSPosixProcessFDSet(&mDNSStorage, &readfds, &writefds);
+
+ // 6. This example client has no other work it needs to be doing,
+ // but a real client would do its work here
+ // ... (do work) ...
+ }
+ }
+
+ debugf("Exiting");
+
+ DeregisterOurServices();
+ mDNS_Close(&mDNSStorage);
if (status == mStatus_NoError) {
result = 0;
result = 2;
}
if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) {
- fprintf(stderr, "%s: Finished with status %ld, result %d\n", gProgramName, status, result);
+ fprintf(stderr, "%s: Finished with status %d, result %d\n", gProgramName, (int)status, result);
}
-
+
return result;
}