summaryrefslogtreecommitdiffstats
path: root/mDNSResponder/mDNSPosix/Responder.c
diff options
context:
space:
mode:
Diffstat (limited to 'mDNSResponder/mDNSPosix/Responder.c')
-rwxr-xr-xmDNSResponder/mDNSPosix/Responder.c788
1 files changed, 788 insertions, 0 deletions
diff --git a/mDNSResponder/mDNSPosix/Responder.c b/mDNSResponder/mDNSPosix/Responder.c
new file mode 100755
index 00000000..3996b7b9
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/Responder.c
@@ -0,0 +1,788 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+#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 <signal.h>
+#include <fcntl.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
+static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
+
+mDNSexport const char ProgramName[] = "mDNSResponderPosix";
+
+static const char *gProgramName = ProgramName;
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Signals
+#endif
+
+static volatile mDNSBool gReceivedSigUsr1;
+static volatile mDNSBool gReceivedSigHup;
+static volatile mDNSBool gStopNow;
+
+// We support 4 signals.
+//
+// o SIGUSR1 toggles verbose mode on and off in debug builds
+// o SIGHUP triggers the program to re-read its preferences.
+// o SIGINT causes an orderly shutdown of the program.
+// 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
+// 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.
+{
+ 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.
+{
+ assert(sigraised == SIGHUP);
+ 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.
+{
+ assert(sigraised == SIGINT);
+
+ if (gMDNSPlatformPosixVerboseLevel > 0) {
+ fprintf(stderr, "\nSIGINT\n");
+ }
+ gStopNow = mDNStrue;
+}
+
+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.
+{
+ assert(sigraised == SIGQUIT);
+
+ if (gMDNSPlatformPosixVerboseLevel > 0) {
+ fprintf(stderr, "\nSIGQUIT\n");
+ }
+ mDNS_Close(&mDNSStorage);
+ exit(0);
+}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Parameter Checking
+#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.
+{
+ 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",
+ gProgramName);
+ }
+ result = mDNSfalse;
+ }
+ if (result && richTextName[0] == 0) {
+ if (printExplanation) {
+ fprintf(stderr, "%s: Service name can't be empty\n", gProgramName);
+ }
+ result = mDNSfalse;
+ }
+ return result;
+}
+
+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.
+{
+ 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",
+ gProgramName);
+ }
+ result = mDNSfalse;
+ }
+ if (result && serviceType[0] == 0) {
+ if (printExplanation) {
+ fprintf(stderr,
+ "%s: Service type can't be empty\n",
+ gProgramName);
+ }
+ result = mDNSfalse;
+ }
+ return result;
+}
+
+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.
+{
+ 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",
+ gProgramName);
+ }
+ result = mDNSfalse;
+ }
+ return result;
+}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Command Line Arguments
+#endif
+
+static const char kDefaultPIDFile[] = "/var/run/mDNSResponder.pid";
+static const char kDefaultServiceType[] = "_afpovertcp._tcp.";
+static const char kDefaultServiceDomain[] = "local.";
+enum {
+ kDefaultPortNumber = 548
+};
+
+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",
+ 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, " 1 = standard debugging info\n");
+ fprintf(stderr, " 2 = intense debugging info\n");
+ fprintf(stderr, " can be cycled kill -USR1\n");
+ fprintf(stderr, " -r also bind to port 53 (port 5353 is always bound)\n");
+ fprintf(stderr, " -n uses 'name' as the service name (required)\n");
+ fprintf(stderr, " -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType);
+ fprintf(stderr, " -d uses 'domain' as the service domain (default is '%s')\n", kDefaultServiceDomain);
+ fprintf(stderr, " -p uses 'port' as the port number (default is '%d')\n", kDefaultPortNumber);
+ fprintf(stderr, " -f reads a service list from 'file'\n");
+ fprintf(stderr, " -b forces daemon (background) mode\n");
+ fprintf(stderr, " -P uses 'pidfile' as the PID file\n");
+ fprintf(stderr, " (default is '%s')\n", kDefaultPIDFile);
+ fprintf(stderr, " only meaningful if -b also specified\n");
+ fprintf(stderr, " -x stores name=val in TXT record (default is empty).\n");
+ fprintf(stderr, " MUST be the last command-line argument;\n");
+ fprintf(stderr, " all subsequent arguments after -x are treated as name=val pairs.\n");
+}
+
+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 const char *gServiceFile = "";
+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.
+{
+ 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]);
+ 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();
+ 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();
+ fprintf(stderr, "%s: You must specify a service name to register (-n) or a service file (-f).\n", gProgramName);
+ exit(1);
+ }
+}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Registration
+#endif
+
+typedef struct PosixService PosixService;
+
+struct PosixService {
+ ServiceRecordSet coreServ;
+ PosixService *next;
+ int serviceID;
+};
+
+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.
+{
+ 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_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.
+
+ #if !defined(NDEBUG)
+ {
+ PosixService *cursor;
+
+ cursor = gServiceList;
+ while (cursor != NULL) {
+ assert(&cursor->coreServ != thisRegistration);
+ cursor = cursor->next;
+ }
+ }
+ #endif
+ free(thisRegistration);
+ 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)
+{
+ mStatus status;
+ PosixService * thisServ;
+ domainlabel name;
+ domainname type;
+ domainname domain;
+
+ status = mStatus_NoError;
+ thisServ = (PosixService *) malloc(sizeof(*thisServ));
+ if (thisServ == NULL) {
+ status = mStatus_NoMemoryErr;
+ }
+ if (status == mStatus_NoError) {
+ MakeDomainLabelFromLiteralString(&name, richTextName);
+ 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, 0); // Callback, context, flags
+ }
+ if (status == mStatus_NoError) {
+ thisServ->serviceID = gServiceID;
+ gServiceID += 1;
+
+ thisServ->next = gServiceList;
+ gServiceList = thisServ;
+
+ if (gMDNSPlatformPosixVerboseLevel > 0) {
+ fprintf(stderr,
+ "%s: Registered service %d, name \"%s\", type \"%s\", domain \"%s\", port %ld\n",
+ gProgramName,
+ thisServ->serviceID,
+ richTextName,
+ serviceType,
+ serviceDomain,
+ portNumber);
+ }
+ } else {
+ if (thisServ != NULL) {
+ free(thisServ);
+ }
+ }
+ return status;
+}
+
+static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp, mDNSBool skipBlankLines)
+{
+ 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;
+ FILE * fp = fopen(filePath, "r");
+
+ if (fp == NULL) {
+ return mStatus_UnknownErr;
+ }
+
+ 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;
+ }
+
+ assert(0 == fclose(fp));
+
+ return status;
+}
+
+static mStatus RegisterOurServices(void)
+{
+ mStatus status;
+
+ status = mStatus_NoError;
+ if (gServiceName[0] != 0) {
+ status = RegisterOneService(gServiceName,
+ gServiceType,
+ gServiceDomain,
+ gServiceText, gServiceTextLen,
+ gPortNumber);
+ }
+ if (status == mStatus_NoError && gServiceFile[0] != 0) {
+ status = RegisterServicesInFile(gServiceFile);
+ }
+ return status;
+}
+
+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,
+ "%s: Deregistered service %d\n",
+ gProgramName,
+ thisServ->serviceID);
+ }
+ }
+}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark **** Main
+#endif
+
+int main(int argc, char **argv)
+{
+ mStatus status;
+ 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
+ // 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);
+ }
+ result = daemon(0,0);
+ if (result == 0) {
+ FILE *fp;
+ 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) {
+ fprintf(stderr, "%s: Starting in foreground mode, PID %ld\n", gProgramName, (long) getpid());
+ }
+ }
+
+ status = mDNS_Init(&mDNSStorage, &PlatformStorage,
+ mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
+ mDNS_Init_AdvertiseLocalAddresses,
+ mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+ if (status != mStatus_NoError) return(2);
+
+ 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);
+
+ if (status == mStatus_NoError) {
+ result = 0;
+ } else {
+ result = 2;
+ }
+ if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) {
+ fprintf(stderr, "%s: Finished with status %d, result %d\n", gProgramName, (int)status, result);
+ }
+
+ return result;
+}