summaryrefslogtreecommitdiffstats
path: root/mDNSResponder/mDNSPosix
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2014-01-30 13:52:13 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2014-01-30 16:23:04 +0100
commit9449f151d0ccf3ac755d5f2bd9b4057ae2b03157 (patch)
treeada21dc6aa0b146c62a7561a08fb51fe4a8922ee /mDNSResponder/mDNSPosix
parentDHCPCD(8): Add MASTER_ONLY option (diff)
downloadrtems-libbsd-9449f151d0ccf3ac755d5f2bd9b4057ae2b03157.tar.bz2
mDNS: Import
The sources can be obtained via: http://opensource.apple.com/tarballs/mDNSResponder/mDNSResponder-544.tar.gz
Diffstat (limited to 'mDNSResponder/mDNSPosix')
-rwxr-xr-xmDNSResponder/mDNSPosix/Client.c223
-rw-r--r--mDNSResponder/mDNSPosix/ExampleClientApp.c91
-rw-r--r--mDNSResponder/mDNSPosix/ExampleClientApp.h18
-rw-r--r--mDNSResponder/mDNSPosix/Identify.c376
-rwxr-xr-xmDNSResponder/mDNSPosix/Makefile523
-rw-r--r--mDNSResponder/mDNSPosix/NetMonitor.c1003
-rw-r--r--mDNSResponder/mDNSPosix/PosixDaemon.c258
-rw-r--r--mDNSResponder/mDNSPosix/ProxyResponder.c300
-rwxr-xr-xmDNSResponder/mDNSPosix/ReadMe.txt314
-rwxr-xr-xmDNSResponder/mDNSPosix/Responder.c788
-rwxr-xr-xmDNSResponder/mDNSPosix/Services.txt36
-rwxr-xr-xmDNSResponder/mDNSPosix/libnss_mdns.8148
-rwxr-xr-xmDNSResponder/mDNSPosix/mDNSPosix.c1789
-rwxr-xr-xmDNSResponder/mDNSPosix/mDNSPosix.h85
-rwxr-xr-xmDNSResponder/mDNSPosix/mDNSUNP.c719
-rwxr-xr-xmDNSResponder/mDNSPosix/mDNSUNP.h130
-rw-r--r--mDNSResponder/mDNSPosix/mdnsd.sh73
-rwxr-xr-xmDNSResponder/mDNSPosix/nss_ReadMe.txt125
-rwxr-xr-xmDNSResponder/mDNSPosix/nss_mdns.c2723
-rwxr-xr-xmDNSResponder/mDNSPosix/nss_mdns.conf13
-rwxr-xr-xmDNSResponder/mDNSPosix/nss_mdns.conf.5135
-rwxr-xr-xmDNSResponder/mDNSPosix/parselog.py247
22 files changed, 10117 insertions, 0 deletions
diff --git a/mDNSResponder/mDNSPosix/Client.c b/mDNSResponder/mDNSPosix/Client.c
new file mode 100755
index 00000000..c0badf43
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/Client.c
@@ -0,0 +1,223 @@
+/* -*- 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.
+ */
+
+#include <assert.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "mDNSEmbeddedAPI.h" // Defines the interface to the mDNS core code
+#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
+#include "ExampleClientApp.h"
+
+// Globals
+static mDNS mDNSStorage; // mDNS core uses this to store its globals
+static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
+#define RR_CACHE_SIZE 500
+static CacheEntity gRRCache[RR_CACHE_SIZE];
+
+mDNSexport const char ProgramName[] = "mDNSClientPosix";
+
+static const char *gProgramName = ProgramName;
+
+static void BrowseCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+// A callback from the core mDNS code that indicates that we've received a
+// response to our query. Note that this code runs on the main thread
+// (in fact, there is only one thread!), so we can safely printf the results.
+{
+ domainlabel name;
+ domainname type;
+ domainname domain;
+ char nameC [MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
+ char typeC [MAX_ESCAPED_DOMAIN_NAME];
+ char domainC[MAX_ESCAPED_DOMAIN_NAME];
+ const char *state;
+
+ (void)m; // Unused
+ (void)question; // Unused
+
+ assert(answer->rrtype == kDNSType_PTR);
+
+ DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain);
+
+ ConvertDomainLabelToCString_unescaped(&name, nameC);
+ ConvertDomainNameToCString(&type, typeC);
+ ConvertDomainNameToCString(&domain, domainC);
+
+ // If the TTL has hit 0, the service is no longer available.
+ if (!AddRecord) {
+ state = "Lost ";
+ } else {
+ state = "Found";
+ }
+ fprintf(stderr, "*** %s name = '%s', type = '%s', domain = '%s'\n", state, nameC, typeC, domainC);
+}
+
+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 specified by -t 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 specified by -t can't be empty\n",
+ gProgramName);
+ }
+ result = mDNSfalse;
+ }
+ return result;
+}
+
+static const char kDefaultServiceType[] = "_afpovertcp._tcp";
+static const char kDefaultDomain[] = "local.";
+
+static void PrintUsage()
+{
+ fprintf(stderr,
+ "Usage: %s [-v level] [-t type] [-d domain]\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, " -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType);
+ fprintf(stderr, " -d uses 'domain' as the domain to browse (default is '%s')\n", kDefaultDomain);
+}
+
+static const char *gServiceType = kDefaultServiceType;
+static const char *gServiceDomain = kDefaultDomain;
+
+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:t:d:");
+ 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 't':
+ gServiceType = optarg;
+ if ( !CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) {
+ exit(1);
+ }
+ break;
+ case 'd':
+ gServiceDomain = optarg;
+ break;
+ case '?':
+ default:
+ PrintUsage();
+ exit(1);
+ break;
+ }
+ }
+ } while (ch != -1);
+
+ // Check for any left over command line arguments.
+
+ if (optind != argc) {
+ fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]);
+ exit(1);
+ }
+}
+
+int main(int argc, char **argv)
+// The program's main entry point. The program does a trivial
+// mDNS query, looking for all AFP servers in the local domain.
+{
+ int result;
+ mStatus status;
+ DNSQuestion question;
+ domainname type;
+ domainname domain;
+
+ // Parse our command line arguments. This won't come back if there's an error.
+ ParseArguments(argc, argv);
+
+ // Initialise the mDNS core.
+ status = mDNS_Init(&mDNSStorage, &PlatformStorage,
+ gRRCache, RR_CACHE_SIZE,
+ mDNS_Init_DontAdvertiseLocalAddresses,
+ mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+ if (status == mStatus_NoError) {
+
+ // Construct and start the query.
+
+ MakeDomainNameFromDNSNameString(&type, gServiceType);
+ MakeDomainNameFromDNSNameString(&domain, gServiceDomain);
+
+ status = mDNS_StartBrowse(&mDNSStorage, &question, &type, &domain, mDNSNULL, mDNSInterface_Any, 0, mDNSfalse, mDNSfalse, BrowseCallback, NULL);
+
+ // Run the platform main event loop until the user types ^C.
+ // The BrowseCallback routine is responsible for printing
+ // any results that we find.
+
+ if (status == mStatus_NoError) {
+ fprintf(stderr, "Hit ^C when you're bored waiting for responses.\n");
+ ExampleClientEventLoop(&mDNSStorage);
+ mDNS_StopQuery(&mDNSStorage, &question);
+ 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 0;
+}
diff --git a/mDNSResponder/mDNSPosix/ExampleClientApp.c b/mDNSResponder/mDNSPosix/ExampleClientApp.c
new file mode 100644
index 00000000..f23248b8
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/ExampleClientApp.c
@@ -0,0 +1,91 @@
+/* -*- 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.
+ */
+
+#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 <netinet/in.h> // For INADDR_NONE
+#include <arpa/inet.h> // For inet_addr()
+#include <netdb.h> // For gethostbyname()
+#include <signal.h> // For SIGINT, etc.
+
+#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
+
+//*******************************************************************************************
+// Main
+
+static volatile mDNSBool StopNow;
+
+mDNSlocal void HandleSIG(int signal)
+{
+ (void)signal; // Unused
+ debugf("%s","");
+ debugf("HandleSIG");
+ StopNow = mDNStrue;
+}
+
+mDNSexport void ExampleClientEventLoop(mDNS *const m)
+{
+ signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C
+ signal(SIGTERM, HandleSIG);
+
+ while (!StopNow)
+ {
+ 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(m, &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) StopNow = mDNStrue;
+ }
+ else
+ {
+ // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work
+ mDNSPosixProcessFDSet(m, &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");
+}
diff --git a/mDNSResponder/mDNSPosix/ExampleClientApp.h b/mDNSResponder/mDNSPosix/ExampleClientApp.h
new file mode 100644
index 00000000..53f7f171
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/ExampleClientApp.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2003 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.
+ */
+
+extern void ExampleClientEventLoop(mDNS *const m);
diff --git a/mDNSResponder/mDNSPosix/Identify.c b/mDNSResponder/mDNSPosix/Identify.c
new file mode 100644
index 00000000..003ac631
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/Identify.c
@@ -0,0 +1,376 @@
+/* -*- 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.
+ *
+ */
+
+//*************************************************************************************************************
+// Incorporate mDNS.c functionality
+
+// We want to use the functionality provided by "mDNS.c",
+// except we'll sneak a peek at the packets before forwarding them to the normal mDNSCoreReceive() routine
+#define mDNSCoreReceive __MDNS__mDNSCoreReceive
+#include "mDNS.c"
+#undef mDNSCoreReceive
+
+//*************************************************************************************************************
+// Headers
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h> // For n_long, required by <netinet/ip.h> below
+#include <netinet/ip.h> // For IPTOS_LOWDELAY etc.
+#include <arpa/inet.h>
+#include <signal.h>
+
+#include "mDNSEmbeddedAPI.h" // Defines the interface to the mDNS core code
+#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
+#include "ExampleClientApp.h"
+
+//*************************************************************************************************************
+// Globals
+
+static mDNS mDNSStorage; // mDNS core uses this to store its globals
+static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
+#define RR_CACHE_SIZE 500
+static CacheEntity gRRCache[RR_CACHE_SIZE];
+mDNSexport const char ProgramName[] = "mDNSIdentify";
+
+static volatile int StopNow; // 0 means running, 1 means stop because we got an answer, 2 means stop because of Ctrl-C
+static volatile int NumAnswers, NumAddr, NumAAAA, NumHINFO;
+static char hostname[MAX_ESCAPED_DOMAIN_NAME], hardware[256], software[256];
+static mDNSAddr lastsrc, hostaddr, target;
+static mDNSOpaque16 lastid, id;
+
+//*************************************************************************************************************
+// Utilities
+
+// Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
+mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
+mDNSlocal mDNSu32 mprintf(const char *format, ...)
+{
+ mDNSu32 length;
+ unsigned char buffer[512];
+ va_list ptr;
+ va_start(ptr,format);
+ length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
+ va_end(ptr);
+ printf("%s", buffer);
+ return(length);
+}
+
+//*************************************************************************************************************
+// Main code
+
+mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
+ const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport,
+ const mDNSInterfaceID InterfaceID)
+{
+ (void)dstaddr; // Unused
+ // Snag copy of header ID, then call through
+ lastid = msg->h.id;
+ lastsrc = *srcaddr;
+
+ // We *want* to allow off-net unicast responses here.
+ // For now, the simplest way to allow that is to pretend it was received via multicast so that mDNSCore doesn't reject the packet
+ __MDNS__mDNSCoreReceive(m, msg, end, srcaddr, srcport, &AllDNSLinkGroup_v4, dstport, InterfaceID);
+}
+
+mDNSlocal void NameCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+{
+ (void)m; // Unused
+ (void)question; // Unused
+ (void)AddRecord; // Unused
+ if (!id.NotAnInteger) id = lastid;
+ if (answer->rrtype == kDNSType_PTR || answer->rrtype == kDNSType_CNAME)
+ {
+ ConvertDomainNameToCString(&answer->rdata->u.name, hostname);
+ StopNow = 1;
+ mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c);
+ }
+}
+
+mDNSlocal void InfoCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+{
+ (void)m; // Unused
+ (void)question; // Unused
+ (void)AddRecord; // Unused
+ if (answer->rrtype == kDNSType_A)
+ {
+ if (!id.NotAnInteger) id = lastid;
+ NumAnswers++;
+ NumAddr++;
+ mprintf("%##s %s %.4a\n", answer->name->c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv4);
+ hostaddr.type = mDNSAddrType_IPv4; // Prefer v4 target to v6 target, for now
+ hostaddr.ip.v4 = answer->rdata->u.ipv4;
+ }
+ else if (answer->rrtype == kDNSType_AAAA)
+ {
+ if (!id.NotAnInteger) id = lastid;
+ NumAnswers++;
+ NumAAAA++;
+ mprintf("%##s %s %.16a\n", answer->name->c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv6);
+ if (!hostaddr.type) // Prefer v4 target to v6 target, for now
+ {
+ hostaddr.type = mDNSAddrType_IPv6;
+ hostaddr.ip.v6 = answer->rdata->u.ipv6;
+ }
+ }
+ else if (answer->rrtype == kDNSType_HINFO)
+ {
+ mDNSu8 *p = answer->rdata->u.data;
+ strncpy(hardware, (char*)(p+1), p[0]);
+ hardware[p[0]] = 0;
+ p += 1 + p[0];
+ strncpy(software, (char*)(p+1), p[0]);
+ software[p[0]] = 0;
+ NumAnswers++;
+ NumHINFO++;
+ }
+
+ // If we've got everything we're looking for, don't need to wait any more
+ if (/*NumHINFO && */ (NumAddr || NumAAAA)) StopNow = 1;
+}
+
+mDNSlocal void ServicesCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+{
+ (void)m; // Unused
+ (void)question; // Unused
+ (void)AddRecord; // Unused
+ // Right now the mDNSCore targeted-query code is incomplete --
+ // it issues targeted queries, but accepts answers from anywhere
+ // For now, we'll just filter responses here so we don't get confused by responses from someone else
+ if (answer->rrtype == kDNSType_PTR && mDNSSameAddress(&lastsrc, &target))
+ {
+ NumAnswers++;
+ mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c);
+ }
+}
+
+mDNSlocal void WaitForAnswer(mDNS *const m, int seconds)
+{
+ struct timeval end;
+ gettimeofday(&end, NULL);
+ end.tv_sec += seconds;
+ StopNow = 0;
+ NumAnswers = 0;
+ while (!StopNow)
+ {
+ int nfds = 0;
+ fd_set readfds;
+ struct timeval now, remain = end;
+ int result;
+
+ FD_ZERO(&readfds);
+ gettimeofday(&now, NULL);
+ if (remain.tv_usec < now.tv_usec) { remain.tv_usec += 1000000; remain.tv_sec--; }
+ if (remain.tv_sec < now.tv_sec)
+ {
+ if (!NumAnswers) printf("No response after %d seconds\n", seconds);
+ return;
+ }
+ remain.tv_usec -= now.tv_usec;
+ remain.tv_sec -= now.tv_sec;
+ mDNSPosixGetFDSet(m, &nfds, &readfds, &remain);
+ result = select(nfds, &readfds, NULL, NULL, &remain);
+ if (result >= 0) mDNSPosixProcessFDSet(m, &readfds);
+ else if (errno != EINTR) StopNow = 2;
+ }
+}
+
+mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
+{
+ lastsrc = zeroAddr;
+ if (qname) MakeDomainNameFromDNSNameString(&q->qname, qname);
+ q->InterfaceID = mDNSInterface_Any;
+ q->flags = 0;
+ q->Target = target ? *target : zeroAddr;
+ q->TargetPort = MulticastDNSPort;
+ q->TargetQID = zeroID;
+ q->qtype = qtype;
+ q->qclass = kDNSClass_IN;
+ q->LongLived = mDNSfalse;
+ q->ExpectUnique = mDNSfalse; // Don't want to stop after the first response packet
+ q->ForceMCast = mDNStrue; // Query via multicast, even for apparently uDNS names like 1.1.1.17.in-addr.arpa.
+ q->ReturnIntermed = mDNStrue;
+ q->SuppressUnusable = mDNSfalse;
+ q->SearchListIndex = 0;
+ q->AppendSearchDomains = 0;
+ q->RetryWithSearchDomains = mDNSfalse;
+ q->TimeoutQuestion = 0;
+ q->ValidationRequired = 0;
+ q->ValidatingResponse = 0;
+ q->WakeOnResolve = 0;
+ q->UseBackgroundTrafficClass = mDNSfalse;
+ q->ProxyQuestion = 0;
+ q->qnameOrig = mDNSNULL;
+ q->AnonInfo = mDNSNULL;
+ q->pid = mDNSPlatformGetPID();
+ q->QuestionCallback = callback;
+ q->QuestionContext = NULL;
+
+ //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype));
+ return(mDNS_StartQuery(&mDNSStorage, q));
+}
+
+mDNSlocal void DoOneQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
+{
+ mStatus status = StartQuery(q, qname, qtype, target, callback);
+ if (status != mStatus_NoError)
+ StopNow = 2;
+ else
+ {
+ WaitForAnswer(&mDNSStorage, 4);
+ mDNS_StopQuery(&mDNSStorage, q);
+ }
+}
+
+mDNSlocal int DoQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
+{
+ DoOneQuery(q, qname, qtype, target, callback);
+ if (StopNow == 0 && NumAnswers == 0 && target && target->type)
+ {
+ mprintf("%##s %s Trying multicast\n", q->qname.c, DNSTypeName(q->qtype));
+ DoOneQuery(q, qname, qtype, NULL, callback);
+ }
+ if (StopNow == 0 && NumAnswers == 0)
+ mprintf("%##s %s *** No Answer ***\n", q->qname.c, DNSTypeName(q->qtype));
+ return(StopNow);
+}
+
+mDNSlocal void HandleSIG(int signal)
+{
+ (void)signal; // Unused
+ debugf("%s","");
+ debugf("HandleSIG");
+ StopNow = 2;
+}
+
+mDNSexport int main(int argc, char **argv)
+{
+ const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
+ int this_arg = 1;
+ mStatus status;
+ struct in_addr s4;
+#if HAVE_IPV6
+ struct in6_addr s6;
+#endif
+ char buffer[256];
+ DNSQuestion q;
+
+ if (argc < 2) goto usage;
+
+ // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog
+ mDNS_DebugMode = mDNStrue;
+
+ // Initialise the mDNS core.
+ status = mDNS_Init(&mDNSStorage, &PlatformStorage,
+ gRRCache, RR_CACHE_SIZE,
+ mDNS_Init_DontAdvertiseLocalAddresses,
+ mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+ if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %d\n", (int)status); return(status); }
+
+ signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C
+ signal(SIGTERM, HandleSIG);
+
+ while (this_arg < argc)
+ {
+ char *arg = argv[this_arg++];
+ if (this_arg > 2) printf("\n");
+
+ lastid = id = zeroID;
+ hostaddr = target = zeroAddr;
+ hostname[0] = hardware[0] = software[0] = 0;
+ NumAddr = NumAAAA = NumHINFO = 0;
+
+ if (inet_pton(AF_INET, arg, &s4) == 1)
+ {
+ mDNSu8 *p = (mDNSu8 *)&s4;
+ // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
+ mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p[3], p[2], p[1], p[0]);
+ printf("%s\n", buffer);
+ target.type = mDNSAddrType_IPv4;
+ target.ip.v4.NotAnInteger = s4.s_addr;
+ DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback);
+ if (StopNow == 2) break;
+ }
+#if HAVE_IPV6
+ else if (inet_pton(AF_INET6, arg, &s6) == 1)
+ {
+ int i;
+ mDNSu8 *p = (mDNSu8 *)&s6;
+ for (i = 0; i < 16; i++)
+ {
+ static const char hexValues[] = "0123456789ABCDEF";
+ buffer[i * 4 ] = hexValues[p[15-i] & 0x0F];
+ buffer[i * 4 + 1] = '.';
+ buffer[i * 4 + 2] = hexValues[p[15-i] >> 4];
+ buffer[i * 4 + 3] = '.';
+ }
+ mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa.");
+ target.type = mDNSAddrType_IPv6;
+ mDNSPlatformMemCopy(&target.ip.v6, &s6, sizeof(target.ip.v6));
+ DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback);
+ if (StopNow == 2) break;
+ }
+#endif
+ else {
+ if (strlen(arg) >= sizeof(hostname)) {
+ fprintf(stderr, "hostname must be < %d characters\n", (int)sizeof(hostname));
+ goto usage;
+ }
+ strcpy(hostname, arg);
+ }
+
+ // Now we have the host name; get its A, AAAA, and HINFO
+ if (hostname[0]) DoQuery(&q, hostname, kDNSQType_ANY, &target, InfoCallback);
+ if (StopNow == 2) break;
+
+ if (hardware[0] || software[0])
+ {
+ printf("HINFO Hardware: %s\n", hardware);
+ printf("HINFO Software: %s\n", software);
+ }
+ else if (NumAnswers) printf("%s has no HINFO record\n", hostname);
+ else printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n");
+
+ if (NumAnswers)
+ {
+ // Because of the way we use lastsrc in ServicesCallback, we need to clear the cache to make sure we're getting fresh answers
+ mDNS *const m = &mDNSStorage;
+ mDNSu32 slot;
+ CacheGroup *cg;
+ CacheRecord *rr;
+ FORALL_CACHERECORDS(slot, cg, rr)
+ {
+ mDNS_PurgeCacheResourceRecord(m, rr);
+ }
+ if (target.type == 0) target = hostaddr; // Make sure the services query is targeted
+ DoQuery(&q, "_services._dns-sd._udp.local.", kDNSType_PTR, &target, ServicesCallback);
+ if (StopNow == 2) break;
+ }
+ }
+
+ mDNS_Close(&mDNSStorage);
+ return(0);
+
+usage:
+ fprintf(stderr, "Usage: %s <dot-local hostname> or <IPv4 address> or <IPv6 address> ...\n", progname);
+ return(-1);
+}
diff --git a/mDNSResponder/mDNSPosix/Makefile b/mDNSResponder/mDNSPosix/Makefile
new file mode 100755
index 00000000..d095a0f4
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/Makefile
@@ -0,0 +1,523 @@
+# -*- tab-width: 4 -*-
+#
+# Copyright (c) 2002-2004, Apple Computer, Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# IMPORTANT NOTE: This is a Makefile for *GNU make*
+# On some systems, a different program may be the default "make" command.
+# If "make os=xxx" gives lots of errors like "Missing dependency operator",
+# then try typing "gmake os=xxx" instead.
+#
+# This Makefile builds an mDNSResponder daemon and a libdns_sd.so shared library
+# for Linux. It also builds several example programs for embedded systems.
+#
+# Make with no arguments to build all production targets.
+# 'make DEBUG=1' to build debugging targets.
+# 'make clean' or 'make clean DEBUG=1' to delete prod/debug objects & targets
+# 'sudo make install [DEBUG=1]' to install mdnsd daemon and libdns_sd.
+#
+# Notes:
+# $@ means "The file name of the target of the rule"
+# $< means "The name of the first prerequisite"
+# $* means "The stem with which an implicit rule matches"
+# $+ means "The names of all the prerequisites, with spaces between them, exactly as given"
+# For more magic automatic variables, see
+# <http://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html>
+
+#############################################################################
+
+LIBVERS = 1
+
+COREDIR = ../mDNSCore
+SHAREDDIR ?= ../mDNSShared
+JDK = /usr/jdk
+
+CC = @cc
+BISON = @bison
+FLEX = @flex
+LD = ld -shared
+CP = cp
+RM = rm
+LN = ln -s -f
+CFLAGS_COMMON = -I$(COREDIR) -I$(SHAREDDIR) -I$(OBJDIR) -fwrapv -W -Wall -DPID_FILE=\"/var/run/mdnsd.pid\" -DMDNS_UDS_SERVERPATH=\"/var/run/mdnsd\"
+CFLAGS_PTHREAD =
+LINKOPTS =
+LINKOPTS_PTHREAD = -lpthread
+LDSUFFIX = so
+JAVACFLAGS_OS = -fPIC -shared -ldns_sd
+
+# Set up diverging paths for debug vs. prod builds
+DEBUG=0
+ifeq ($(DEBUG),1)
+CFLAGS_DEBUG = -g -DMDNS_DEBUGMSGS=2
+OBJDIR = objects/debug
+BUILDDIR = build/debug
+STRIP = echo
+else
+# We use -Os for two reasons:
+# 1. We want to make small binaries, suitable for putting into hardware devices
+# 2. Some of the code analysis warnings only work when some form of optimization is enabled
+CFLAGS_DEBUG = -Os -DMDNS_DEBUGMSGS=0
+OBJDIR ?= objects/prod
+BUILDDIR ?= build/prod
+STRIP = strip -S
+endif
+
+# Configure per-OS peculiarities
+ifeq ($(os),solaris)
+CFLAGS_DEBUG = -O0 -DMDNS_DEBUGMSGS=0
+CFLAGS_OS = -DNOT_HAVE_DAEMON -DNOT_HAVE_SA_LEN -DNOT_HAVE_SOCKLEN_T -DNOT_HAVE_IF_NAMETOINDEX \
+ -DLOG_PERROR=0 -D_XPG4_2 -D__EXTENSIONS__ -DHAVE_BROKEN_RECVIF_NAME -DTARGET_OS_SOLARIS
+CC = gcc
+LD = gcc -shared
+LINKOPTS = -lsocket -lnsl -lresolv
+JAVACFLAGS_OS += -I$(JDK)/include/solaris
+ifneq ($(DEBUG),1)
+STRIP = strip
+endif
+else
+
+# any target that contains the string "linux"
+ifeq ($(findstring linux,$(os)),linux)
+CFLAGS_OS = -D_GNU_SOURCE -DHAVE_IPV6 -DNOT_HAVE_SA_LEN -DUSES_NETLINK -DHAVE_LINUX -DTARGET_OS_LINUX -fno-strict-aliasing
+LD = gcc -shared
+FLEXFLAGS_OS = -l
+JAVACFLAGS_OS += -I$(JDK)/include/linux
+
+# uClibc does not support Name Service Switch
+ifneq ($(os),linux-uclibc)
+OPTIONALTARG = nss_mdns
+OPTINSTALL = InstalledNSS
+endif
+else
+
+ifeq ($(os),netbsd)
+CFLAGS_OS =
+LDCONFIG = ldconfig
+else
+
+ifeq ($(os),freebsd)
+# If not already defined, set LOCALBASE to /usr/local
+LOCALBASE?=/usr/local
+INSTBASE=$(LOCALBASE)
+CFLAGS_OS = -DHAVE_IPV6
+# FreeBSD 4 requires threaded code to be compiled and linked using the "-pthread" option,
+# and requires that the "-lpthread" link option NOT be used
+# This appies only to FreeBSD -- "man cc" on FreeBSD says:
+# FreeBSD SPECIFIC OPTIONS
+# -pthread
+# Link a user-threaded process against libc_r instead of libc.
+CFLAGS_PTHREAD = -pthread -D_THREAD_SAFE
+LINKOPTS_PTHREAD = -pthread
+JAVACFLAGS_OS += -I$(JDK)/include/freebsd
+LDCONFIG = ldconfig
+else
+
+ifeq ($(os),openbsd)
+CFLAGS_OS = -DHAVE_BROKEN_RECVDSTADDR
+LDCONFIG = ldconfig
+else
+
+ifeq ($(os),x)
+# We have to define __MAC_OS_X_VERSION_MIN_REQUIRED=__MAC_OS_X_VERSION_10_4 or on Leopard
+# we get build failures: ‘daemon’ is deprecated (declared at /usr/include/stdlib.h:283)
+CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -Werror -Wdeclaration-after-statement \
+ -D__MAC_OS_X_VERSION_MIN_REQUIRED=__MAC_OS_X_VERSION_10_4 \
+ -D__APPLE_USE_RFC_2292 #-Wunreachable-code
+CC = gcc
+LD = $(CC) -dynamiclib
+LINKOPTS = -lSystem
+LDSUFFIX = dylib
+JDK = /System/Library/Frameworks/JavaVM.framework/Home
+JAVACFLAGS_OS = -dynamiclib -I/System/Library/Frameworks/JavaVM.framework/Headers -framework JavaVM
+else
+
+$(error ERROR: Must specify target OS on command-line, e.g. "make os=x [target]".\
+Supported operating systems include: x, linux, linux-uclibc, netbsd, freebsd, openbsd, solaris)
+endif
+endif
+endif
+endif
+endif
+endif
+
+NSSLIBNAME := libnss_mdns
+NSSVERSION := 0.2
+NSSLIBFILE := $(NSSLIBNAME)-$(NSSVERSION).so
+NSSLINKNAME := $(NSSLIBNAME).so.2
+NSSINSTPATH := /lib
+
+# If not otherwise defined, we install into /usr/lib and /usr/include
+# and our startup script is called mdns (e.g. /etc/init.d/mdns)
+INSTBASE?=/usr
+STARTUPSCRIPTNAME?=mdns
+
+ifeq ($(HAVE_IPV6),1)
+CFLAGS_OS += -DHAVE_IPV6=1
+else
+ifeq ($(HAVE_IPV6),0)
+CFLAGS_OS += -DHAVE_IPV6=0
+endif
+endif
+
+# If directory /usr/share/man exists, then we install man pages into that, else /usr/man
+ifeq ($(wildcard /usr/share/man), /usr/share/man)
+MANPATH := /usr/share/man
+else
+MANPATH := /usr/man
+endif
+
+# If directories /etc/init.d/rc*.d exist, then we install into that (Suse)
+ifeq ($(wildcard /etc/init.d/rc2.d/), /etc/init.d/rc2.d/)
+STARTUPSCRIPTDIR = /etc/init.d
+RUNLEVELSCRIPTSDIR = /etc/init.d
+else
+# else if directory /etc/rc.d/init.d/ exists, then we install into that (old Linux)
+ifeq ($(wildcard /etc/rc.d/init.d/), /etc/rc.d/init.d/)
+STARTUPSCRIPTDIR = /etc/rc.d/init.d
+RUNLEVELSCRIPTSDIR = /etc/rc.d
+else
+# else if directory /etc/init.d/ exists, then we install into that (new Linux)
+ifeq ($(wildcard /etc/init.d/), /etc/init.d/)
+STARTUPSCRIPTDIR = /etc/init.d
+RUNLEVELSCRIPTSDIR = /etc
+else
+# else install into /etc/rc.d/ (*BSD)
+STARTUPSCRIPTDIR = $(INSTBASE)/etc/rc.d
+endif
+endif
+endif
+
+CFLAGS = $(CFLAGS_COMMON) $(CFLAGS_OS) $(CFLAGS_DEBUG)
+
+#############################################################################
+
+all: setup Daemon libdns_sd Clients SAClient SAResponder SAProxyResponder Identify NetMonitor dnsextd $(OPTIONALTARG)
+
+install: setup InstalledDaemon InstalledStartup InstalledLib InstalledManPages InstalledClients $(OPTINSTALL)
+
+# 'setup' sets up the build directory structure the way we want
+setup:
+ @if test ! -d $(OBJDIR) ; then mkdir -p $(OBJDIR) ; fi
+ @if test ! -d $(BUILDDIR) ; then mkdir -p $(BUILDDIR) ; fi
+
+# clean removes targets and objects
+clean:
+ @if test -d $(OBJDIR) ; then rm -r $(OBJDIR) ; fi
+ @if test -d $(BUILDDIR) ; then rm -r $(BUILDDIR) ; fi
+ @$(MAKE) -C ../Clients clean
+
+#############################################################################
+
+# daemon target builds the daemon
+DAEMONOBJS = $(OBJDIR)/PosixDaemon.c.o $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNS.c.o \
+ $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/uds_daemon.c.o \
+ $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/dnssd_ipc.c.o $(OBJDIR)/GenLinkedList.c.o $(OBJDIR)/PlatformCommon.c.o \
+ $(OBJDIR)/CryptoAlg.c.o $(OBJDIR)/anonymous.c.o
+
+# dnsextd target build dnsextd
+DNSEXTDOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/GenLinkedList.c.o $(OBJDIR)/DNSDigest.c.o \
+ $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/PlatformCommon.c.o $(OBJDIR)/dnsextd_parser.y.o $(OBJDIR)/dnsextd_lexer.l.o \
+ $(OBJDIR)/CryptoAlg.c.o
+
+Daemon: setup $(BUILDDIR)/mdnsd
+ @echo "Responder daemon done"
+
+$(BUILDDIR)/mdnsd: $(DAEMONOBJS)
+ $(CC) -o $@ $+ $(LINKOPTS)
+ @$(STRIP) $@
+
+# libdns_sd target builds the client library
+libdns_sd: setup $(BUILDDIR)/libdns_sd.$(LDSUFFIX)
+ @echo "Client library done"
+
+CLIENTLIBOBJS = $(OBJDIR)/dnssd_clientlib.c.so.o $(OBJDIR)/dnssd_clientstub.c.so.o $(OBJDIR)/dnssd_ipc.c.so.o
+
+$(BUILDDIR)/libdns_sd.$(LDSUFFIX): $(CLIENTLIBOBJS)
+ @$(LD) $(LINKOPTS) -o $@ $+
+ @$(STRIP) $@
+
+Clients: setup libdns_sd ../Clients/build/dns-sd
+ @echo "Clients done"
+
+../Clients/build/dns-sd:
+ @$(MAKE) -C ../Clients
+
+# nss_mdns target builds the Name Service Switch module
+nss_mdns: setup $(BUILDDIR)/$(NSSLIBFILE)
+ @echo "Name Service Switch module done"
+
+$(BUILDDIR)/$(NSSLIBFILE): $(CLIENTLIBOBJS) $(OBJDIR)/nss_mdns.c.so.o
+ @$(LD) $(LINKOPTS) -o $@ $+
+ @$(STRIP) $@
+
+#############################################################################
+
+# The Install targets place built stuff in their proper places
+InstalledDaemon: $(INSTBASE)/sbin/mdnsd
+ @echo $+ " installed"
+
+InstalledLib: $(INSTBASE)/lib/libdns_sd.$(LDSUFFIX).$(LIBVERS) $(INSTBASE)/include/dns_sd.h
+ @echo $+ " installed"
+
+InstalledStartup: $(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME)
+ @echo $+ " installed"
+
+InstalledManPages: $(MANPATH)/man8/mdnsd.8
+ @echo $+ " installed"
+
+InstalledClients: $(INSTBASE)/bin/dns-sd
+ @echo $+ " installed"
+
+InstalledNSS: $(NSSINSTPATH)/$(NSSLINKNAME) /etc/nss_mdns.conf $(MANPATH)/man5/nss_mdns.conf.5 $(MANPATH)/man8/libnss_mdns.8
+ @echo $+ " installed"
+
+# Note: If daemon already installed, we make sure it's stopped before overwriting it
+$(INSTBASE)/sbin/mdnsd: $(BUILDDIR)/mdnsd
+ @if test -x $@; then $(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME) stop; fi
+ $(CP) $< $@
+
+$(INSTBASE)/lib/libdns_sd.$(LDSUFFIX).$(LIBVERS): $(BUILDDIR)/libdns_sd.$(LDSUFFIX)
+ $(CP) $< $@
+ $(LN) $@ $(INSTBASE)/lib/libdns_sd.$(LDSUFFIX)
+ifdef LDCONFIG
+ # -m means 'merge into existing database', -R means 'rescan directories'
+ $(LDCONFIG) -mR
+endif
+
+$(INSTBASE)/include/dns_sd.h: $(SHAREDDIR)/dns_sd.h
+ $(CP) $< $@
+
+# We make this target dependent on $(INSTBASE)/sbin/mdnsd because we need to ensure
+# that the daemon is installed *before* we try to execute the command to start it.
+$(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME): mdnsd.sh $(STARTUPSCRIPTDIR) $(INSTBASE)/sbin/mdnsd
+ $(CP) $< $@
+ chmod ugo+x $@
+ $@ start
+ifdef RUNLEVELSCRIPTSDIR
+ifeq ($(wildcard $(RUNLEVELSCRIPTSDIR)/runlevels/default), $(RUNLEVELSCRIPTSDIR)/runlevels/default)
+ $(LN) $@ $(RUNLEVELSCRIPTSDIR)/runlevels/default/mdns
+else
+ $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc2.d/S52mdns
+ $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc3.d/S52mdns
+ $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc4.d/S52mdns
+ $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc5.d/S52mdns
+ $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc0.d/K16mdns
+ $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc6.d/K16mdns
+endif
+endif
+
+$(MANPATH)/man5/%.5: %.5
+ cp $< $@
+ chmod 444 $@
+
+$(MANPATH)/man8/%.8: %.8
+ cp $< $@
+ chmod 444 $@
+
+$(MANPATH)/man8/mdnsd.8: $(SHAREDDIR)/mDNSResponder.8
+ cp $< $@
+ chmod 444 $@
+
+$(INSTBASE)/bin/dns-sd: ../Clients/build/dns-sd
+ $(CP) $< $@
+
+$(NSSINSTPATH)/$(NSSLINKNAME): $(NSSINSTPATH)/$(NSSLIBFILE)
+ $(LN) $< $@
+ ldconfig
+
+$(NSSINSTPATH)/$(NSSLIBFILE): $(BUILDDIR)/$(NSSLIBFILE)
+ $(CP) $< $@
+ chmod 444 $@
+
+/etc/nss_mdns.conf: nss_mdns.conf
+ $(CP) $< $@
+ chmod 444 $@
+ # Check the nsswitch.conf file.
+ # If 'mdns' does not already appear on the "hosts:" line, then add it right before 'dns'
+ cp -f /etc/nsswitch.conf /etc/nsswitch.conf.pre-mdns
+ sed -e '/mdns/!s/^\(hosts:.*\)dns\(.*\)/\1mdns dns\2/' /etc/nsswitch.conf.pre-mdns > /etc/nsswitch.conf
+
+#############################################################################
+
+# The following targets build Java wrappers for the dns-sd.h API.
+# Note that the JavaForXcode targets are used when building the project for OS X using Xcode
+
+JAVAC = $(JDK)/bin/javac
+JAVAH = $(JDK)/bin/javah
+JAVADOC = $(JDK)/bin/javadoc
+JAR = $(JDK)/bin/jar
+JAVACFLAGS = $(CFLAGS) $(JAVACFLAGS_OS) -I$(JDK)/include
+
+JavaForXcode_: setup $(BUILDDIR)/dns_sd.jar $(PROJECT_DERIVED_FILE_DIR)/DNSSD.java.h
+ @echo $@ done
+
+$(PROJECT_DERIVED_FILE_DIR)/DNSSD.java.h: $(OBJDIR)/DNSSD.java.h
+ @if test ! -d $(PROJECT_DERIVED_FILE_DIR) ; then mkdir -p $(PROJECT_DERIVED_FILE_DIR) ; fi
+ $(CP) $< $@
+
+JavaForXcode_clean:
+ @if test -d $(OBJDIR) ; then rm -r $(OBJDIR) ; fi
+ @if test -f $(PROJECT_DERIVED_FILE_DIR)/DNSSD.java.h ; then $(RM) $(PROJECT_DERIVED_FILE_DIR)/DNSSD.java.h ; fi
+ @if test -f $(BUILDDIR)/dns_sd.jar ; then $(RM) $(BUILDDIR)/dns_sd.jar ; fi
+ @echo $@ done
+
+JavaForXcode_installhdrs:
+ @echo $@ NOOP
+
+JavaForXcode_install: JavaForXcode_ $(DSTROOT)/$(SYSTEM_LIBRARY_DIR)/Java/Extensions/dns_sd.jar
+ @echo $@ done
+
+$(DSTROOT)/$(SYSTEM_LIBRARY_DIR)/Java/Extensions/dns_sd.jar: $(BUILDDIR)/dns_sd.jar
+ @if test ! -d $(DSTROOT)/$(SYSTEM_LIBRARY_DIR)/Java/Extensions ; then mkdir -p $(DSTROOT)/$(SYSTEM_LIBRARY_DIR)/Java/Extensions ; fi
+ $(CP) $< $@
+
+Java: setup $(BUILDDIR)/dns_sd.jar $(BUILDDIR)/libjdns_sd.$(LDSUFFIX)
+ @echo "Java wrappers done"
+
+JAVASRC = $(SHAREDDIR)/Java
+JARCONTENTS = $(OBJDIR)/com/apple/dnssd/DNSSDService.class \
+ $(OBJDIR)/com/apple/dnssd/DNSSDException.class \
+ $(OBJDIR)/com/apple/dnssd/DNSRecord.class \
+ $(OBJDIR)/com/apple/dnssd/TXTRecord.class \
+ $(OBJDIR)/com/apple/dnssd/DNSSDRegistration.class \
+ $(OBJDIR)/com/apple/dnssd/BaseListener.class \
+ $(OBJDIR)/com/apple/dnssd/BrowseListener.class \
+ $(OBJDIR)/com/apple/dnssd/ResolveListener.class \
+ $(OBJDIR)/com/apple/dnssd/RegisterListener.class \
+ $(OBJDIR)/com/apple/dnssd/QueryListener.class \
+ $(OBJDIR)/com/apple/dnssd/DomainListener.class \
+ $(OBJDIR)/com/apple/dnssd/RegisterRecordListener.class \
+ $(OBJDIR)/com/apple/dnssd/DNSSDRecordRegistrar.class \
+ $(OBJDIR)/com/apple/dnssd/DNSSD.class
+
+$(BUILDDIR)/dns_sd.jar: $(JARCONTENTS) setup
+ $(JAR) -cf $@ -C $(OBJDIR) com
+
+$(BUILDDIR)/libjdns_sd.$(LDSUFFIX): $(JAVASRC)/JNISupport.c $(OBJDIR)/DNSSD.java.h setup libdns_sd
+ $(CC) -o $@ $< $(JAVACFLAGS) -I$(OBJDIR) -L$(BUILDDIR)
+
+$(OBJDIR)/com/apple/dnssd/%.class: $(JAVASRC)/%.java
+ $(JAVAC) -d $(OBJDIR) -classpath $(OBJDIR) $<
+
+$(OBJDIR)/DNSSD.java.h: $(OBJDIR)/com/apple/dnssd/DNSSD.class
+ $(JAVAH) -force -classpath $(OBJDIR) -o $@ \
+ com.apple.dnssd.AppleDNSSD \
+ com.apple.dnssd.AppleBrowser \
+ com.apple.dnssd.AppleResolver \
+ com.apple.dnssd.AppleRegistration \
+ com.apple.dnssd.AppleQuery \
+ com.apple.dnssd.AppleDomainEnum \
+ com.apple.dnssd.AppleService \
+ com.apple.dnssd.AppleDNSRecord \
+ com.apple.dnssd.AppleRecordRegistrar
+
+#############################################################################
+
+# The following target builds documentation for the Java wrappers.
+
+JavaDoc: Java setup
+ $(JAVADOC) $(JAVASRC)/*.java -classpath $(OBJDIR) -d $(BUILDDIR) -public
+
+#############################################################################
+
+# The following targets build embedded example programs
+SPECIALOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/GenLinkedList.c.o \
+ $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/PlatformCommon.c.o \
+ $(OBJDIR)/CryptoAlg.c.o $(OBJDIR)/anonymous.c.o
+COMMONOBJ = $(SPECIALOBJ) $(OBJDIR)/mDNS.c.o
+APPOBJ = $(COMMONOBJ) $(OBJDIR)/ExampleClientApp.c.o
+
+SAClient: setup $(BUILDDIR)/mDNSClientPosix
+ @echo "Embedded Standalone Client done"
+
+SAResponder: setup $(BUILDDIR)/mDNSResponderPosix
+ @echo "Embedded Standalone Responder done"
+
+SAProxyResponder: setup $(BUILDDIR)/mDNSProxyResponderPosix
+ @echo "Embedded Standalone ProxyResponder done"
+
+Identify: setup $(BUILDDIR)/mDNSIdentify
+ @echo "Identify done"
+
+NetMonitor: setup $(BUILDDIR)/mDNSNetMonitor
+ @echo "NetMonitor done"
+
+dnsextd: setup $(BUILDDIR)/dnsextd
+ @echo "dnsextd done"
+
+$(BUILDDIR)/mDNSClientPosix: $(APPOBJ) $(OBJDIR)/Client.c.o
+ $(CC) $+ -o $@ $(LINKOPTS)
+
+$(BUILDDIR)/mDNSResponderPosix: $(COMMONOBJ) $(OBJDIR)/Responder.c.o
+ $(CC) $+ -o $@ $(LINKOPTS)
+
+$(BUILDDIR)/mDNSProxyResponderPosix: $(COMMONOBJ) $(OBJDIR)/ProxyResponder.c.o
+ $(CC) $+ -o $@ $(LINKOPTS)
+
+$(BUILDDIR)/mDNSIdentify: $(SPECIALOBJ) $(OBJDIR)/Identify.c.o
+ $(CC) $+ -o $@ $(LINKOPTS)
+
+$(OBJDIR)/Identify.c.o: $(COREDIR)/mDNS.c # Note: Identify.c textually imports mDNS.c
+
+$(BUILDDIR)/mDNSNetMonitor: $(SPECIALOBJ) $(OBJDIR)/NetMonitor.c.o
+ $(CC) $+ -o $@ $(LINKOPTS)
+
+$(OBJDIR)/NetMonitor.c.o: $(COREDIR)/mDNS.c # Note: NetMonitor.c textually imports mDNS.c
+
+$(BUILDDIR)/dnsextd: $(DNSEXTDOBJ) $(OBJDIR)/dnsextd.c.threadsafe.o
+ $(CC) $+ -o $@ $(LINKOPTS) $(LINKOPTS_PTHREAD)
+
+#############################################################################
+
+# Implicit rules
+$(OBJDIR)/%.c.o: %.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+$(OBJDIR)/%.c.o: $(COREDIR)/%.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+$(OBJDIR)/%.c.o: $(SHAREDDIR)/%.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+$(OBJDIR)/%.c.threadsafe.o: %.c
+ $(CC) $(CFLAGS) $(CFLAGS_PTHREAD) -D_REENTRANT -c -o $@ $<
+
+$(OBJDIR)/%.c.threadsafe.o: $(SHAREDDIR)/%.c
+ $(CC) $(CFLAGS) $(CFLAGS_PTHREAD) -D_REENTRANT -c -o $@ $<
+
+$(OBJDIR)/%.c.so.o: %.c
+ $(CC) $(CFLAGS) -c -fPIC -o $@ $<
+
+$(OBJDIR)/%.c.so.o: $(SHAREDDIR)/%.c
+ $(CC) $(CFLAGS) -c -fPIC -o $@ $<
+
+$(OBJDIR)/%.y.o: $(SHAREDDIR)/%.y
+ $(BISON) -o $(OBJDIR)/$*.c -d $<
+ $(CC) $(CFLAGS) -c -o $@ $(OBJDIR)/$*.c
+
+$(OBJDIR)/%.l.o: $(SHAREDDIR)/%.l
+ $(FLEX) $(FLEXFLAGS_OS) -i -o$(OBJDIR)/$*.l.c $<
+ $(CC) $(CFLAGS) -Wno-error -c -o $@ $(OBJDIR)/$*.l.c
diff --git a/mDNSResponder/mDNSPosix/NetMonitor.c b/mDNSResponder/mDNSPosix/NetMonitor.c
new file mode 100644
index 00000000..1354f1f8
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/NetMonitor.c
@@ -0,0 +1,1003 @@
+/* -*- 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.
+ *
+ */
+
+//*************************************************************************************************************
+// Incorporate mDNS.c functionality
+
+// We want to use much of the functionality provided by "mDNS.c",
+// except we'll steal the packets that would be sent to normal mDNSCoreReceive() routine
+#define mDNSCoreReceive __NOT__mDNSCoreReceive__NOT__
+#include "mDNS.c"
+#undef mDNSCoreReceive
+
+//*************************************************************************************************************
+// Headers
+
+#include <stdio.h> // For printf()
+#include <stdlib.h> // For malloc()
+#include <string.h> // For strrchr(), strcmp()
+#include <time.h> // For "struct tm" etc.
+#include <signal.h> // For SIGINT, SIGTERM
+#if defined(WIN32)
+// Both mDNS.c and mDNSWin32.h declare UDPSocket_struct type resulting in a compile-time error, so
+// trick the compiler when including mDNSWin32.h
+# define UDPSocket_struct _UDPSocket_struct
+# include <mDNSEmbeddedAPI.h>
+# include <mDNSWin32.h>
+# include <PosixCompat.h>
+# include <Poll.h>
+# define IFNAMSIZ 256
+static HANDLE gStopEvent = INVALID_HANDLE_VALUE;
+static mDNSBool gRunning;
+static void CALLBACK StopNotification( HANDLE event, void *context ) { gRunning = mDNSfalse; }
+static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent ) { SetEvent( gStopEvent ); return TRUE; }
+void setlinebuf( FILE * fp ) {}
+#else
+# include <netdb.h> // For gethostbyname()
+# include <sys/socket.h> // For AF_INET, AF_INET6, etc.
+# include <net/if.h> // For IF_NAMESIZE
+# include <netinet/in.h> // For INADDR_NONE
+# include <arpa/inet.h> // For inet_addr()
+# include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
+#endif
+#include "ExampleClientApp.h"
+
+//*************************************************************************************************************
+// Types and structures
+
+enum
+{
+ // Primitive operations
+ OP_probe = 0,
+ OP_goodbye = 1,
+
+ // These are meta-categories;
+ // Query and Answer operations are actually subdivided into two classes:
+ // Browse query/answer and
+ // Resolve query/answer
+ OP_query = 2,
+ OP_answer = 3,
+
+ // The "Browse" variants of query/answer
+ OP_browsegroup = 2,
+ OP_browseq = 2,
+ OP_browsea = 3,
+
+ // The "Resolve" variants of query/answer
+ OP_resolvegroup = 4,
+ OP_resolveq = 4,
+ OP_resolvea = 5,
+
+ OP_NumTypes = 6
+};
+
+typedef struct ActivityStat_struct ActivityStat;
+struct ActivityStat_struct
+{
+ ActivityStat *next;
+ domainname srvtype;
+ int printed;
+ int totalops;
+ int stat[OP_NumTypes];
+};
+
+typedef struct FilterList_struct FilterList;
+struct FilterList_struct
+{
+ FilterList *next;
+ mDNSAddr FilterAddr;
+};
+
+//*************************************************************************************************************
+// Constants
+
+#define kReportTopServices 15
+#define kReportTopHosts 15
+
+//*************************************************************************************************************
+// Globals
+
+mDNS mDNSStorage; // mDNS core uses this to store its globals
+static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
+mDNSexport const char ProgramName[] = "mDNSNetMonitor";
+
+struct timeval tv_start, tv_end, tv_interval;
+static int FilterInterface = 0;
+static FilterList *Filters;
+#define ExactlyOneFilter (Filters && !Filters->next)
+
+static int NumPktQ, NumPktL, NumPktR, NumPktB; // Query/Legacy/Response/Bad
+static int NumProbes, NumGoodbyes, NumQuestions, NumLegacy, NumAnswers, NumAdditionals;
+
+static ActivityStat *stats;
+
+#define OPBanner "Total Ops Probe Goodbye BrowseQ BrowseA ResolveQ ResolveA"
+
+//*************************************************************************************************************
+// Utilities
+
+// Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
+mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
+mDNSlocal mDNSu32 mprintf(const char *format, ...)
+{
+ mDNSu32 length;
+ unsigned char buffer[512];
+ va_list ptr;
+ va_start(ptr,format);
+ length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
+ va_end(ptr);
+ printf("%s", buffer);
+ return(length);
+}
+
+//*************************************************************************************************************
+// Host Address List
+//
+// Would benefit from a hash
+
+typedef enum
+{
+ HostPkt_Q = 0, // Query
+ HostPkt_L = 1, // Legacy Query
+ HostPkt_R = 2, // Response
+ HostPkt_B = 3, // Bad
+ HostPkt_NumTypes = 4
+} HostPkt_Type;
+
+typedef struct
+{
+ mDNSAddr addr;
+ unsigned long pkts[HostPkt_NumTypes];
+ unsigned long totalops;
+ unsigned long stat[OP_NumTypes];
+ domainname hostname;
+ domainname revname;
+ UTF8str255 HIHardware;
+ UTF8str255 HISoftware;
+ mDNSu32 NumQueries;
+ mDNSs32 LastQuery;
+} HostEntry;
+
+#define HostEntryTotalPackets(H) ((H)->pkts[HostPkt_Q] + (H)->pkts[HostPkt_L] + (H)->pkts[HostPkt_R] + (H)->pkts[HostPkt_B])
+
+typedef struct
+{
+ long num;
+ long max;
+ HostEntry *hosts;
+} HostList;
+
+static HostList IPv4HostList = { 0, 0, 0 };
+static HostList IPv6HostList = { 0, 0, 0 };
+
+mDNSlocal HostEntry *FindHost(const mDNSAddr *addr, HostList *list)
+{
+ long i;
+
+ for (i = 0; i < list->num; i++)
+ {
+ HostEntry *entry = list->hosts + i;
+ if (mDNSSameAddress(addr, &entry->addr))
+ return entry;
+ }
+
+ return NULL;
+}
+
+mDNSlocal HostEntry *AddHost(const mDNSAddr *addr, HostList *list)
+{
+ int i;
+ HostEntry *entry;
+ if (list->num >= list->max)
+ {
+ long newMax = list->max + 64;
+ HostEntry *newHosts = realloc(list->hosts, newMax * sizeof(HostEntry));
+ if (newHosts == NULL)
+ return NULL;
+ list->max = newMax;
+ list->hosts = newHosts;
+ }
+
+ entry = list->hosts + list->num++;
+
+ entry->addr = *addr;
+ for (i=0; i<HostPkt_NumTypes; i++) entry->pkts[i] = 0;
+ entry->totalops = 0;
+ for (i=0; i<OP_NumTypes; i++) entry->stat[i] = 0;
+ entry->hostname.c[0] = 0;
+ entry->revname.c[0] = 0;
+ entry->HIHardware.c[0] = 0;
+ entry->HISoftware.c[0] = 0;
+ entry->NumQueries = 0;
+
+ if (entry->addr.type == mDNSAddrType_IPv4)
+ {
+ mDNSv4Addr ip = entry->addr.ip.v4;
+ char buffer[32];
+ // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
+ mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", ip.b[3], ip.b[2], ip.b[1], ip.b[0]);
+ MakeDomainNameFromDNSNameString(&entry->revname, buffer);
+ }
+
+ return(entry);
+}
+
+mDNSlocal HostEntry *GotPacketFromHost(const mDNSAddr *addr, HostPkt_Type t, mDNSOpaque16 id)
+{
+ if (ExactlyOneFilter) return(NULL);
+ else
+ {
+ HostList *list = (addr->type == mDNSAddrType_IPv4) ? &IPv4HostList : &IPv6HostList;
+ HostEntry *entry = FindHost(addr, list);
+ if (!entry) entry = AddHost(addr, list);
+ if (!entry) return(NULL);
+ // Don't count our own interrogation packets
+ if (id.NotAnInteger != 0xFFFF) entry->pkts[t]++;
+ return(entry);
+ }
+}
+
+mDNSlocal void RecordHostInfo(HostEntry *entry, const ResourceRecord *const pktrr)
+{
+ if (!entry->hostname.c[0])
+ {
+ if (pktrr->rrtype == kDNSType_A || pktrr->rrtype == kDNSType_AAAA)
+ {
+ // Should really check that the rdata in the address record matches the source address of this packet
+ entry->NumQueries = 0;
+ AssignDomainName(&entry->hostname, pktrr->name);
+ }
+
+ if (pktrr->rrtype == kDNSType_PTR)
+ if (SameDomainName(&entry->revname, pktrr->name))
+ {
+ entry->NumQueries = 0;
+ AssignDomainName(&entry->hostname, &pktrr->rdata->u.name);
+ }
+ }
+ else if (pktrr->rrtype == kDNSType_HINFO)
+ {
+ RDataBody *rd = &pktrr->rdata->u;
+ mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength;
+ mDNSu8 *hw = rd->txt.c;
+ mDNSu8 *sw = hw + 1 + (mDNSu32)hw[0];
+ if (sw + 1 + sw[0] <= rdend)
+ {
+ AssignDomainName(&entry->hostname, pktrr->name);
+ mDNSPlatformMemCopy(entry->HIHardware.c, hw, 1 + (mDNSu32)hw[0]);
+ mDNSPlatformMemCopy(entry->HISoftware.c, sw, 1 + (mDNSu32)sw[0]);
+ }
+ }
+}
+
+mDNSlocal void SendUnicastQuery(mDNS *const m, HostEntry *entry, domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID)
+{
+ const mDNSOpaque16 id = { { 0xFF, 0xFF } };
+ DNSMessage query;
+ mDNSu8 *qptr = query.data;
+ const mDNSu8 *const limit = query.data + sizeof(query.data);
+ const mDNSAddr *target = &entry->addr;
+ InitializeDNSMessage(&query.h, id, QueryFlags);
+ qptr = putQuestion(&query, qptr, limit, name, rrtype, kDNSClass_IN);
+ entry->LastQuery = m->timenow;
+ entry->NumQueries++;
+
+ // Note: When there are multiple mDNSResponder agents running on a single machine
+ // (e.g. Apple mDNSResponder plus a SliMP3 server with embedded mDNSResponder)
+ // it is possible that unicast queries may not go to the primary system responder.
+ // We try the first query using unicast, but if that doesn't work we try again via multicast.
+ if (entry->NumQueries > 2)
+ {
+ target = &AllDNSLinkGroup_v4;
+ }
+ else
+ {
+ //mprintf("%#a Q\n", target);
+ InterfaceID = mDNSInterface_Any; // Send query from our unicast reply socket
+ }
+
+ mDNSSendDNSMessage(&mDNSStorage, &query, qptr, InterfaceID, mDNSNULL, target, MulticastDNSPort, mDNSNULL, mDNSNULL, mDNSfalse);
+}
+
+mDNSlocal void AnalyseHost(mDNS *const m, HostEntry *entry, const mDNSInterfaceID InterfaceID)
+{
+ // If we've done four queries without answer, give up
+ if (entry->NumQueries >= 4) return;
+
+ // If we've done a query in the last second, give the host a chance to reply before trying again
+ if (entry->NumQueries && m->timenow - entry->LastQuery < mDNSPlatformOneSecond) return;
+
+ // If we don't know the host name, try to find that first
+ if (!entry->hostname.c[0])
+ {
+ if (entry->revname.c[0])
+ {
+ SendUnicastQuery(m, entry, &entry->revname, kDNSType_PTR, InterfaceID);
+ //mprintf("%##s PTR %d\n", entry->revname.c, entry->NumQueries);
+ }
+ }
+ // If we have the host name but no HINFO, now ask for that
+ else if (!entry->HIHardware.c[0])
+ {
+ SendUnicastQuery(m, entry, &entry->hostname, kDNSType_HINFO, InterfaceID);
+ //mprintf("%##s HINFO %d\n", entry->hostname.c, entry->NumQueries);
+ }
+}
+
+mDNSlocal int CompareHosts(const void *p1, const void *p2)
+{
+ return (int)(HostEntryTotalPackets((HostEntry *)p2) - HostEntryTotalPackets((HostEntry *)p1));
+}
+
+mDNSlocal void ShowSortedHostList(HostList *list, int max)
+{
+ HostEntry *e, *end = &list->hosts[(max < list->num) ? max : list->num];
+ qsort(list->hosts, list->num, sizeof(HostEntry), CompareHosts);
+ if (list->num) mprintf("\n%-25s%s%s\n", "Source Address", OPBanner, " Pkts Query LegacyQ Response");
+ for (e = &list->hosts[0]; e < end; e++)
+ {
+ int len = mprintf("%#-25a", &e->addr);
+ if (len > 25) mprintf("\n%25s", "");
+ mprintf("%8lu %8lu %8lu %8lu %8lu %8lu %8lu", e->totalops,
+ e->stat[OP_probe], e->stat[OP_goodbye],
+ e->stat[OP_browseq], e->stat[OP_browsea],
+ e->stat[OP_resolveq], e->stat[OP_resolvea]);
+ mprintf(" %8lu %8lu %8lu %8lu",
+ HostEntryTotalPackets(e), e->pkts[HostPkt_Q], e->pkts[HostPkt_L], e->pkts[HostPkt_R]);
+ if (e->pkts[HostPkt_B]) mprintf("Bad: %8lu", e->pkts[HostPkt_B]);
+ mprintf("\n");
+ if (!e->HISoftware.c[0] && e->NumQueries > 2)
+ mDNSPlatformMemCopy(&e->HISoftware, "\x27*** Unknown (Jaguar, Windows, etc.) ***", 0x28);
+ if (e->hostname.c[0] || e->HIHardware.c[0] || e->HISoftware.c[0])
+ mprintf("%##-45s %#-14s %#s\n", e->hostname.c, e->HIHardware.c, e->HISoftware.c);
+ }
+}
+
+//*************************************************************************************************************
+// Receive and process packets
+
+mDNSexport mDNSBool ExtractServiceType(const domainname *const fqdn, domainname *const srvtype)
+{
+ int i, len;
+ const mDNSu8 *src = fqdn->c;
+ mDNSu8 *dst = srvtype->c;
+
+ len = *src;
+ if (len == 0 || len >= 0x40) return(mDNSfalse);
+ if (src[1] != '_') src += 1 + len;
+
+ len = *src;
+ if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
+ for (i=0; i<=len; i++) *dst++ = *src++;
+
+ len = *src;
+ if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
+ for (i=0; i<=len; i++) *dst++ = *src++;
+
+ *dst++ = 0; // Put the null root label on the end of the service type
+
+ return(mDNStrue);
+}
+
+mDNSlocal void recordstat(HostEntry *entry, const domainname *fqdn, int op, mDNSu16 rrtype)
+{
+ ActivityStat **s = &stats;
+ domainname srvtype;
+
+ if (op != OP_probe)
+ {
+ if (rrtype == kDNSType_SRV || rrtype == kDNSType_TXT) op = op - OP_browsegroup + OP_resolvegroup;
+ else if (rrtype != kDNSType_PTR) return;
+ }
+
+ if (!ExtractServiceType(fqdn, &srvtype)) return;
+
+ while (*s && !SameDomainName(&(*s)->srvtype, &srvtype)) s = &(*s)->next;
+ if (!*s)
+ {
+ int i;
+ *s = malloc(sizeof(ActivityStat));
+ if (!*s) exit(-1);
+ (*s)->next = NULL;
+ (*s)->srvtype = srvtype;
+ (*s)->printed = 0;
+ (*s)->totalops = 0;
+ for (i=0; i<OP_NumTypes; i++) (*s)->stat[i] = 0;
+ }
+
+ (*s)->totalops++;
+ (*s)->stat[op]++;
+ if (entry)
+ {
+ entry->totalops++;
+ entry->stat[op]++;
+ }
+}
+
+mDNSlocal void printstats(int max)
+{
+ int i;
+ if (!stats) return;
+ for (i=0; i<max; i++)
+ {
+ int max = 0;
+ ActivityStat *s, *m = NULL;
+ for (s = stats; s; s=s->next)
+ if (!s->printed && max < s->totalops)
+ { m = s; max = s->totalops; }
+ if (!m) return;
+ m->printed = mDNStrue;
+ if (i==0) mprintf("%-25s%s\n", "Service Type", OPBanner);
+ mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", m->srvtype.c, m->totalops, m->stat[OP_probe],
+ m->stat[OP_goodbye], m->stat[OP_browseq], m->stat[OP_browsea], m->stat[OP_resolveq], m->stat[OP_resolvea]);
+ }
+}
+
+mDNSlocal const mDNSu8 *FindUpdate(mDNS *const m, const DNSMessage *const query, const mDNSu8 *ptr, const mDNSu8 *const end,
+ DNSQuestion *q, LargeCacheRecord *pkt)
+{
+ int i;
+ for (i = 0; i < query->h.numAuthorities; i++)
+ {
+ const mDNSu8 *p2 = ptr;
+ ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, pkt);
+ if (!ptr) break;
+ if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && ResourceRecordAnswersQuestion(&pkt->r.resrec, q)) return(p2);
+ }
+ return(mDNSNULL);
+}
+
+mDNSlocal void DisplayPacketHeader(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID)
+{
+ const char *const ptype = (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "-R- " :
+ (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-";
+
+ struct timeval tv;
+ struct tm tm;
+ const mDNSu32 index = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNSfalse);
+ char if_name[IFNAMSIZ]; // Older Linux distributions don't define IF_NAMESIZE
+ if_indextoname(index, if_name);
+ gettimeofday(&tv, NULL);
+ localtime_r((time_t*)&tv.tv_sec, &tm);
+ mprintf("\n%d:%02d:%02d.%06d Interface %d/%s\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec, index, if_name);
+
+ mprintf("%#-16a %s Q:%3d Ans:%3d Auth:%3d Add:%3d Size:%5d bytes",
+ srcaddr, ptype, msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals, end - (mDNSu8 *)msg);
+
+ if (msg->h.id.NotAnInteger) mprintf(" ID:%u", mDNSVal16(msg->h.id));
+
+ if (!mDNSAddrIsDNSMulticast(dstaddr)) mprintf(" To: %#a", dstaddr);
+
+ if (msg->h.flags.b[0] & kDNSFlag0_TC)
+ {
+ if (msg->h.flags.b[0] & kDNSFlag0_QR_Response) mprintf(" Truncated");
+ else mprintf(" Truncated (KA list continues in next packet)");
+ }
+ mprintf("\n");
+}
+
+mDNSlocal void DisplayResourceRecord(const mDNSAddr *const srcaddr, const char *const op, const ResourceRecord *const pktrr)
+{
+ static const char hexchars[16] = "0123456789ABCDEF";
+ #define MaxWidth 132
+ char buffer[MaxWidth+8];
+ char *p = buffer;
+
+ RDataBody *rd = &pktrr->rdata->u;
+ mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength;
+ int n = mprintf("%#-16a %-5s %-5s%5lu %##s -> ", srcaddr, op, DNSTypeName(pktrr->rrtype), pktrr->rroriginalttl, pktrr->name->c);
+
+ if (pktrr->RecordType == kDNSRecordTypePacketNegative) { mprintf("**** ERROR: FAILED TO READ RDATA ****\n"); return; }
+
+ // The kDNSType_OPT case below just calls GetRRDisplayString_rdb
+ // Perhaps more (or all?) of the cases should do that?
+ switch(pktrr->rrtype)
+ {
+ case kDNSType_A: n += mprintf("%.4a", &rd->ipv4); break;
+ case kDNSType_PTR: n += mprintf("%##.*s", MaxWidth - n, rd->name.c); break;
+ case kDNSType_HINFO: // same as kDNSType_TXT below
+ case kDNSType_TXT: {
+ mDNSu8 *t = rd->txt.c;
+ while (t < rdend && t[0] && p < buffer+MaxWidth)
+ {
+ int i;
+ for (i=1; i<=t[0] && p < buffer+MaxWidth; i++)
+ {
+ if (t[i] == '\\') *p++ = '\\';
+ if (t[i] >= ' ') *p++ = t[i];
+ else
+ {
+ *p++ = '\\';
+ *p++ = '0';
+ *p++ = 'x';
+ *p++ = hexchars[t[i] >> 4];
+ *p++ = hexchars[t[i] & 0xF];
+ }
+ }
+ t += 1+t[0];
+ if (t < rdend && t[0]) { *p++ = '\\'; *p++ = ' '; }
+ }
+ *p++ = 0;
+ n += mprintf("%.*s", MaxWidth - n, buffer);
+ } break;
+ case kDNSType_AAAA: n += mprintf("%.16a", &rd->ipv6); break;
+ case kDNSType_SRV: n += mprintf("%##s:%d", rd->srv.target.c, mDNSVal16(rd->srv.port)); break;
+ case kDNSType_OPT: {
+ char b[MaxMsg];
+ // Quick hack: we don't want the prefix that GetRRDisplayString_rdb puts at the start of its
+ // string, because it duplicates the name and rrtype we already display, so we compute the
+ // length of that prefix and strip that many bytes off the beginning of the string we display.
+ mDNSu32 striplen = mDNS_snprintf(b, MaxMsg-1, "%4d %##s %s ", pktrr->rdlength, pktrr->name->c, DNSTypeName(pktrr->rrtype));
+ GetRRDisplayString_rdb(pktrr, &pktrr->rdata->u, b);
+ n += mprintf("%.*s", MaxWidth - n, b + striplen);
+ } break;
+ case kDNSType_NSEC: {
+ char b[MaxMsg];
+ // See the quick hack above
+ mDNSu32 striplen = mDNS_snprintf(b, MaxMsg-1, "%4d %##s %s ", pktrr->rdlength, pktrr->name->c, DNSTypeName(pktrr->rrtype));
+ GetRRDisplayString_rdb(pktrr, &pktrr->rdata->u, b);
+ n += mprintf("%s", b+striplen);
+ } break;
+ default: {
+ mDNSu8 *s = rd->data;
+ while (s < rdend && p < buffer+MaxWidth)
+ {
+ if (*s == '\\') *p++ = '\\';
+ if (*s >= ' ') *p++ = *s;
+ else
+ {
+ *p++ = '\\';
+ *p++ = '0';
+ *p++ = 'x';
+ *p++ = hexchars[*s >> 4];
+ *p++ = hexchars[*s & 0xF];
+ }
+ s++;
+ }
+ *p++ = 0;
+ n += mprintf("%.*s", MaxWidth - n, buffer);
+ } break;
+ }
+
+ mprintf("\n");
+}
+
+mDNSlocal void HexDump(const mDNSu8 *ptr, const mDNSu8 *const end)
+{
+ while (ptr < end)
+ {
+ int i;
+ for (i=0; i<16; i++)
+ if (&ptr[i] < end) mprintf("%02X ", ptr[i]);
+ else mprintf(" ");
+ for (i=0; i<16; i++)
+ if (&ptr[i] < end) mprintf("%c", ptr[i] <= ' ' || ptr[i] >= 126 ? '.' : ptr[i]);
+ ptr += 16;
+ mprintf("\n");
+ }
+}
+
+mDNSlocal void DisplayError(const mDNSAddr *srcaddr, const mDNSu8 *ptr, const mDNSu8 *const end, char *msg)
+{
+ mprintf("%#-16a **** ERROR: FAILED TO READ %s **** \n", srcaddr, msg);
+ HexDump(ptr, end);
+}
+
+mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
+ const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID)
+{
+ int i;
+ const mDNSu8 *ptr = msg->data;
+ const mDNSu8 *auth = LocateAuthorities(msg, end);
+ mDNSBool MQ = (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger);
+ HostEntry *entry = GotPacketFromHost(srcaddr, MQ ? HostPkt_Q : HostPkt_L, msg->h.id);
+ LargeCacheRecord pkt;
+
+ DisplayPacketHeader(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
+ if (msg->h.id.NotAnInteger != 0xFFFF)
+ {
+ if (MQ) NumPktQ++;else NumPktL++;
+ }
+
+ for (i=0; i<msg->h.numQuestions; i++)
+ {
+ DNSQuestion q;
+ mDNSu8 *p2 = (mDNSu8 *)getQuestion(msg, ptr, end, InterfaceID, &q);
+ mDNSu16 ucbit = q.qclass & kDNSQClass_UnicastResponse;
+ q.qclass &= ~kDNSQClass_UnicastResponse;
+ if (!p2) { DisplayError(srcaddr, ptr, end, "QUESTION"); return; }
+ ptr = p2;
+ p2 = (mDNSu8 *)FindUpdate(m, msg, auth, end, &q, &pkt);
+ if (p2)
+ {
+ NumProbes++;
+ DisplayResourceRecord(srcaddr, ucbit ? "(PU)" : "(PM)", &pkt.r.resrec);
+ recordstat(entry, &q.qname, OP_probe, q.qtype);
+ p2 = (mDNSu8 *)skipDomainName(msg, p2, end);
+ // Having displayed this update record, clear type and class so we don't display the same one again.
+ p2[0] = p2[1] = p2[2] = p2[3] = 0;
+ }
+ else
+ {
+ const char *ptype = ucbit ? "(QU)" : "(QM)";
+ if (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) NumQuestions++;
+ else { NumLegacy++; ptype = "(LQ)"; }
+ mprintf("%#-16a %-5s %-5s %##s\n", srcaddr, ptype, DNSTypeName(q.qtype), q.qname.c);
+ if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, &q.qname, OP_query, q.qtype);
+ }
+ }
+
+ for (i=0; i<msg->h.numAnswers; i++)
+ {
+ const mDNSu8 *ep = ptr;
+ ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt);
+ if (!ptr) { DisplayError(srcaddr, ep, end, "KNOWN ANSWER"); return; }
+ DisplayResourceRecord(srcaddr, "(KA)", &pkt.r.resrec);
+
+ // In the case of queries with long multi-packet KA lists, we count each subsequent KA packet
+ // the same as a single query, to more accurately reflect the burden on the network
+ // (A query with a six-packet KA list is *at least* six times the burden on the network as a single-packet query.)
+ if (msg->h.numQuestions == 0 && i == 0)
+ recordstat(entry, pkt.r.resrec.name, OP_query, pkt.r.resrec.rrtype);
+ }
+
+ for (i=0; i<msg->h.numAuthorities; i++)
+ {
+ const mDNSu8 *ep = ptr;
+ ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt);
+ if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
+ // After we display an Update record with its matching question (above) we zero out its type and class
+ // If any remain that haven't been zero'd out, display them here
+ if (pkt.r.resrec.rrtype || pkt.r.resrec.rrclass) DisplayResourceRecord(srcaddr, "(AU)", &pkt.r.resrec);
+ }
+
+ for (i=0; i<msg->h.numAdditionals; i++)
+ {
+ const mDNSu8 *ep = ptr;
+ ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &pkt);
+ if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; }
+ DisplayResourceRecord(srcaddr, pkt.r.resrec.rrtype == kDNSType_OPT ? "(OP)" : "(AD)", &pkt.r.resrec);
+ }
+
+ if (entry) AnalyseHost(m, entry, InterfaceID);
+}
+
+mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end,
+ const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID)
+{
+ int i;
+ const mDNSu8 *ptr = msg->data;
+ HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id);
+ LargeCacheRecord pkt;
+
+ DisplayPacketHeader(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
+ if (msg->h.id.NotAnInteger != 0xFFFF) NumPktR++;
+
+ for (i=0; i<msg->h.numQuestions; i++)
+ {
+ DNSQuestion q;
+ const mDNSu8 *ep = ptr;
+ ptr = getQuestion(msg, ptr, end, InterfaceID, &q);
+ if (!ptr) { DisplayError(srcaddr, ep, end, "QUESTION"); return; }
+ if (mDNSAddrIsDNSMulticast(dstaddr))
+ mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE Q IN mDNS RESPONSE **** %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c);
+ else
+ mprintf("%#-16a (Q) %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c);
+ }
+
+ for (i=0; i<msg->h.numAnswers; i++)
+ {
+ const mDNSu8 *ep = ptr;
+ ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt);
+ if (!ptr) { DisplayError(srcaddr, ep, end, "ANSWER"); return; }
+ if (pkt.r.resrec.rroriginalttl)
+ {
+ NumAnswers++;
+ DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AN)" : "(AN+)", &pkt.r.resrec);
+ if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, pkt.r.resrec.name, OP_answer, pkt.r.resrec.rrtype);
+ if (entry) RecordHostInfo(entry, &pkt.r.resrec);
+ }
+ else
+ {
+ NumGoodbyes++;
+ DisplayResourceRecord(srcaddr, "(DE)", &pkt.r.resrec);
+ recordstat(entry, pkt.r.resrec.name, OP_goodbye, pkt.r.resrec.rrtype);
+ }
+ }
+
+ for (i=0; i<msg->h.numAuthorities; i++)
+ {
+ const mDNSu8 *ep = ptr;
+ ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt);
+ if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
+ if (pkt.r.resrec.rrtype != kDNSType_NSEC3)
+ mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n",
+ srcaddr, DNSTypeName(pkt.r.resrec.rrtype), pkt.r.resrec.name->c);
+ }
+
+ for (i=0; i<msg->h.numAdditionals; i++)
+ {
+ const mDNSu8 *ep = ptr;
+ ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &pkt);
+ if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; }
+ NumAdditionals++;
+ DisplayResourceRecord(srcaddr,
+ pkt.r.resrec.rrtype == kDNSType_OPT ? "(OP)" : (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AD)" : "(AD+)",
+ &pkt.r.resrec);
+ if (entry) RecordHostInfo(entry, &pkt.r.resrec);
+ }
+
+ if (entry) AnalyseHost(m, entry, InterfaceID);
+}
+
+mDNSlocal void ProcessUnicastResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID)
+{
+ int i;
+ const mDNSu8 *ptr = LocateAnswers(msg, end);
+ HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id);
+ //mprintf("%#a R\n", srcaddr);
+
+ for (i=0; i<msg->h.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals; i++)
+ {
+ LargeCacheRecord pkt;
+ ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt);
+ if (ptr && pkt.r.resrec.rroriginalttl && entry) RecordHostInfo(entry, &pkt.r.resrec);
+ }
+}
+
+mDNSlocal mDNSBool AddressMatchesFilterList(const mDNSAddr *srcaddr)
+{
+ FilterList *f;
+ if (!Filters) return(srcaddr->type == mDNSAddrType_IPv4);
+ for (f=Filters; f; f=f->next) if (mDNSSameAddress(srcaddr, &f->FilterAddr)) return(mDNStrue);
+ return(mDNSfalse);
+}
+
+mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
+ const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID)
+{
+ const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery;
+ const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
+ const mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
+ mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions;
+ int goodinterface = (FilterInterface == 0);
+
+ (void)dstaddr; // Unused
+ (void)dstport; // Unused
+
+ // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
+ msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
+ msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
+ msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
+ msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]);
+
+ // For now we're only interested in monitoring IPv4 traffic.
+ // All IPv6 packets should just be duplicates of the v4 packets.
+ if (!goodinterface) goodinterface = (FilterInterface == (int)mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNSfalse));
+ if (goodinterface && AddressMatchesFilterList(srcaddr))
+ {
+ mDNS_Lock(m);
+ if (!mDNSAddrIsDNSMulticast(dstaddr))
+ {
+ if (QR_OP == StdQ) mprintf("Unicast query from %#a\n", srcaddr);
+ else if (QR_OP == StdR) ProcessUnicastResponse(m, msg, end, srcaddr, InterfaceID);
+ }
+ else
+ {
+ if (QR_OP == StdQ) DisplayQuery (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
+ else if (QR_OP == StdR) DisplayResponse (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
+ else
+ {
+ debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]);
+ GotPacketFromHost(srcaddr, HostPkt_B, msg->h.id);
+ NumPktB++;
+ }
+ }
+ mDNS_Unlock(m);
+ }
+}
+
+mDNSlocal mStatus mDNSNetMonitor(void)
+{
+ struct tm tm;
+ int h, m, s, mul, div, TotPkt;
+#if !defined(WIN32)
+ sigset_t signals;
+#endif
+
+ mStatus status = mDNS_Init(&mDNSStorage, &PlatformStorage,
+ mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
+ mDNS_Init_DontAdvertiseLocalAddresses,
+ mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+ if (status) return(status);
+
+ gettimeofday(&tv_start, NULL);
+
+#if defined( WIN32 )
+ status = SetupInterfaceList(&mDNSStorage);
+ if (status) return(status);
+ gStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (gStopEvent == INVALID_HANDLE_VALUE) return mStatus_UnknownErr;
+ mDNSPollRegisterEvent( gStopEvent, StopNotification, NULL );
+ if (!SetConsoleCtrlHandler(ConsoleControlHandler, TRUE)) return mStatus_UnknownErr;
+ gRunning = mDNStrue; while (gRunning) mDNSPoll( INFINITE );
+ if (!SetConsoleCtrlHandler(ConsoleControlHandler, FALSE)) return mStatus_UnknownErr;
+ CloseHandle(gStopEvent);
+#else
+ mDNSPosixListenForSignalInEventLoop(SIGINT);
+ mDNSPosixListenForSignalInEventLoop(SIGTERM);
+
+ do
+ {
+ struct timeval timeout = { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM
+ mDNSBool gotSomething;
+ mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething);
+ }
+ while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM)));
+#endif
+
+ // Now display final summary
+ TotPkt = NumPktQ + NumPktL + NumPktR;
+ gettimeofday(&tv_end, NULL);
+ tv_interval = tv_end;
+ if (tv_start.tv_usec > tv_interval.tv_usec)
+ { tv_interval.tv_usec += 1000000; tv_interval.tv_sec--; }
+ tv_interval.tv_sec -= tv_start.tv_sec;
+ tv_interval.tv_usec -= tv_start.tv_usec;
+ h = (tv_interval.tv_sec / 3600);
+ m = (tv_interval.tv_sec % 3600) / 60;
+ s = (tv_interval.tv_sec % 60);
+ if (tv_interval.tv_sec > 10)
+ {
+ mul = 60;
+ div = tv_interval.tv_sec;
+ }
+ else
+ {
+ mul = 60000;
+ div = tv_interval.tv_sec * 1000 + tv_interval.tv_usec / 1000;
+ if (div == 0) div=1;
+ }
+
+ mprintf("\n\n");
+ localtime_r((time_t*)&tv_start.tv_sec, &tm);
+ mprintf("Started %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_start.tv_usec);
+ localtime_r((time_t*)&tv_end.tv_sec, &tm);
+ mprintf("End %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_end.tv_usec);
+ mprintf("Captured for %3d:%02d:%02d.%06d\n", h, m, s, tv_interval.tv_usec);
+ if (!Filters)
+ {
+ mprintf("Unique source addresses seen on network:");
+ if (IPv4HostList.num) mprintf(" %ld (IPv4)", IPv4HostList.num);
+ if (IPv6HostList.num) mprintf(" %ld (IPv6)", IPv6HostList.num);
+ if (!IPv4HostList.num && !IPv6HostList.num) mprintf(" None");
+ mprintf("\n");
+ }
+ mprintf("\n");
+ mprintf("Modern Query Packets: %7d (avg%5d/min)\n", NumPktQ, NumPktQ * mul / div);
+ mprintf("Legacy Query Packets: %7d (avg%5d/min)\n", NumPktL, NumPktL * mul / div);
+ mprintf("Multicast Response Packets: %7d (avg%5d/min)\n", NumPktR, NumPktR * mul / div);
+ mprintf("Total Multicast Packets: %7d (avg%5d/min)\n", TotPkt, TotPkt * mul / div);
+ mprintf("\n");
+ mprintf("Total New Service Probes: %7d (avg%5d/min)\n", NumProbes, NumProbes * mul / div);
+ mprintf("Total Goodbye Announcements: %7d (avg%5d/min)\n", NumGoodbyes, NumGoodbyes * mul / div);
+ mprintf("Total Query Questions: %7d (avg%5d/min)\n", NumQuestions, NumQuestions * mul / div);
+ mprintf("Total Queries from Legacy Clients:%7d (avg%5d/min)\n", NumLegacy, NumLegacy * mul / div);
+ mprintf("Total Answers/Announcements: %7d (avg%5d/min)\n", NumAnswers, NumAnswers * mul / div);
+ mprintf("Total Additional Records: %7d (avg%5d/min)\n", NumAdditionals, NumAdditionals * mul / div);
+ mprintf("\n");
+ printstats(kReportTopServices);
+
+ if (!ExactlyOneFilter)
+ {
+ ShowSortedHostList(&IPv4HostList, kReportTopHosts);
+ ShowSortedHostList(&IPv6HostList, kReportTopHosts);
+ }
+
+ mDNS_Close(&mDNSStorage);
+ return(0);
+}
+
+mDNSexport int main(int argc, char **argv)
+{
+ const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
+ int i;
+ mStatus status;
+
+#if defined(WIN32)
+ HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
+#endif
+
+ setlinebuf(stdout); // Want to see lines as they appear, not block buffered
+
+ for (i=1; i<argc; i++)
+ {
+ if (i+1 < argc && !strcmp(argv[i], "-i") && atoi(argv[i+1]))
+ {
+ FilterInterface = atoi(argv[i+1]);
+ i += 2;
+ printf("Monitoring interface %d\n", FilterInterface);
+ }
+ else
+ {
+ struct in_addr s4;
+ struct in6_addr s6;
+ FilterList *f;
+ mDNSAddr a;
+ a.type = mDNSAddrType_IPv4;
+
+ if (inet_pton(AF_INET, argv[i], &s4) == 1)
+ a.ip.v4.NotAnInteger = s4.s_addr;
+ else if (inet_pton(AF_INET6, argv[i], &s6) == 1)
+ {
+ a.type = mDNSAddrType_IPv6;
+ mDNSPlatformMemCopy(&a.ip.v6, &s6, sizeof(a.ip.v6));
+ }
+ else
+ {
+ struct hostent *h = gethostbyname(argv[i]);
+ if (h) a.ip.v4.NotAnInteger = *(long*)h->h_addr;
+ else goto usage;
+ }
+
+ f = malloc(sizeof(*f));
+ f->FilterAddr = a;
+ f->next = Filters;
+ Filters = f;
+ }
+ }
+
+ status = mDNSNetMonitor();
+ if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %d\n", progname, (int)status); return(status); }
+ return(0);
+
+usage:
+ fprintf(stderr, "\nmDNS traffic monitor\n");
+ fprintf(stderr, "Usage: %s [-i index] [host]\n", progname);
+ fprintf(stderr, "Optional [-i index] parameter displays only packets from that interface index\n");
+ fprintf(stderr, "Optional [host] parameter displays only packets from that host\n");
+
+ fprintf(stderr, "\nPer-packet header output:\n");
+ fprintf(stderr, "-Q- Multicast Query from mDNS client that accepts multicast responses\n");
+ fprintf(stderr, "-R- Multicast Response packet containing answers/announcements\n");
+ fprintf(stderr, "-LQ- Multicast Query from legacy client that does *not* listen for multicast responses\n");
+ fprintf(stderr, "Q/Ans/Auth/Add Number of questions, answers, authority records and additional records in packet\n");
+
+ fprintf(stderr, "\nPer-record display:\n");
+ fprintf(stderr, "(PM) Probe Question (new service starting), requesting multicast response\n");
+ fprintf(stderr, "(PU) Probe Question (new service starting), requesting unicast response\n");
+ fprintf(stderr, "(DE) Deletion/Goodbye (service going away)\n");
+ fprintf(stderr, "(LQ) Legacy Query Question\n");
+ fprintf(stderr, "(QM) Query Question, requesting multicast response\n");
+ fprintf(stderr, "(QU) Query Question, requesting unicast response\n");
+ fprintf(stderr, "(KA) Known Answer (information querier already knows)\n");
+ fprintf(stderr, "(AN) Unique Answer to question (or periodic announcment) (entire RR Set)\n");
+ fprintf(stderr, "(AN+) Answer to question (or periodic announcment) (add to existing RR Set members)\n");
+ fprintf(stderr, "(AD) Unique Additional Record Set (entire RR Set)\n");
+ fprintf(stderr, "(AD+) Additional records (add to existing RR Set members)\n");
+
+ fprintf(stderr, "\nFinal summary, sorted by service type:\n");
+ fprintf(stderr, "Probe Probes for this service type starting up\n");
+ fprintf(stderr, "Goodbye Goodbye (deletion) packets for this service type shutting down\n");
+ fprintf(stderr, "BrowseQ Browse questions from clients browsing to find a list of instances of this service\n");
+ fprintf(stderr, "BrowseA Browse answers/announcments advertising instances of this service\n");
+ fprintf(stderr, "ResolveQ Resolve questions from clients actively connecting to an instance of this service\n");
+ fprintf(stderr, "ResolveA Resolve answers/announcments giving connection information for an instance of this service\n");
+ fprintf(stderr, "\n");
+ return(-1);
+}
diff --git a/mDNSResponder/mDNSPosix/PosixDaemon.c b/mDNSResponder/mDNSPosix/PosixDaemon.c
new file mode 100644
index 00000000..88b3292c
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/PosixDaemon.c
@@ -0,0 +1,258 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-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.
+
+ File: daemon.c
+
+ Contains: main & associated Application layer for mDNSResponder on Linux.
+
+ */
+
+#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 <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <sys/types.h>
+
+#if __APPLE__
+#undef daemon
+extern int daemon(int, int);
+#endif
+
+#include "mDNSEmbeddedAPI.h"
+#include "mDNSPosix.h"
+#include "mDNSUNP.h" // For daemon()
+#include "uds_daemon.h"
+#include "PlatformCommon.h"
+
+#define CONFIG_FILE "/etc/mdnsd.conf"
+static domainname DynDNSZone; // Default wide-area zone for service registration
+static domainname DynDNSHostname;
+
+#define RR_CACHE_SIZE 500
+static CacheEntity gRRCache[RR_CACHE_SIZE];
+static mDNS_PlatformSupport PlatformStorage;
+
+mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result)
+{
+ (void)m; // Unused
+ if (result == mStatus_NoError)
+ {
+ // On successful registration of dot-local mDNS host name, daemon may want to check if
+ // any name conflict and automatic renaming took place, and if so, record the newly negotiated
+ // name in persistent storage for next time. It should also inform the user of the name change.
+ // On Mac OS X we store the current dot-local mDNS host name in the SCPreferences store,
+ // and notify the user with a CFUserNotification.
+ }
+ else if (result == mStatus_ConfigChanged)
+ {
+ udsserver_handle_configchange(m);
+ }
+ else if (result == mStatus_GrowCache)
+ {
+ // Allocate another chunk of cache storage
+ CacheEntity *storage = malloc(sizeof(CacheEntity) * RR_CACHE_SIZE);
+ if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE);
+ }
+}
+
+// %%% Reconfigure() probably belongs in the platform support layer (mDNSPosix.c), not the daemon cde
+// -- all client layers running on top of mDNSPosix.c need to handle network configuration changes,
+// not only the Unix Domain Socket Daemon
+
+static void Reconfigure(mDNS *m)
+{
+ mDNSAddr DynDNSIP;
+ const mDNSAddr dummy = { mDNSAddrType_IPv4, { { { 1, 1, 1, 1 } } } };;
+ mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL, NULL);
+ if (ParseDNSServers(m, uDNS_SERVERS_FILE) < 0)
+ LogMsg("Unable to parse DNS server list. Unicast DNS-SD unavailable");
+ ReadDDNSSettingsFromConfFile(m, CONFIG_FILE, &DynDNSHostname, &DynDNSZone, NULL);
+ mDNSPlatformSourceAddrForDest(&DynDNSIP, &dummy);
+ if (DynDNSHostname.c[0]) mDNS_AddDynDNSHostName(m, &DynDNSHostname, NULL, NULL);
+ if (DynDNSIP.type) mDNS_SetPrimaryInterfaceInfo(m, &DynDNSIP, NULL, NULL);
+ mDNS_ConfigChanged(m);
+}
+
+// Do appropriate things at startup with command line arguments. Calls exit() if unhappy.
+mDNSlocal void ParseCmdLinArgs(int argc, char **argv)
+{
+ if (argc > 1)
+ {
+ if (0 == strcmp(argv[1], "-debug")) mDNS_DebugMode = mDNStrue;
+ else printf("Usage: %s [-debug]\n", argv[0]);
+ }
+
+ if (!mDNS_DebugMode)
+ {
+ int result = daemon(0, 0);
+ if (result != 0) { LogMsg("Could not run as daemon - exiting"); exit(result); }
+#if __APPLE__
+ LogMsg("The POSIX mdnsd should only be used on OS X for testing - exiting");
+ exit(-1);
+#endif
+ }
+}
+
+mDNSlocal void DumpStateLog(mDNS *const m)
+// Dump a little log of what we've been up to.
+{
+ LogMsg("---- BEGIN STATE LOG ----");
+ udsserver_info(m);
+ LogMsg("---- END STATE LOG ----");
+}
+
+mDNSlocal mStatus MainLoop(mDNS *m) // Loop until we quit.
+{
+ sigset_t signals;
+ mDNSBool gotData = mDNSfalse;
+
+ mDNSPosixListenForSignalInEventLoop(SIGINT);
+ mDNSPosixListenForSignalInEventLoop(SIGTERM);
+ mDNSPosixListenForSignalInEventLoop(SIGUSR1);
+ mDNSPosixListenForSignalInEventLoop(SIGPIPE);
+ mDNSPosixListenForSignalInEventLoop(SIGHUP) ;
+
+ for (; ;)
+ {
+ // Work out how long we expect to sleep before the next scheduled task
+ struct timeval timeout;
+ mDNSs32 ticks;
+
+ // Only idle if we didn't find any data the last time around
+ if (!gotData)
+ {
+ mDNSs32 nextTimerEvent = mDNS_Execute(m);
+ nextTimerEvent = udsserver_idle(nextTimerEvent);
+ ticks = nextTimerEvent - mDNS_TimeNow(m);
+ if (ticks < 1) ticks = 1;
+ }
+ else // otherwise call EventLoop again with 0 timemout
+ ticks = 0;
+
+ timeout.tv_sec = ticks / mDNSPlatformOneSecond;
+ timeout.tv_usec = (ticks % mDNSPlatformOneSecond) * 1000000 / mDNSPlatformOneSecond;
+
+ (void) mDNSPosixRunEventLoopOnce(m, &timeout, &signals, &gotData);
+
+ if (sigismember(&signals, SIGHUP )) Reconfigure(m);
+ if (sigismember(&signals, SIGUSR1)) DumpStateLog(m);
+ // SIGPIPE happens when we try to write to a dead client; death should be detected soon in request_callback() and cleaned up.
+ if (sigismember(&signals, SIGPIPE)) LogMsg("Received SIGPIPE - ignoring");
+ if (sigismember(&signals, SIGINT) || sigismember(&signals, SIGTERM)) break;
+ }
+ return EINTR;
+}
+
+int main(int argc, char **argv)
+{
+ mStatus err;
+
+ ParseCmdLinArgs(argc, argv);
+
+ LogMsg("%s starting", mDNSResponderVersionString);
+
+ err = mDNS_Init(&mDNSStorage, &PlatformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses,
+ mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext);
+
+ if (mStatus_NoError == err)
+ err = udsserver_init(mDNSNULL, 0);
+
+ Reconfigure(&mDNSStorage);
+
+ // Now that we're finished with anything privileged, switch over to running as "nobody"
+ if (mStatus_NoError == err)
+ {
+ const struct passwd *pw = getpwnam("nobody");
+ if (pw != NULL)
+ setuid(pw->pw_uid);
+ else
+ LogMsg("WARNING: mdnsd continuing as root because user \"nobody\" does not exist");
+ }
+
+ if (mStatus_NoError == err)
+ err = MainLoop(&mDNSStorage);
+
+ LogMsg("%s stopping", mDNSResponderVersionString);
+
+ mDNS_Close(&mDNSStorage);
+
+ if (udsserver_exit() < 0)
+ LogMsg("ExitCallback: udsserver_exit failed");
+
+ #if MDNS_DEBUGMSGS > 0
+ printf("mDNSResponder exiting normally with %ld\n", err);
+ #endif
+
+ return err;
+}
+
+// uds_daemon support ////////////////////////////////////////////////////////////
+
+mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context, void **platform_data)
+/* Support routine for uds_daemon.c */
+{
+ // Depends on the fact that udsEventCallback == mDNSPosixEventCallback
+ (void) platform_data;
+ return mDNSPosixAddFDToEventLoop(fd, callback, context);
+}
+
+int udsSupportReadFD(dnssd_sock_t fd, char *buf, int len, int flags, void *platform_data)
+{
+ (void) platform_data;
+ return recv(fd, buf, len, flags);
+}
+
+mStatus udsSupportRemoveFDFromEventLoop(int fd, void *platform_data) // Note: This also CLOSES the file descriptor
+{
+ mStatus err = mDNSPosixRemoveFDFromEventLoop(fd);
+ (void) platform_data;
+ close(fd);
+ return err;
+}
+
+mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay)
+{
+ (void)m;
+ (void)delay;
+ // No-op, for now
+}
+
+#if _BUILDING_XCODE_PROJECT_
+// If the process crashes, then this string will be magically included in the automatically-generated crash log
+const char *__crashreporter_info__ = mDNSResponderVersionString_SCCS + 5;
+asm (".desc ___crashreporter_info__, 0x10");
+#endif
+
+// For convenience when using the "strings" command, this is the last thing in the file
+#if mDNSResponderVersion > 1
+mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder-" STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
+#elif MDNS_VERSIONSTR_NODTS
+mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder (Engineering Build)";
+#else
+mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder (Engineering Build) (" __DATE__ " " __TIME__ ")";
+#endif
diff --git a/mDNSResponder/mDNSPosix/ProxyResponder.c b/mDNSResponder/mDNSPosix/ProxyResponder.c
new file mode 100644
index 00000000..3982161d
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/ProxyResponder.c
@@ -0,0 +1,300 @@
+/* -*- 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.
+ */
+
+#include <stdio.h> // For printf()
+#include <stdlib.h> // For exit() etc.
+#include <string.h> // For strlen() etc.
+#include <unistd.h> // For select()
+#include <signal.h> // For SIGINT, SIGTERM
+#include <errno.h> // For errno, EINTR
+#include <netinet/in.h> // For INADDR_NONE
+#include <arpa/inet.h> // For inet_addr()
+#include <netdb.h> // For gethostbyname()
+
+#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 "ExampleClientApp.h"
+
+// Compatibility workaround: Solaris 2.5 has no INADDR_NONE
+#ifndef INADDR_NONE
+#define INADDR_NONE (mDNSu32)0xffffffff
+#endif
+
+//*************************************************************************************************************
+// Globals
+static mDNS mDNSStorage; // mDNS core uses this to store its globals
+static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
+mDNSexport const char ProgramName[] = "mDNSProxyResponderPosix";
+
+//*************************************************************************************************************
+// Proxy Host Registration
+
+typedef struct
+{
+ mDNSv4Addr ip;
+ domainlabel hostlabel; // Conforms to standard DNS letter-digit-hyphen host name rules
+ AuthRecord RR_A; // 'A' (address) record for our ".local" name
+ AuthRecord RR_PTR; // PTR (reverse lookup) record
+} ProxyHost;
+
+mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
+{
+ ProxyHost *f = (ProxyHost*)rr->RecordContext;
+ if (result == mStatus_NoError)
+ debugf("Host name successfully registered: %##s", rr->resrec.name->c);
+ else
+ {
+ debugf("Host name conflict for %##s", rr->resrec.name->c);
+ mDNS_Deregister(m, &f->RR_A);
+ mDNS_Deregister(m, &f->RR_PTR);
+ exit(-1);
+ }
+}
+
+mDNSlocal mStatus mDNS_RegisterProxyHost(mDNS *m, ProxyHost *p)
+{
+ char buffer[32];
+
+ mDNS_SetupResourceRecord(&p->RR_A, mDNSNULL, mDNSInterface_Any, kDNSType_A, 60, kDNSRecordTypeUnique, AuthRecordAny, HostNameCallback, p);
+ mDNS_SetupResourceRecord(&p->RR_PTR, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, AuthRecordAny, HostNameCallback, p);
+
+ p->RR_A.namestorage.c[0] = 0;
+ AppendDomainLabel(&p->RR_A.namestorage, &p->hostlabel);
+ AppendLiteralLabelString(&p->RR_A.namestorage, "local");
+
+ // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
+ mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p->ip.b[3], p->ip.b[2], p->ip.b[1], p->ip.b[0]);
+ MakeDomainNameFromDNSNameString(&p->RR_PTR.namestorage, buffer);
+ p->RR_PTR.ForceMCast = mDNStrue; // This PTR points to our dot-local name, so don't ever try to write it into a uDNS server
+
+ p->RR_A.resrec.rdata->u.ipv4 = p->ip;
+ AssignDomainName(&p->RR_PTR.resrec.rdata->u.name, p->RR_A.resrec.name);
+
+ mDNS_Register(m, &p->RR_A);
+ mDNS_Register(m, &p->RR_PTR);
+
+ debugf("Made Proxy Host Records for %##s", p->RR_A.resrec.name->c);
+
+ return(mStatus_NoError);
+}
+
+//*************************************************************************************************************
+// Service Registration
+
+// This sample ServiceCallback 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.
+mDNSlocal void ServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result)
+{
+ switch (result)
+ {
+ case mStatus_NoError: debugf("Callback: %##s Name Registered", sr->RR_SRV.resrec.name->c); break;
+ case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", sr->RR_SRV.resrec.name->c); break;
+ case mStatus_MemFree: debugf("Callback: %##s Memory Free", sr->RR_SRV.resrec.name->c); break;
+ default: debugf("Callback: %##s Unknown Result %ld", sr->RR_SRV.resrec.name->c, result); break;
+ }
+
+ if (result == mStatus_NoError)
+ {
+ char buffer[MAX_ESCAPED_DOMAIN_NAME];
+ ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer);
+ printf("Service %s now registered and active\n", buffer);
+ }
+
+ if (result == mStatus_NameConflict)
+ {
+ char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME];
+ ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer1);
+ mDNS_RenameAndReregisterService(m, sr, mDNSNULL);
+ ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer2);
+ printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2);
+ }
+}
+
+// RegisterService() is a simple wrapper function which takes C string
+// parameters, converts them to domainname parameters, and calls mDNS_RegisterService()
+mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset,
+ const char name[], const char type[], const char domain[],
+ const domainname *host, mDNSu16 PortAsNumber, int argc, char **argv)
+{
+ domainlabel n;
+ domainname t, d;
+ unsigned char txtbuffer[1024], *bptr = txtbuffer;
+ char buffer[MAX_ESCAPED_DOMAIN_NAME];
+
+ MakeDomainLabelFromLiteralString(&n, name);
+ MakeDomainNameFromDNSNameString(&t, type);
+ MakeDomainNameFromDNSNameString(&d, domain);
+ while (argc)
+ {
+ int len = strlen(argv[0]);
+ if (len > 255 || bptr + 1 + len >= txtbuffer + sizeof(txtbuffer)) break;
+ printf("STR: %s\n", argv[0]);
+ bptr[0] = len;
+ strcpy((char*)(bptr+1), argv[0]);
+ bptr += 1 + len;
+ argc--;
+ argv++;
+ }
+
+ mDNS_RegisterService(m, recordset,
+ &n, &t, &d, // Name, type, domain
+ host, mDNSOpaque16fromIntVal(PortAsNumber),
+ txtbuffer, bptr-txtbuffer, // TXT data, length
+ mDNSNULL, 0, // Subtypes
+ mDNSInterface_Any, // Interface ID
+ ServiceCallback, mDNSNULL, 0); // Callback, context, flags
+
+ ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer);
+ printf("Made Service Records for %s\n", buffer);
+}
+
+//*************************************************************************************************************
+// Service non-existence assertion
+// (claiming a service name without actually providing a service at that name, to prevent anyone else using that name)
+// This is useful to avoid confusion between similar services
+// e.g. A printer that implements IPP printing service using the name "My Printer", but doesn't implement LPR service,
+// should also claim the LPR service name "My Printer" to stop a different printer offering LPR service under the same name,
+// since it would be confusing to users to have two equivalent services with the same name.
+
+mDNSlocal void NoSuchServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
+{
+ const domainname *proxyhostname = (const domainname *)rr->RecordContext;
+ switch (result)
+ {
+ case mStatus_NoError: debugf("Callback: %##s Name Registered", rr->resrec.name->c); break;
+ case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", rr->resrec.name->c); break;
+ case mStatus_MemFree: debugf("Callback: %##s Memory Free", rr->resrec.name->c); break;
+ default: debugf("Callback: %##s Unknown Result %ld", rr->resrec.name->c, result); break;
+ }
+
+ if (result == mStatus_NoError)
+ {
+ char buffer[MAX_ESCAPED_DOMAIN_NAME];
+ ConvertDomainNameToCString(rr->resrec.name, buffer);
+ printf("Non-existence assertion %s now registered and active\n", buffer);
+ }
+
+ if (result == mStatus_NameConflict)
+ {
+ domainlabel n;
+ domainname t, d;
+ char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME];
+ ConvertDomainNameToCString(rr->resrec.name, buffer1);
+ DeconstructServiceName(rr->resrec.name, &n, &t, &d);
+ IncrementLabelSuffix(&n, mDNStrue);
+ mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, mDNSNULL, 0);
+ ConvertDomainNameToCString(rr->resrec.name, buffer2);
+ printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2);
+ }
+}
+
+mDNSlocal void RegisterNoSuchService(mDNS *m, AuthRecord *const rr, domainname *proxyhostname,
+ const char name[], const char type[], const char domain[])
+{
+ domainlabel n;
+ domainname t, d;
+ char buffer[MAX_ESCAPED_DOMAIN_NAME];
+ MakeDomainLabelFromLiteralString(&n, name);
+ MakeDomainNameFromDNSNameString(&t, type);
+ MakeDomainNameFromDNSNameString(&d, domain);
+ mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, proxyhostname, 0);
+ ConvertDomainNameToCString(rr->resrec.name, buffer);
+ printf("Made Non-existence Record for %s\n", buffer);
+}
+
+//*************************************************************************************************************
+// Main
+
+mDNSexport int main(int argc, char **argv)
+{
+ mStatus status;
+ sigset_t signals;
+
+ if (argc < 3) goto usage;
+
+ status = mDNS_Init(&mDNSStorage, &PlatformStorage,
+ mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
+ mDNS_Init_DontAdvertiseLocalAddresses,
+ mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+ if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %d\n", (int)status); return(status); }
+
+ mDNSPosixListenForSignalInEventLoop(SIGINT);
+ mDNSPosixListenForSignalInEventLoop(SIGTERM);
+
+ if (!strcmp(argv[1], "-"))
+ {
+ domainname proxyhostname;
+ AuthRecord proxyrecord;
+ if (argc < 5) goto usage;
+ proxyhostname.c[0] = 0;
+ AppendLiteralLabelString(&proxyhostname, argv[2]);
+ AppendLiteralLabelString(&proxyhostname, "local");
+ RegisterNoSuchService(&mDNSStorage, &proxyrecord, &proxyhostname, argv[3], argv[4], "local.");
+ }
+ else
+ {
+ ProxyHost proxyhost;
+ ServiceRecordSet proxyservice;
+
+ proxyhost.ip.NotAnInteger = inet_addr(argv[1]);
+ if (proxyhost.ip.NotAnInteger == INADDR_NONE) // INADDR_NONE is 0xFFFFFFFF
+ {
+ struct hostent *h = gethostbyname(argv[1]);
+ if (h) proxyhost.ip.NotAnInteger = *(long*)h->h_addr;
+ }
+ if (proxyhost.ip.NotAnInteger == INADDR_NONE) // INADDR_NONE is 0xFFFFFFFF
+ {
+ fprintf(stderr, "%s is not valid host address\n", argv[1]);
+ return(-1);
+ }
+
+ MakeDomainLabelFromLiteralString(&proxyhost.hostlabel, argv[2]);
+
+ mDNS_RegisterProxyHost(&mDNSStorage, &proxyhost);
+
+ if (argc >=6)
+ RegisterService(&mDNSStorage, &proxyservice, argv[3], argv[4], "local.",
+ proxyhost.RR_A.resrec.name, atoi(argv[5]), argc-6, &argv[6]);
+ }
+
+ do
+ {
+ struct timeval timeout = { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM
+ mDNSBool gotSomething;
+ mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething);
+ }
+ while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM)));
+
+ mDNS_Close(&mDNSStorage);
+
+ return(0);
+
+usage:
+ fprintf(stderr, "%s ip hostlabel [srvname srvtype port txt [txt ...]]\n", argv[0]);
+ fprintf(stderr, "ip Real IP address (or valid host name) of the host where the service actually resides\n");
+ fprintf(stderr, "hostlabel First label of the dot-local host name to create for this host, e.g. \"foo\" for \"foo.local.\"\n");
+ fprintf(stderr, "srvname Descriptive name of service, e.g. \"Stuart's Ink Jet Printer\"\n");
+ fprintf(stderr, "srvtype IANA service type, e.g. \"_ipp._tcp\" or \"_ssh._tcp\", etc.\n");
+ fprintf(stderr, "port Port number where the service resides (1-65535)\n");
+ fprintf(stderr, "txt Additional name/value pairs specified in service definition, e.g. \"pdl=application/postscript\"\n");
+ fprintf(stderr, "e.g. %s 169.254.12.34 thehost (just create a dot-local host name)\n", argv[0]);
+ fprintf(stderr, "or %s 169.254.12.34 thehost \"My Printer\" _printer._tcp. 515 rp=lpt1 pdl=application/postscript\n", argv[0]);
+ fprintf(stderr, "or %s - thehost \"My Printer\" _printer._tcp. (assertion of non-existence)\n", argv[0]);
+ return(-1);
+}
diff --git a/mDNSResponder/mDNSPosix/ReadMe.txt b/mDNSResponder/mDNSPosix/ReadMe.txt
new file mode 100755
index 00000000..c2f56412
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/ReadMe.txt
@@ -0,0 +1,314 @@
+ReadMe About mDNSPosix
+----------------------
+
+mDNSPosix is a port of Apple's Multicast DNS and DNS Service Discovery
+code to Posix platforms.
+
+Multicast DNS and DNS Service Discovery are technologies that allow you
+to register IP-based services and browse the network for those services.
+For more information about mDNS, see the mDNS web site.
+
+ <http://www.multicastdns.org/>
+
+Multicast DNS is part of a family of technologies resulting from the
+efforts of the IETF Zeroconf working group. For information about
+other Zeroconf technologies, see the Zeroconf web site.
+
+ <http://www.zeroconf.org/>
+
+Apple uses the trade mark "Bonjour" to describe our implementation of
+Zeroconf technologies. This sample is designed to show how easy it is
+to make a device "Bonjour compatible".
+
+The "Bonjour" trade mark can also be licensed at no charge for
+inclusion on your own products, packaging, manuals, promotional
+materials, or web site. For details and licensing terms, see
+
+ <http://developer.apple.com/bonjour/>
+
+The code in this sample was compiled and tested on Mac OS X (10.1.x,
+10.2, 10.3), Solaris (SunOS 5.8), Linux (Redhat 2.4.9-21, Fedora Core 1),
+and OpenBSD (2.9). YMMV.
+
+
+Packing List
+------------
+
+The sample uses the following directories:
+
+o mDNSCore -- A directory containing the core mDNS code. This code
+ is written in pure ANSI C and has proved to be very portable.
+ Every platform needs this core protocol engine code.
+
+o mDNSShared -- A directory containing useful code that's not core to
+ the main protocol engine itself, but nonetheless useful, and used by
+ more than one (but not necessarily all) platforms.
+
+o mDNSPosix -- The files that are specific to Posix platforms: Linux,
+ Solaris, FreeBSD, NetBSD, OpenBSD, etc. This code will also work on
+ OS X, though that's not its primary purpose.
+
+o Clients -- Example client code showing how to use the API to the
+ services provided by the daemon.
+
+
+Building the Code
+-----------------
+
+The sample does not use autoconf technology, primarily because I didn't
+want to delay shipping while I learnt how to use it. Thus the code
+builds using a very simple make file. To build the sample you should
+cd to the mDNSPosix directory and type "make os=myos", e.g.
+
+ make os=panther
+
+For Linux you would change that to:
+
+ make os=linux
+
+There are definitions for each of the platforms I ported to. If you're
+porting to any other platform please add appropriate definitions for it
+and send us the diffs so they can be incorporated into the main
+distribution.
+
+
+Using the Sample
+----------------
+When you compile, you will get:
+
+o Main products for general-purpose use (e.g. on a desktop computer):
+ - mdnsd
+ - libmdns
+ - nss_mdns (See nss_ReadMe.txt for important information about nss_mdns)
+
+o Standalone products for dedicated devices (printer, network camera, etc.)
+ - mDNSClientPosix
+ - mDNSResponderPosix
+ - mDNSProxyResponderPosix
+
+o Testing and Debugging tools
+ - dns-sd command-line tool (from the "Clients" folder)
+ - mDNSNetMonitor
+ - mDNSIdentify
+
+As root type "make install" to install eight things:
+o mdnsd (usually in /usr/sbin)
+o libmdns (usually in /usr/lib)
+o dns_sd.h (usually in /usr/include)
+o startup scripts (e.g. in /etc/rc.d)
+o manual pages (usually in /usr/share/man)
+o dns-sd tool (usually in /usr/bin)
+o nss_mdns (usually in /lib)
+o nss configuration files (usually in /etc)
+
+The "make install" concludes by executing the startup script
+(usually "/etc/init.d/mdns start") to start the daemon running.
+You shouldn't need to reboot unless you really want to.
+
+Once the daemon is running, you can use the dns-sd test tool
+to exercise all the major functionality of the daemon. Running
+"dns-sd" with no arguments gives a summary of the available options.
+This test tool is also described in detail, with several examples,
+in Chapter 6 of the O'Reilly "Zero Configuration Networking" book.
+
+
+How It Works
+------------
+ +--------------------+
+ | Client Application |
+ +----------------+ +--------------------+
+ | uds_daemon.c | <--- Unix Domain Socket ---> | libmdns |
+ +----------------+ +--------------------+
+ | mDNSCore |
+ +----------------+
+ | mDNSPosix.c |
+ +----------------+
+
+mdnsd is divided into three sections.
+
+o mDNSCore is the main protocol engine
+o mDNSPosix.c provides the glue it needs to run on a Posix OS
+o uds_daemon.c exports a Unix Domain Socket interface to
+ the services provided by mDNSCore
+
+Client applications link with the libmdns, which implements the functions
+defined in the dns_sd.h header file, and implements the IPC protocol
+used to communicate over the Unix Domain Socket interface to the daemon.
+
+Note that, strictly speaking, nss_mdns could be just another client of
+mdnsd, linking with libmdns just like any other client. However, because
+of its central role in the normal operation of multicast DNS, it is built
+and installed along with the other essential system support components.
+
+
+Clients for Embedded Systems
+----------------------------
+
+For small devices with very constrained resources, with a single address
+space and (typically) no virtual memory, the uds_daemon.c/UDS/libmdns
+layer may be eliminated, and the Client Application may live directly
+on top of mDNSCore:
+
+ +--------------------+
+ | Client Application |
+ +--------------------+
+ | mDNSCore |
+ +--------------------+
+ | mDNSPosix.c |
+ +--------------------+
+
+Programming to this model is more work, so using the daemon and its
+library is recommended if your platform is capable of that.
+
+The runtime behaviour when using the embedded model is as follows:
+
+1. The application calls mDNS_Init, which in turns calls the platform
+ (mDNSPlatformInit).
+
+2. mDNSPlatformInit gets a list of interfaces (get_ifi_info) and registers
+ each one with the core (mDNS_RegisterInterface). For each interface
+ it also creates a multicast socket (SetupSocket).
+
+3. The application then calls select() repeatedly to handle file descriptor
+ events. Before calling select() each time, the application calls
+ mDNSPosixGetFDSet() to give mDNSPosix.c a chance to add its own file
+ descriptors to the set, and then after select() returns, it calls
+ mDNSPosixProcessFDSet() to give mDNSPosix.c a chance to receive and
+ process any packets that may have arrived.
+
+4. When the core needs to send a UDP packet it calls
+ mDNSPlatformSendUDP. That routines finds the interface that
+ corresponds to the source address requested by the core, and
+ sends the datagram using the UDP socket created for the
+ interface. If the socket is flow send-side controlled it just
+ drops the packet.
+
+5. When SocketDataReady runs it uses a complex routine,
+ "recvfrom_flags", to actually receive the packet. This is required
+ because the core needs information about the packet that is
+ only available via the "recvmsg" call, and that call is complex
+ to implement in a portable way. I got my implementation of
+ "recvfrom_flags" from Stevens' "UNIX Network Programming", but
+ I had to modify it further to work with Linux.
+
+One thing to note is that the Posix platform code is very deliberately
+not multi-threaded. I do everything from a main loop that calls
+"select()". This is good because it avoids all the problems that often
+accompany multi-threaded code. If you decide to use threads in your
+platform, you will have to implement the mDNSPlatformLock() and
+mDNSPlatformUnlock() calls which are currently no-ops in mDNSPosix.c.
+
+
+Once you've built the embedded samples you can test them by first
+running the client, as shown below.
+
+ quinn% build/mDNSClientPosix
+ Hit ^C when you're bored waiting for responses.
+
+By default the client starts a search for AppleShare servers and then
+sits and waits, printing a message when services appear and disappear.
+
+To continue with the test you should start the responder in another
+shell window.
+
+ quinn% build/mDNSResponderPosix -n Foo
+
+This will start the responder and tell it to advertise a AppleShare
+service "Foo". In the client window you will see the client print out
+the following as the service shows up on the network.
+
+ quinn% build/mDNSClientPosix
+ Hit ^C when you're bored waiting for responses.
+ *** Found name = 'Foo', type = '_afpovertcp._tcp.', domain = 'local.'
+
+Back in the responder window you can quit the responder cleanly using
+SIGINT (typically ^C).
+
+ quinn% build/mDNSResponderPosix -n Foo
+ ^C
+ quinn%
+
+As the responder quits it will multicast that the "Foo" service is
+disappearing and the client will see that notification and print a
+message to that effect (shown below). Finally, when you're done with
+the client you can use SIGINT to quit it.
+
+ quinn% build/mDNSClientPosix
+ Hit ^C when you're bored waiting for responses.
+ *** Found name = 'Foo', type = '_afpovertcp._tcp.', domain = 'local.'
+ *** Lost name = 'Foo', type = '_afpovertcp._tcp.', domain = 'local.'
+ ^C
+ quinn%
+
+If things don't work, try starting each program in verbose mode (using
+the "-v 1" option, or very verbose mode with "-v 2") to see if there's
+an obvious cause.
+
+That's it for the core functionality. Each program supports a variety
+of other options. For example, you can advertise and browse for a
+different service type using the "-t type" option. Use the "-?" option
+on each program for more user-level information.
+
+
+Caveats
+-------
+Currently the program uses a simple make file.
+
+The Multicast DNS protocol can also operate locally over the loopback
+interface, but this exposed some problems with the underlying network
+stack in early versions of Mac OS X and may expose problems with other
+network stacks too.
+
+o On Mac OS X 10.1.x the code failed to start on the loopback interface
+ because the IP_ADD_MEMBERSHIP option returns ENOBUFS.
+
+o On Mac OS X 10.2 the loopback-only case failed because
+ "sendto" calls fails with error EHOSTUNREACH. (3016042)
+
+Consequently, the code will attempt service discovery on the loopback
+interface only if no other interfaces are available.
+
+I haven't been able to test the loopback-only case on other platforms
+because I don't have access to the physical machine.
+
+
+Licencing
+---------
+This source code is Open Source; for details see the "LICENSE" file.
+
+Credits and Version History
+---------------------------
+If you find any problems with this sample, mail <dts@apple.com> and I
+will try to fix them up.
+
+1.0a1 (Jul 2002) was a prerelease version that was distributed
+internally at Apple.
+
+1.0a2 (Jul 2002) was a prerelease version that was distributed
+internally at Apple.
+
+1.0a3 (Aug 2002) was the first shipping version. The core mDNS code is
+the code from Mac OS 10.2 (Jaguar) GM.
+
+Share and Enjoy
+
+Apple Developer Technical Support
+Networking, Communications, Hardware
+
+6 Aug 2002
+
+Impact: A local network user may cause a denial of the Bonjour service
+Description: An error handling issue exists in the Bonjour Namespace
+Provider. A local network user may send a maliciously crafted multicast
+DNS packet leading to an unexpected termination of the Bonjour service.
+This update addresses the issue by performing additional validation of
+multicast DNS packets. This issue does not affect systems running Mac OS
+X or Windows.
+CVE-ID
+CVE-2011-0220 : JaeSeung Song of the Department of Computing at Imperial
+College London
+
+To Do List
+----------
+• port to a System V that's not Solaris
+• use sig_atomic_t for signal to main thread flags
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;
+}
diff --git a/mDNSResponder/mDNSPosix/Services.txt b/mDNSResponder/mDNSPosix/Services.txt
new file mode 100755
index 00000000..f8d69786
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/Services.txt
@@ -0,0 +1,36 @@
+#
+# Example services file parsed by mDNSResponderPosix.
+#
+# Lines beginning with '#' are comments/ignored.
+# Blank lines indicate the end of a service record specification.
+# The first character of the service name can be a '#' if you escape it with
+# backslash to distinguish if from a comment line.
+# ie, "\#serviceName" will be registered as "#serviceName".
+# Note that any line beginning with white space is considered a blank line.
+#
+# The record format is:
+#
+# <service name>
+# <type>.<protocol> <optional domain>
+# <port number>
+# <zero or more strings for the text record, one string per line>
+#
+# <One or more blank lines between records>
+#
+# Examples shown below.
+
+serviceName1
+_afpovertcp._tcp.
+548
+name=val1
+
+serviceName2
+_afpovertcp._tcp. local.
+548
+name=val2
+name2=anotherattribute
+
+serviceName3
+_afpovertcp._tcp.
+548
+name=val3
diff --git a/mDNSResponder/mDNSPosix/libnss_mdns.8 b/mDNSResponder/mDNSPosix/libnss_mdns.8
new file mode 100755
index 00000000..9c00d4fb
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/libnss_mdns.8
@@ -0,0 +1,148 @@
+.\"
+.\" See section LICENSE for license information.
+.\"
+.Dd June 15, 2004
+.Dt LIBNSS_MDNS 8
+.Os
+.Sh NAME
+.Nm libnss_mdns
+.Nd name service switch module to support Apple mdnsd
+.Sh DESCRIPTION
+The
+.Nm
+shared library implements a name service switch module to interface with Apple
+mdnsd. It is enabled by adding
+.Ql mdns
+to the
+.Ql hosts:
+line of
+.Xr nsswitch.conf 5 .
+This will cause calls to
+.Xr gethostbyname 3 ,
+.Xr gethostbyname2 3
+and
+.Xr gethostbyaddr 3
+to include mdnsd in their lookup path.
+.Pp
+The
+.Nm
+shared library should be installed in the system library paths, typically in
+.Pa /lib .
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /etc/nss_mdns.conf
+configuration options
+.El
+.Sh EXAMPLES
+In
+.Pa /etc/nsswitch.conf :
+.Dl hosts: files mdns dns
+.Pp
+This will cause the name service switch to lookup hostnames first using
+.Pa /etc/hosts ,
+then mdns and finally via DNS.
+.Sh DIAGNOSTICS
+.Nm
+dumps status information via
+.Xr syslog 3 .
+.Sh SEE ALSO
+.\" Cross-references should be ordered by section (low to high), then in
+.\" alphabetical order.
+.Xr nsswitch.conf 5 ,
+.Xr nss_mdns.conf 5 ,
+.Xr ldconfig 8
+.Pp
+.Li info libc Qq Name Service Switch
+.\" .Sh STANDARDS
+.Sh HISTORY
+.Nm
+was originally written for
+.An NICTA Bq http://www.nicta.com.au/ .
+.Sh AUTHORS
+.An "Andrew White" Bq Andrew.White@nicta.com.au
+.Sh BUGS
+Return values for obscure error conditions may not match those expected by nsswitch code.
+.Pp
+Version 0.62 of mdnsd does not support an option to force link-local multicast lookups.
+.Ql PTR
+reverse lookups for non-link-local domains will not function correctly.
+.Sh LICENSE
+This software is licensed under the NICTA Public Source License version 1.0
+.Ss NICTA Public Software Licence
+Version 1.0
+.Pp
+Copyright 2004 National ICT Australia Ltd
+.Pp
+All rights reserved.
+.Pp
+By this licence, National ICT Australia Ltd (NICTA) grants permission,
+free of charge, to any person who obtains a copy of this software
+and any associated documentation files ("the Software") to use and
+deal with the Software in source code and binary forms without
+restriction, with or without modification, and to permit persons
+to whom the Software is furnished to do so, provided that the
+following conditions are met:
+.Bl -bullet
+.It
+Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimers.
+.It
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimers in
+the documentation and/or other materials provided with the
+distribution.
+.It
+The name of NICTA may not be used to endorse or promote products
+derived from this Software without specific prior written permission.
+.El
+.Pp
+EXCEPT AS EXPRESSLY STATED IN THIS LICENCE AND TO THE FULL EXTENT
+PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS-IS" AND
+NICTA MAKES NO REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY
+REPRESENTATIONS, WARRANTIES OR CONDITIONS REGARDING THE CONTENTS
+OR ACCURACY OF THE SOFTWARE, OR OF TITLE, MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, THE ABSENCE OF LATENT
+OR OTHER DEFECTS, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR
+NOT DISCOVERABLE.
+.Pp
+TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL
+NICTA BE LIABLE ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+NEGLIGENCE) FOR ANY LOSS OR DAMAGE WHATSOEVER, INCLUDING (WITHOUT
+LIMITATION) LOSS OF PRODUCTION OR OPERATION TIME, LOSS, DAMAGE OR
+CORRUPTION OF DATA OR RECORDS; OR LOSS OF ANTICIPATED SAVINGS,
+OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR OTHER ECONOMIC LOSS;
+OR ANY SPECIAL, INCIDENTAL, INDIRECT, CONSEQUENTIAL, PUNITIVE OR
+EXEMPLARY DAMAGES ARISING OUT OF OR IN CONNECTION WITH THIS LICENCE,
+THE SOFTWARE OR THE USE OF THE SOFTWARE, EVEN IF NICTA HAS BEEN
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+.Pp
+If applicable legislation implies warranties or conditions, or
+imposes obligations or liability on NICTA in respect of the Software
+that cannot be wholly or partly excluded, restricted or modified,
+NICTA's liability is limited, to the full extent permitted by the
+applicable legislation, at its option, to:
+.Pp
+.Bl -tag -width "a." -compact
+.It a.
+in the case of goods, any one or more of the following:
+.Bl -tag -width "iii." -compact
+.It i.
+the replacement of the goods or the supply of equivalent goods;
+.It ii.
+the repair of the goods;
+.It iii.
+the payment of the cost of replacing the goods or of acquiring
+equivalent goods;
+.It iv.
+the payment of the cost of having the goods repaired; or
+.El
+.It b.
+in the case of services:
+.Bl -tag -width "iii." -compact
+.It i.
+the supplying of the services again; or
+.It ii.
+the payment of the cost of having the services supplied again.
+.El
+.El
diff --git a/mDNSResponder/mDNSPosix/mDNSPosix.c b/mDNSResponder/mDNSPosix/mDNSPosix.c
new file mode 100755
index 00000000..953bf64f
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/mDNSPosix.c
@@ -0,0 +1,1789 @@
+/* -*- 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.
+ *
+ */
+
+#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above
+#include "DNSCommon.h"
+#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
+#include "dns_sd.h"
+#include "dnssec.h"
+#include "nsec.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/select.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <time.h> // platform support for UTC time
+
+#if USES_NETLINK
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#else // USES_NETLINK
+#include <net/route.h>
+#include <net/if.h>
+#endif // USES_NETLINK
+
+#include "mDNSUNP.h"
+#include "GenLinkedList.h"
+
+// ***************************************************************************
+// Structures
+
+// We keep a list of client-supplied event sources in PosixEventSource records
+struct PosixEventSource
+{
+ mDNSPosixEventCallback Callback;
+ void *Context;
+ int fd;
+ struct PosixEventSource *Next;
+};
+typedef struct PosixEventSource PosixEventSource;
+
+// Context record for interface change callback
+struct IfChangeRec
+{
+ int NotifySD;
+ mDNS *mDNS;
+};
+typedef struct IfChangeRec IfChangeRec;
+
+// Note that static data is initialized to zero in (modern) C.
+static fd_set gEventFDs;
+static int gMaxFD; // largest fd in gEventFDs
+static GenLinkedList gEventSources; // linked list of PosixEventSource's
+static sigset_t gEventSignalSet; // Signals which event loop listens for
+static sigset_t gEventSignals; // Signals which were received while inside loop
+
+// ***************************************************************************
+// Globals (for debugging)
+
+static int num_registered_interfaces = 0;
+static int num_pkts_accepted = 0;
+static int num_pkts_rejected = 0;
+
+// ***************************************************************************
+// Functions
+
+int gMDNSPlatformPosixVerboseLevel = 0;
+
+#define PosixErrorToStatus(errNum) ((errNum) == 0 ? mStatus_NoError : mStatus_UnknownErr)
+
+mDNSlocal void SockAddrTomDNSAddr(const struct sockaddr *const sa, mDNSAddr *ipAddr, mDNSIPPort *ipPort)
+{
+ switch (sa->sa_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in *sin = (struct sockaddr_in*)sa;
+ ipAddr->type = mDNSAddrType_IPv4;
+ ipAddr->ip.v4.NotAnInteger = sin->sin_addr.s_addr;
+ if (ipPort) ipPort->NotAnInteger = sin->sin_port;
+ break;
+ }
+
+#if HAVE_IPV6
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa;
+#ifndef NOT_HAVE_SA_LEN
+ assert(sin6->sin6_len == sizeof(*sin6));
+#endif
+ ipAddr->type = mDNSAddrType_IPv6;
+ ipAddr->ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr;
+ if (ipPort) ipPort->NotAnInteger = sin6->sin6_port;
+ break;
+ }
+#endif
+
+ default:
+ verbosedebugf("SockAddrTomDNSAddr: Uknown address family %d\n", sa->sa_family);
+ ipAddr->type = mDNSAddrType_None;
+ if (ipPort) ipPort->NotAnInteger = 0;
+ break;
+ }
+}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Send and Receive
+#endif
+
+// mDNS core calls this routine when it needs to send a packet.
+mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end,
+ mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst,
+ mDNSIPPort dstPort, mDNSBool useBackgroundTrafficClass)
+{
+ int err = 0;
+ struct sockaddr_storage to;
+ PosixNetworkInterface * thisIntf = (PosixNetworkInterface *)(InterfaceID);
+ int sendingsocket = -1;
+
+ (void)src; // Will need to use this parameter once we implement mDNSPlatformUDPSocket/mDNSPlatformUDPClose
+ (void) useBackgroundTrafficClass;
+
+ assert(m != NULL);
+ assert(msg != NULL);
+ assert(end != NULL);
+ assert((((char *) end) - ((char *) msg)) > 0);
+
+ if (dstPort.NotAnInteger == 0)
+ {
+ LogMsg("mDNSPlatformSendUDP: Invalid argument -dstPort is set to 0");
+ return PosixErrorToStatus(EINVAL);
+ }
+ if (dst->type == mDNSAddrType_IPv4)
+ {
+ struct sockaddr_in *sin = (struct sockaddr_in*)&to;
+#ifndef NOT_HAVE_SA_LEN
+ sin->sin_len = sizeof(*sin);
+#endif
+ sin->sin_family = AF_INET;
+ sin->sin_port = dstPort.NotAnInteger;
+ sin->sin_addr.s_addr = dst->ip.v4.NotAnInteger;
+ sendingsocket = thisIntf ? thisIntf->multicastSocket4 : m->p->unicastSocket4;
+ }
+
+#if HAVE_IPV6
+ else if (dst->type == mDNSAddrType_IPv6)
+ {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&to;
+ mDNSPlatformMemZero(sin6, sizeof(*sin6));
+#ifndef NOT_HAVE_SA_LEN
+ sin6->sin6_len = sizeof(*sin6);
+#endif
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = dstPort.NotAnInteger;
+ sin6->sin6_addr = *(struct in6_addr*)&dst->ip.v6;
+ sendingsocket = thisIntf ? thisIntf->multicastSocket6 : m->p->unicastSocket6;
+ }
+#endif
+
+ if (sendingsocket >= 0)
+ err = sendto(sendingsocket, msg, (char*)end - (char*)msg, 0, (struct sockaddr *)&to, GET_SA_LEN(to));
+
+ if (err > 0) err = 0;
+ else if (err < 0)
+ {
+ static int MessageCount = 0;
+ // Don't report EHOSTDOWN (i.e. ARP failure), ENETDOWN, or no route to host for unicast destinations
+ if (!mDNSAddressIsAllDNSLinkGroup(dst))
+ if (errno == EHOSTDOWN || errno == ENETDOWN || errno == EHOSTUNREACH || errno == ENETUNREACH) return(mStatus_TransientErr);
+
+ if (MessageCount < 1000)
+ {
+ MessageCount++;
+ if (thisIntf)
+ LogMsg("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a on interface %#a/%s/%d",
+ errno, strerror(errno), dst, &thisIntf->coreIntf.ip, thisIntf->intfName, thisIntf->index);
+ else
+ LogMsg("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a", errno, strerror(errno), dst);
+ }
+ }
+
+ return PosixErrorToStatus(err);
+}
+
+// This routine is called when the main loop detects that data is available on a socket.
+mDNSlocal void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int skt)
+{
+ mDNSAddr senderAddr, destAddr;
+ mDNSIPPort senderPort;
+ ssize_t packetLen;
+ DNSMessage packet;
+ struct my_in_pktinfo packetInfo;
+ struct sockaddr_storage from;
+ socklen_t fromLen;
+ int flags;
+ mDNSu8 ttl;
+ mDNSBool reject;
+ const mDNSInterfaceID InterfaceID = intf ? intf->coreIntf.InterfaceID : NULL;
+
+ assert(m != NULL);
+ assert(skt >= 0);
+
+ fromLen = sizeof(from);
+ flags = 0;
+ packetLen = recvfrom_flags(skt, &packet, sizeof(packet), &flags, (struct sockaddr *) &from, &fromLen, &packetInfo, &ttl);
+
+ if (packetLen >= 0)
+ {
+ SockAddrTomDNSAddr((struct sockaddr*)&from, &senderAddr, &senderPort);
+ SockAddrTomDNSAddr((struct sockaddr*)&packetInfo.ipi_addr, &destAddr, NULL);
+
+ // If we have broken IP_RECVDSTADDR functionality (so far
+ // I've only seen this on OpenBSD) then apply a hack to
+ // convince mDNS Core that this isn't a spoof packet.
+ // Basically what we do is check to see whether the
+ // packet arrived as a multicast and, if so, set its
+ // destAddr to the mDNS address.
+ //
+ // I must admit that I could just be doing something
+ // wrong on OpenBSD and hence triggering this problem
+ // but I'm at a loss as to how.
+ //
+ // If this platform doesn't have IP_PKTINFO or IP_RECVDSTADDR, then we have
+ // no way to tell the destination address or interface this packet arrived on,
+ // so all we can do is just assume it's a multicast
+
+ #if HAVE_BROKEN_RECVDSTADDR || (!defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR))
+ if ((destAddr.NotAnInteger == 0) && (flags & MSG_MCAST))
+ {
+ destAddr.type = senderAddr.type;
+ if (senderAddr.type == mDNSAddrType_IPv4) destAddr.ip.v4 = AllDNSLinkGroup_v4.ip.v4;
+ else if (senderAddr.type == mDNSAddrType_IPv6) destAddr.ip.v6 = AllDNSLinkGroup_v6.ip.v6;
+ }
+ #endif
+
+ // We only accept the packet if the interface on which it came
+ // in matches the interface associated with this socket.
+ // We do this match by name or by index, depending on which
+ // information is available. recvfrom_flags sets the name
+ // to "" if the name isn't available, or the index to -1
+ // if the index is available. This accomodates the various
+ // different capabilities of our target platforms.
+
+ reject = mDNSfalse;
+ if (!intf)
+ {
+ // Ignore multicasts accidentally delivered to our unicast receiving socket
+ if (mDNSAddrIsDNSMulticast(&destAddr)) packetLen = -1;
+ }
+ else
+ {
+ if (packetInfo.ipi_ifname[0] != 0) reject = (strcmp(packetInfo.ipi_ifname, intf->intfName) != 0);
+ else if (packetInfo.ipi_ifindex != -1) reject = (packetInfo.ipi_ifindex != intf->index);
+
+ if (reject)
+ {
+ verbosedebugf("SocketDataReady ignored a packet from %#a to %#a on interface %s/%d expecting %#a/%s/%d/%d",
+ &senderAddr, &destAddr, packetInfo.ipi_ifname, packetInfo.ipi_ifindex,
+ &intf->coreIntf.ip, intf->intfName, intf->index, skt);
+ packetLen = -1;
+ num_pkts_rejected++;
+ if (num_pkts_rejected > (num_pkts_accepted + 1) * (num_registered_interfaces + 1) * 2)
+ {
+ fprintf(stderr,
+ "*** WARNING: Received %d packets; Accepted %d packets; Rejected %d packets because of interface mismatch\n",
+ num_pkts_accepted + num_pkts_rejected, num_pkts_accepted, num_pkts_rejected);
+ num_pkts_accepted = 0;
+ num_pkts_rejected = 0;
+ }
+ }
+ else
+ {
+ verbosedebugf("SocketDataReady got a packet from %#a to %#a on interface %#a/%s/%d/%d",
+ &senderAddr, &destAddr, &intf->coreIntf.ip, intf->intfName, intf->index, skt);
+ num_pkts_accepted++;
+ }
+ }
+ }
+
+ if (packetLen >= 0)
+ mDNSCoreReceive(m, &packet, (mDNSu8 *)&packet + packetLen,
+ &senderAddr, senderPort, &destAddr, MulticastDNSPort, InterfaceID);
+}
+
+mDNSexport mDNSBool mDNSPlatformPeekUDP(mDNS *const m, UDPSocket *src)
+{
+ (void)m; // unused
+ (void)src; // unused
+ return mDNSfalse;
+}
+
+mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS * const m, TCPSocketFlags flags, mDNSIPPort * port, mDNSBool useBackgroundTrafficClass)
+{
+ (void)m; // Unused
+ (void)flags; // Unused
+ (void)port; // Unused
+ (void)useBackgroundTrafficClass; // Unused
+ return NULL;
+}
+
+mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int sd)
+{
+ (void)flags; // Unused
+ (void)sd; // Unused
+ return NULL;
+}
+
+mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock)
+{
+ (void)sock; // Unused
+ return -1;
+}
+
+mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, mDNSInterfaceID InterfaceID,
+ TCPConnectionCallback callback, void *context)
+{
+ (void)sock; // Unused
+ (void)dst; // Unused
+ (void)dstport; // Unused
+ (void)hostname; // Unused
+ (void)InterfaceID; // Unused
+ (void)callback; // Unused
+ (void)context; // Unused
+ return(mStatus_UnsupportedErr);
+}
+
+mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sock)
+{
+ (void)sock; // Unused
+}
+
+mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool * closed)
+{
+ (void)sock; // Unused
+ (void)buf; // Unused
+ (void)buflen; // Unused
+ (void)closed; // Unused
+ return 0;
+}
+
+mDNSexport long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len)
+{
+ (void)sock; // Unused
+ (void)msg; // Unused
+ (void)len; // Unused
+ return 0;
+}
+
+mDNSexport UDPSocket *mDNSPlatformUDPSocket(mDNS * const m, mDNSIPPort port)
+{
+ (void)m; // Unused
+ (void)port; // Unused
+ return NULL;
+}
+
+mDNSexport void mDNSPlatformUDPClose(UDPSocket *sock)
+{
+ (void)sock; // Unused
+}
+
+mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID)
+{
+ (void)m; // Unused
+ (void)InterfaceID; // Unused
+}
+
+mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID)
+{
+ (void)msg; // Unused
+ (void)end; // Unused
+ (void)InterfaceID; // Unused
+}
+
+mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID)
+{
+ (void)m; // Unused
+ (void)tpa; // Unused
+ (void)tha; // Unused
+ (void)InterfaceID; // Unused
+}
+
+mDNSexport mStatus mDNSPlatformTLSSetupCerts(void)
+{
+ return(mStatus_UnsupportedErr);
+}
+
+mDNSexport void mDNSPlatformTLSTearDownCerts(void)
+{
+}
+
+mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason)
+{
+ (void) m;
+ (void) allowSleep;
+ (void) reason;
+}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - /etc/hosts support
+#endif
+
+mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result)
+{
+ (void)m; // unused
+ (void)rr;
+ (void)result;
+}
+
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** DDNS Config Platform Functions
+#endif
+
+mDNSexport mDNSBool mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains,
+ DNameListElem **BrowseDomains, mDNSBool ackConfig)
+{
+ (void) m;
+ (void) setservers;
+ (void) fqdn;
+ (void) setsearch;
+ (void) RegDomains;
+ (void) BrowseDomains;
+ (void) ackConfig;
+
+ return mDNStrue;
+}
+
+mDNSexport mStatus mDNSPlatformGetPrimaryInterface(mDNS * const m, mDNSAddr * v4, mDNSAddr * v6, mDNSAddr * router)
+{
+ (void) m;
+ (void) v4;
+ (void) v6;
+ (void) router;
+
+ return mStatus_UnsupportedErr;
+}
+
+mDNSexport void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status)
+{
+ (void) dname;
+ (void) status;
+}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Init and Term
+#endif
+
+// This gets the current hostname, truncating it at the first dot if necessary
+mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel)
+{
+ int len = 0;
+ gethostname((char *)(&namelabel->c[1]), MAX_DOMAIN_LABEL);
+ while (len < MAX_DOMAIN_LABEL && namelabel->c[len+1] && namelabel->c[len+1] != '.') len++;
+ namelabel->c[0] = len;
+}
+
+// On OS X this gets the text of the field labelled "Computer Name" in the Sharing Prefs Control Panel
+// Other platforms can either get the information from the appropriate place,
+// or they can alternatively just require all registering services to provide an explicit name
+mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel)
+{
+ // On Unix we have no better name than the host name, so we just use that.
+ GetUserSpecifiedRFC1034ComputerName(namelabel);
+}
+
+mDNSexport int ParseDNSServers(mDNS *m, const char *filePath)
+{
+ char line[256];
+ char nameserver[16];
+ char keyword[11];
+ int numOfServers = 0;
+ FILE *fp = fopen(filePath, "r");
+ if (fp == NULL) return -1;
+ while (fgets(line,sizeof(line),fp))
+ {
+ struct in_addr ina;
+ line[255]='\0'; // just to be safe
+ if (sscanf(line,"%10s %15s", keyword, nameserver) != 2) continue; // it will skip whitespaces
+ if (strncasecmp(keyword,"nameserver",10)) continue;
+ if (inet_aton(nameserver, (struct in_addr *)&ina) != 0)
+ {
+ mDNSAddr DNSAddr;
+ DNSAddr.type = mDNSAddrType_IPv4;
+ DNSAddr.ip.v4.NotAnInteger = ina.s_addr;
+ mDNS_AddDNSServer(m, NULL, mDNSInterface_Any, 0, &DNSAddr, UnicastDNSPort, kScopeNone, 0, mDNSfalse, 0, mDNStrue, mDNStrue, mDNSfalse);
+ numOfServers++;
+ }
+ }
+ return (numOfServers > 0) ? 0 : -1;
+}
+
+// Searches the interface list looking for the named interface.
+// Returns a pointer to if it found, or NULL otherwise.
+mDNSlocal PosixNetworkInterface *SearchForInterfaceByName(mDNS *const m, const char *intfName)
+{
+ PosixNetworkInterface *intf;
+
+ assert(m != NULL);
+ assert(intfName != NULL);
+
+ intf = (PosixNetworkInterface*)(m->HostInterfaces);
+ while ((intf != NULL) && (strcmp(intf->intfName, intfName) != 0))
+ intf = (PosixNetworkInterface *)(intf->coreIntf.next);
+
+ return intf;
+}
+
+mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 index)
+{
+ PosixNetworkInterface *intf;
+
+ assert(m != NULL);
+
+ if (index == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly);
+ if (index == kDNSServiceInterfaceIndexP2P ) return(mDNSInterface_P2P);
+ if (index == kDNSServiceInterfaceIndexAny ) return(mDNSInterface_Any);
+
+ intf = (PosixNetworkInterface*)(m->HostInterfaces);
+ while ((intf != NULL) && (mDNSu32) intf->index != index)
+ intf = (PosixNetworkInterface *)(intf->coreIntf.next);
+
+ return (mDNSInterfaceID) intf;
+}
+
+mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange)
+{
+ PosixNetworkInterface *intf;
+ (void) suppressNetworkChange; // Unused
+
+ assert(m != NULL);
+
+ if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly);
+ if (id == mDNSInterface_P2P ) return(kDNSServiceInterfaceIndexP2P);
+ if (id == mDNSInterface_Any ) return(kDNSServiceInterfaceIndexAny);
+
+ intf = (PosixNetworkInterface*)(m->HostInterfaces);
+ while ((intf != NULL) && (mDNSInterfaceID) intf != id)
+ intf = (PosixNetworkInterface *)(intf->coreIntf.next);
+
+ return intf ? intf->index : 0;
+}
+
+// Frees the specified PosixNetworkInterface structure. The underlying
+// interface must have already been deregistered with the mDNS core.
+mDNSlocal void FreePosixNetworkInterface(PosixNetworkInterface *intf)
+{
+ assert(intf != NULL);
+ if (intf->intfName != NULL) free((void *)intf->intfName);
+ if (intf->multicastSocket4 != -1) assert(close(intf->multicastSocket4) == 0);
+#if HAVE_IPV6
+ if (intf->multicastSocket6 != -1) assert(close(intf->multicastSocket6) == 0);
+#endif
+ free(intf);
+}
+
+// Grab the first interface, deregister it, free it, and repeat until done.
+mDNSlocal void ClearInterfaceList(mDNS *const m)
+{
+ assert(m != NULL);
+
+ while (m->HostInterfaces)
+ {
+ PosixNetworkInterface *intf = (PosixNetworkInterface*)(m->HostInterfaces);
+ mDNS_DeregisterInterface(m, &intf->coreIntf, mDNSfalse);
+ if (gMDNSPlatformPosixVerboseLevel > 0) fprintf(stderr, "Deregistered interface %s\n", intf->intfName);
+ FreePosixNetworkInterface(intf);
+ }
+ num_registered_interfaces = 0;
+ num_pkts_accepted = 0;
+ num_pkts_rejected = 0;
+}
+
+// Sets up a send/receive socket.
+// If mDNSIPPort port is non-zero, then it's a multicast socket on the specified interface
+// If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries
+mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interfaceIndex, int *sktPtr)
+{
+ int err = 0;
+ static const int kOn = 1;
+ static const int kIntTwoFiveFive = 255;
+ static const unsigned char kByteTwoFiveFive = 255;
+ const mDNSBool JoinMulticastGroup = (port.NotAnInteger != 0);
+
+ (void) interfaceIndex; // This parameter unused on plaforms that don't have IPv6
+ assert(intfAddr != NULL);
+ assert(sktPtr != NULL);
+ assert(*sktPtr == -1);
+
+ // Open the socket...
+ if (intfAddr->sa_family == AF_INET) *sktPtr = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+#if HAVE_IPV6
+ else if (intfAddr->sa_family == AF_INET6) *sktPtr = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+#endif
+ else return EINVAL;
+
+ if (*sktPtr < 0) { err = errno; perror((intfAddr->sa_family == AF_INET) ? "socket AF_INET" : "socket AF_INET6"); }
+
+ // ... with a shared UDP port, if it's for multicast receiving
+ if (err == 0 && port.NotAnInteger)
+ {
+ #if defined(SO_REUSEPORT)
+ err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEPORT, &kOn, sizeof(kOn));
+ #elif defined(SO_REUSEADDR)
+ err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
+ #else
+ #error This platform has no way to avoid address busy errors on multicast.
+ #endif
+ if (err < 0) { err = errno; perror("setsockopt - SO_REUSExxxx"); }
+ }
+
+ // We want to receive destination addresses and interface identifiers.
+ if (intfAddr->sa_family == AF_INET)
+ {
+ struct ip_mreq imr;
+ struct sockaddr_in bindAddr;
+ if (err == 0)
+ {
+ #if defined(IP_PKTINFO) // Linux
+ err = setsockopt(*sktPtr, IPPROTO_IP, IP_PKTINFO, &kOn, sizeof(kOn));
+ if (err < 0) { err = errno; perror("setsockopt - IP_PKTINFO"); }
+ #elif defined(IP_RECVDSTADDR) || defined(IP_RECVIF) // BSD and Solaris
+ #if defined(IP_RECVDSTADDR)
+ err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVDSTADDR, &kOn, sizeof(kOn));
+ if (err < 0) { err = errno; perror("setsockopt - IP_RECVDSTADDR"); }
+ #endif
+ #if defined(IP_RECVIF)
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVIF, &kOn, sizeof(kOn));
+ if (err < 0) { err = errno; perror("setsockopt - IP_RECVIF"); }
+ }
+ #endif
+ #else
+ #warning This platform has no way to get the destination interface information -- will only work for single-homed hosts
+ #endif
+ }
+ #if defined(IP_RECVTTL) // Linux
+ if (err == 0)
+ {
+ setsockopt(*sktPtr, IPPROTO_IP, IP_RECVTTL, &kOn, sizeof(kOn));
+ // We no longer depend on being able to get the received TTL, so don't worry if the option fails
+ }
+ #endif
+
+ // Add multicast group membership on this interface
+ if (err == 0 && JoinMulticastGroup)
+ {
+ imr.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger;
+ imr.imr_interface = ((struct sockaddr_in*)intfAddr)->sin_addr;
+ err = setsockopt(*sktPtr, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr));
+ if (err < 0) { err = errno; perror("setsockopt - IP_ADD_MEMBERSHIP"); }
+ }
+
+ // Specify outgoing interface too
+ if (err == 0 && JoinMulticastGroup)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_IF, &((struct sockaddr_in*)intfAddr)->sin_addr, sizeof(struct in_addr));
+ if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_IF"); }
+ }
+
+ // Per the mDNS spec, send unicast packets with TTL 255
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IP, IP_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive));
+ if (err < 0) { err = errno; perror("setsockopt - IP_TTL"); }
+ }
+
+ // and multicast packets with TTL 255 too
+ // There's some debate as to whether IP_MULTICAST_TTL is an int or a byte so we just try both.
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive));
+ if (err < 0 && errno == EINVAL)
+ err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive));
+ if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_TTL"); }
+ }
+
+ // And start listening for packets
+ if (err == 0)
+ {
+ bindAddr.sin_family = AF_INET;
+ bindAddr.sin_port = port.NotAnInteger;
+ bindAddr.sin_addr.s_addr = INADDR_ANY; // Want to receive multicasts AND unicasts on this socket
+ err = bind(*sktPtr, (struct sockaddr *) &bindAddr, sizeof(bindAddr));
+ if (err < 0) { err = errno; perror("bind"); fflush(stderr); }
+ }
+ } // endif (intfAddr->sa_family == AF_INET)
+
+#if HAVE_IPV6
+ else if (intfAddr->sa_family == AF_INET6)
+ {
+ struct ipv6_mreq imr6;
+ struct sockaddr_in6 bindAddr6;
+ #if defined(IPV6_PKTINFO)
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_2292_PKTINFO, &kOn, sizeof(kOn));
+ if (err < 0) { err = errno; perror("setsockopt - IPV6_PKTINFO"); }
+ }
+ #else
+ #warning This platform has no way to get the destination interface information for IPv6 -- will only work for single-homed hosts
+ #endif
+ #if defined(IPV6_HOPLIMIT)
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_2292_HOPLIMIT, &kOn, sizeof(kOn));
+ if (err < 0) { err = errno; perror("setsockopt - IPV6_HOPLIMIT"); }
+ }
+ #endif
+
+ // Add multicast group membership on this interface
+ if (err == 0 && JoinMulticastGroup)
+ {
+ imr6.ipv6mr_multiaddr = *(const struct in6_addr*)&AllDNSLinkGroup_v6.ip.v6;
+ imr6.ipv6mr_interface = interfaceIndex;
+ //LogMsg("Joining %.16a on %d", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface);
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_JOIN_GROUP, &imr6, sizeof(imr6));
+ if (err < 0)
+ {
+ err = errno;
+ verbosedebugf("IPV6_JOIN_GROUP %.16a on %d failed.\n", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface);
+ perror("setsockopt - IPV6_JOIN_GROUP");
+ }
+ }
+
+ // Specify outgoing interface too
+ if (err == 0 && JoinMulticastGroup)
+ {
+ u_int multicast_if = interfaceIndex;
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_if, sizeof(multicast_if));
+ if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_IF"); }
+ }
+
+ // We want to receive only IPv6 packets on this socket.
+ // Without this option, we may get IPv4 addresses as mapped addresses.
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_V6ONLY, &kOn, sizeof(kOn));
+ if (err < 0) { err = errno; perror("setsockopt - IPV6_V6ONLY"); }
+ }
+
+ // Per the mDNS spec, send unicast packets with TTL 255
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive));
+ if (err < 0) { err = errno; perror("setsockopt - IPV6_UNICAST_HOPS"); }
+ }
+
+ // and multicast packets with TTL 255 too
+ // There's some debate as to whether IPV6_MULTICAST_HOPS is an int or a byte so we just try both.
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive));
+ if (err < 0 && errno == EINVAL)
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive));
+ if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_HOPS"); }
+ }
+
+ // And start listening for packets
+ if (err == 0)
+ {
+ mDNSPlatformMemZero(&bindAddr6, sizeof(bindAddr6));
+#ifndef NOT_HAVE_SA_LEN
+ bindAddr6.sin6_len = sizeof(bindAddr6);
+#endif
+ bindAddr6.sin6_family = AF_INET6;
+ bindAddr6.sin6_port = port.NotAnInteger;
+ bindAddr6.sin6_flowinfo = 0;
+ bindAddr6.sin6_addr = in6addr_any; // Want to receive multicasts AND unicasts on this socket
+ bindAddr6.sin6_scope_id = 0;
+ err = bind(*sktPtr, (struct sockaddr *) &bindAddr6, sizeof(bindAddr6));
+ if (err < 0) { err = errno; perror("bind"); fflush(stderr); }
+ }
+ } // endif (intfAddr->sa_family == AF_INET6)
+#endif
+
+ // Set the socket to non-blocking.
+ if (err == 0)
+ {
+ err = fcntl(*sktPtr, F_GETFL, 0);
+ if (err < 0) err = errno;
+ else
+ {
+ err = fcntl(*sktPtr, F_SETFL, err | O_NONBLOCK);
+ if (err < 0) err = errno;
+ }
+ }
+
+ // Clean up
+ if (err != 0 && *sktPtr != -1) { assert(close(*sktPtr) == 0); *sktPtr = -1; }
+ assert((err == 0) == (*sktPtr != -1));
+ return err;
+}
+
+// Creates a PosixNetworkInterface for the interface whose IP address is
+// intfAddr and whose name is intfName and registers it with mDNS core.
+mDNSlocal int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, struct sockaddr *intfMask, const char *intfName, int intfIndex)
+{
+ int err = 0;
+ PosixNetworkInterface *intf;
+ PosixNetworkInterface *alias = NULL;
+
+ assert(m != NULL);
+ assert(intfAddr != NULL);
+ assert(intfName != NULL);
+ assert(intfMask != NULL);
+
+ // Allocate the interface structure itself.
+ intf = (PosixNetworkInterface*)malloc(sizeof(*intf));
+ if (intf == NULL) { assert(0); err = ENOMEM; }
+
+ // And make a copy of the intfName.
+ if (err == 0)
+ {
+ intf->intfName = strdup(intfName);
+ if (intf->intfName == NULL) { assert(0); err = ENOMEM; }
+ }
+
+ if (err == 0)
+ {
+ // Set up the fields required by the mDNS core.
+ SockAddrTomDNSAddr(intfAddr, &intf->coreIntf.ip, NULL);
+ SockAddrTomDNSAddr(intfMask, &intf->coreIntf.mask, NULL);
+
+ //LogMsg("SetupOneInterface: %#a %#a", &intf->coreIntf.ip, &intf->coreIntf.mask);
+ strncpy(intf->coreIntf.ifname, intfName, sizeof(intf->coreIntf.ifname));
+ intf->coreIntf.ifname[sizeof(intf->coreIntf.ifname)-1] = 0;
+ intf->coreIntf.Advertise = m->AdvertiseLocalAddresses;
+ intf->coreIntf.McastTxRx = mDNStrue;
+
+ // Set up the extra fields in PosixNetworkInterface.
+ assert(intf->intfName != NULL); // intf->intfName already set up above
+ intf->index = intfIndex;
+ intf->multicastSocket4 = -1;
+#if HAVE_IPV6
+ intf->multicastSocket6 = -1;
+#endif
+ alias = SearchForInterfaceByName(m, intf->intfName);
+ if (alias == NULL) alias = intf;
+ intf->coreIntf.InterfaceID = (mDNSInterfaceID)alias;
+
+ if (alias != intf)
+ debugf("SetupOneInterface: %s %#a is an alias of %#a", intfName, &intf->coreIntf.ip, &alias->coreIntf.ip);
+ }
+
+ // Set up the multicast socket
+ if (err == 0)
+ {
+ if (alias->multicastSocket4 == -1 && intfAddr->sa_family == AF_INET)
+ err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket4);
+#if HAVE_IPV6
+ else if (alias->multicastSocket6 == -1 && intfAddr->sa_family == AF_INET6)
+ err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket6);
+#endif
+ }
+
+ // If interface is a direct link, address record will be marked as kDNSRecordTypeKnownUnique
+ // and skip the probe phase of the probe/announce packet sequence.
+ intf->coreIntf.DirectLink = mDNSfalse;
+
+ // The interface is all ready to go, let's register it with the mDNS core.
+ if (err == 0)
+ err = mDNS_RegisterInterface(m, &intf->coreIntf, mDNSfalse);
+
+ // Clean up.
+ if (err == 0)
+ {
+ num_registered_interfaces++;
+ debugf("SetupOneInterface: %s %#a Registered", intf->intfName, &intf->coreIntf.ip);
+ if (gMDNSPlatformPosixVerboseLevel > 0)
+ fprintf(stderr, "Registered interface %s\n", intf->intfName);
+ }
+ else
+ {
+ // Use intfName instead of intf->intfName in the next line to avoid dereferencing NULL.
+ debugf("SetupOneInterface: %s %#a failed to register %d", intfName, &intf->coreIntf.ip, err);
+ if (intf) { FreePosixNetworkInterface(intf); intf = NULL; }
+ }
+
+ assert((err == 0) == (intf != NULL));
+
+ return err;
+}
+
+// Call get_ifi_info() to obtain a list of active interfaces and call SetupOneInterface() on each one.
+mDNSlocal int SetupInterfaceList(mDNS *const m)
+{
+ mDNSBool foundav4 = mDNSfalse;
+ int err = 0;
+ struct ifi_info *intfList = get_ifi_info(AF_INET, mDNStrue);
+ struct ifi_info *firstLoopback = NULL;
+
+ assert(m != NULL);
+ debugf("SetupInterfaceList");
+
+ if (intfList == NULL) err = ENOENT;
+
+#if HAVE_IPV6
+ if (err == 0) /* Link the IPv6 list to the end of the IPv4 list */
+ {
+ struct ifi_info **p = &intfList;
+ while (*p) p = &(*p)->ifi_next;
+ *p = get_ifi_info(AF_INET6, mDNStrue);
+ }
+#endif
+
+ if (err == 0)
+ {
+ struct ifi_info *i = intfList;
+ while (i)
+ {
+ if ( ((i->ifi_addr->sa_family == AF_INET)
+#if HAVE_IPV6
+ || (i->ifi_addr->sa_family == AF_INET6)
+#endif
+ ) && (i->ifi_flags & IFF_UP) && !(i->ifi_flags & IFF_POINTOPOINT))
+ {
+ if (i->ifi_flags & IFF_LOOPBACK)
+ {
+ if (firstLoopback == NULL)
+ firstLoopback = i;
+ }
+ else
+ {
+ if (SetupOneInterface(m, i->ifi_addr, i->ifi_netmask, i->ifi_name, i->ifi_index) == 0)
+ if (i->ifi_addr->sa_family == AF_INET)
+ foundav4 = mDNStrue;
+ }
+ }
+ i = i->ifi_next;
+ }
+
+ // If we found no normal interfaces but we did find a loopback interface, register the
+ // loopback interface. This allows self-discovery if no interfaces are configured.
+ // Temporary workaround: Multicast loopback on IPv6 interfaces appears not to work.
+ // In the interim, we skip loopback interface only if we found at least one v4 interface to use
+ // if ((m->HostInterfaces == NULL) && (firstLoopback != NULL))
+ if (!foundav4 && firstLoopback)
+ (void) SetupOneInterface(m, firstLoopback->ifi_addr, firstLoopback->ifi_netmask, firstLoopback->ifi_name, firstLoopback->ifi_index);
+ }
+
+ // Clean up.
+ if (intfList != NULL) free_ifi_info(intfList);
+ return err;
+}
+
+#if USES_NETLINK
+
+// See <http://www.faqs.org/rfcs/rfc3549.html> for a description of NetLink
+
+// Open a socket that will receive interface change notifications
+mDNSlocal mStatus OpenIfNotifySocket(int *pFD)
+{
+ mStatus err = mStatus_NoError;
+ struct sockaddr_nl snl;
+ int sock;
+ int ret;
+
+ sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (sock < 0)
+ return errno;
+
+ // Configure read to be non-blocking because inbound msg size is not known in advance
+ (void) fcntl(sock, F_SETFL, O_NONBLOCK);
+
+ /* Subscribe the socket to Link & IP addr notifications. */
+ mDNSPlatformMemZero(&snl, sizeof snl);
+ snl.nl_family = AF_NETLINK;
+ snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
+ ret = bind(sock, (struct sockaddr *) &snl, sizeof snl);
+ if (0 == ret)
+ *pFD = sock;
+ else
+ err = errno;
+
+ return err;
+}
+
+#if MDNS_DEBUGMSGS
+mDNSlocal void PrintNetLinkMsg(const struct nlmsghdr *pNLMsg)
+{
+ const char *kNLMsgTypes[] = { "", "NLMSG_NOOP", "NLMSG_ERROR", "NLMSG_DONE", "NLMSG_OVERRUN" };
+ const char *kNLRtMsgTypes[] = { "RTM_NEWLINK", "RTM_DELLINK", "RTM_GETLINK", "RTM_NEWADDR", "RTM_DELADDR", "RTM_GETADDR" };
+
+ printf("nlmsghdr len=%d, type=%s, flags=0x%x\n", pNLMsg->nlmsg_len,
+ pNLMsg->nlmsg_type < RTM_BASE ? kNLMsgTypes[pNLMsg->nlmsg_type] : kNLRtMsgTypes[pNLMsg->nlmsg_type - RTM_BASE],
+ pNLMsg->nlmsg_flags);
+
+ if (RTM_NEWLINK <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETLINK)
+ {
+ struct ifinfomsg *pIfInfo = (struct ifinfomsg*) NLMSG_DATA(pNLMsg);
+ printf("ifinfomsg family=%d, type=%d, index=%d, flags=0x%x, change=0x%x\n", pIfInfo->ifi_family,
+ pIfInfo->ifi_type, pIfInfo->ifi_index, pIfInfo->ifi_flags, pIfInfo->ifi_change);
+
+ }
+ else if (RTM_NEWADDR <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETADDR)
+ {
+ struct ifaddrmsg *pIfAddr = (struct ifaddrmsg*) NLMSG_DATA(pNLMsg);
+ printf("ifaddrmsg family=%d, index=%d, flags=0x%x\n", pIfAddr->ifa_family,
+ pIfAddr->ifa_index, pIfAddr->ifa_flags);
+ }
+ printf("\n");
+}
+#endif
+
+mDNSlocal mDNSu32 ProcessRoutingNotification(int sd)
+// Read through the messages on sd and if any indicate that any interface records should
+// be torn down and rebuilt, return affected indices as a bitmask. Otherwise return 0.
+{
+ ssize_t readCount;
+ char buff[4096];
+ struct nlmsghdr *pNLMsg = (struct nlmsghdr*) buff;
+ mDNSu32 result = 0;
+
+ // The structure here is more complex than it really ought to be because,
+ // unfortunately, there's no good way to size a buffer in advance large
+ // enough to hold all pending data and so avoid message fragmentation.
+ // (Note that FIONREAD is not supported on AF_NETLINK.)
+
+ readCount = read(sd, buff, sizeof buff);
+ while (1)
+ {
+ // Make sure we've got an entire nlmsghdr in the buffer, and payload, too.
+ // If not, discard already-processed messages in buffer and read more data.
+ if (((char*) &pNLMsg[1] > (buff + readCount)) || // i.e. *pNLMsg extends off end of buffer
+ ((char*) pNLMsg + pNLMsg->nlmsg_len > (buff + readCount)))
+ {
+ if (buff < (char*) pNLMsg) // we have space to shuffle
+ {
+ // discard processed data
+ readCount -= ((char*) pNLMsg - buff);
+ memmove(buff, pNLMsg, readCount);
+ pNLMsg = (struct nlmsghdr*) buff;
+
+ // read more data
+ readCount += read(sd, buff + readCount, sizeof buff - readCount);
+ continue; // spin around and revalidate with new readCount
+ }
+ else
+ break; // Otherwise message does not fit in buffer
+ }
+
+#if MDNS_DEBUGMSGS
+ PrintNetLinkMsg(pNLMsg);
+#endif
+
+ // Process the NetLink message
+ if (pNLMsg->nlmsg_type == RTM_GETLINK || pNLMsg->nlmsg_type == RTM_NEWLINK)
+ result |= 1 << ((struct ifinfomsg*) NLMSG_DATA(pNLMsg))->ifi_index;
+ else if (pNLMsg->nlmsg_type == RTM_DELADDR || pNLMsg->nlmsg_type == RTM_NEWADDR)
+ result |= 1 << ((struct ifaddrmsg*) NLMSG_DATA(pNLMsg))->ifa_index;
+
+ // Advance pNLMsg to the next message in the buffer
+ if ((pNLMsg->nlmsg_flags & NLM_F_MULTI) != 0 && pNLMsg->nlmsg_type != NLMSG_DONE)
+ {
+ ssize_t len = readCount - ((char*)pNLMsg - buff);
+ pNLMsg = NLMSG_NEXT(pNLMsg, len);
+ }
+ else
+ break; // all done!
+ }
+
+ return result;
+}
+
+#else // USES_NETLINK
+
+// Open a socket that will receive interface change notifications
+mDNSlocal mStatus OpenIfNotifySocket(int *pFD)
+{
+ *pFD = socket(AF_ROUTE, SOCK_RAW, 0);
+
+ if (*pFD < 0)
+ return mStatus_UnknownErr;
+
+ // Configure read to be non-blocking because inbound msg size is not known in advance
+ (void) fcntl(*pFD, F_SETFL, O_NONBLOCK);
+
+ return mStatus_NoError;
+}
+
+#if MDNS_DEBUGMSGS
+mDNSlocal void PrintRoutingSocketMsg(const struct ifa_msghdr *pRSMsg)
+{
+ const char *kRSMsgTypes[] = { "", "RTM_ADD", "RTM_DELETE", "RTM_CHANGE", "RTM_GET", "RTM_LOSING",
+ "RTM_REDIRECT", "RTM_MISS", "RTM_LOCK", "RTM_OLDADD", "RTM_OLDDEL", "RTM_RESOLVE",
+ "RTM_NEWADDR", "RTM_DELADDR", "RTM_IFINFO", "RTM_NEWMADDR", "RTM_DELMADDR" };
+
+ int index = pRSMsg->ifam_type == RTM_IFINFO ? ((struct if_msghdr*) pRSMsg)->ifm_index : pRSMsg->ifam_index;
+
+ printf("ifa_msghdr len=%d, type=%s, index=%d\n", pRSMsg->ifam_msglen, kRSMsgTypes[pRSMsg->ifam_type], index);
+}
+#endif
+
+mDNSlocal mDNSu32 ProcessRoutingNotification(int sd)
+// Read through the messages on sd and if any indicate that any interface records should
+// be torn down and rebuilt, return affected indices as a bitmask. Otherwise return 0.
+{
+ ssize_t readCount;
+ char buff[4096];
+ struct ifa_msghdr *pRSMsg = (struct ifa_msghdr*) buff;
+ mDNSu32 result = 0;
+
+ readCount = read(sd, buff, sizeof buff);
+ if (readCount < (ssize_t) sizeof(struct ifa_msghdr))
+ return mStatus_UnsupportedErr; // cannot decipher message
+
+#if MDNS_DEBUGMSGS
+ PrintRoutingSocketMsg(pRSMsg);
+#endif
+
+ // Process the message
+ if (pRSMsg->ifam_type == RTM_NEWADDR || pRSMsg->ifam_type == RTM_DELADDR ||
+ pRSMsg->ifam_type == RTM_IFINFO)
+ {
+ if (pRSMsg->ifam_type == RTM_IFINFO)
+ result |= 1 << ((struct if_msghdr*) pRSMsg)->ifm_index;
+ else
+ result |= 1 << pRSMsg->ifam_index;
+ }
+
+ return result;
+}
+
+#endif // USES_NETLINK
+
+// Called when data appears on interface change notification socket
+mDNSlocal void InterfaceChangeCallback(int fd, short filter, void *context)
+{
+ IfChangeRec *pChgRec = (IfChangeRec*) context;
+ fd_set readFDs;
+ mDNSu32 changedInterfaces = 0;
+ struct timeval zeroTimeout = { 0, 0 };
+
+ (void)fd; // Unused
+ (void)filter; // Unused
+
+ FD_ZERO(&readFDs);
+ FD_SET(pChgRec->NotifySD, &readFDs);
+
+ do
+ {
+ changedInterfaces |= ProcessRoutingNotification(pChgRec->NotifySD);
+ }
+ while (0 < select(pChgRec->NotifySD + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout));
+
+ // Currently we rebuild the entire interface list whenever any interface change is
+ // detected. If this ever proves to be a performance issue in a multi-homed
+ // configuration, more care should be paid to changedInterfaces.
+ if (changedInterfaces)
+ mDNSPlatformPosixRefreshInterfaceList(pChgRec->mDNS);
+}
+
+// Register with either a Routing Socket or RtNetLink to listen for interface changes.
+mDNSlocal mStatus WatchForInterfaceChange(mDNS *const m)
+{
+ mStatus err;
+ IfChangeRec *pChgRec;
+
+ pChgRec = (IfChangeRec*) mDNSPlatformMemAllocate(sizeof *pChgRec);
+ if (pChgRec == NULL)
+ return mStatus_NoMemoryErr;
+
+ pChgRec->mDNS = m;
+ err = OpenIfNotifySocket(&pChgRec->NotifySD);
+ if (err == 0)
+ err = mDNSPosixAddFDToEventLoop(pChgRec->NotifySD, InterfaceChangeCallback, pChgRec);
+
+ return err;
+}
+
+// Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT.
+// If we fail, someone else got here first. That's not a big problem; we can share the port for multicast responses --
+// we just need to be aware that we shouldn't expect to successfully receive unicast UDP responses.
+mDNSlocal mDNSBool mDNSPlatformInit_CanReceiveUnicast(void)
+{
+ int err;
+ int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ struct sockaddr_in s5353;
+ s5353.sin_family = AF_INET;
+ s5353.sin_port = MulticastDNSPort.NotAnInteger;
+ s5353.sin_addr.s_addr = 0;
+ err = bind(s, (struct sockaddr *)&s5353, sizeof(s5353));
+ close(s);
+ if (err) debugf("No unicast UDP responses");
+ else debugf("Unicast UDP responses okay");
+ return(err == 0);
+}
+
+// mDNS core calls this routine to initialise the platform-specific data.
+mDNSexport mStatus mDNSPlatformInit(mDNS *const m)
+{
+ int err = 0;
+ struct sockaddr sa;
+ assert(m != NULL);
+
+ if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue;
+
+ // Tell mDNS core the names of this machine.
+
+ // Set up the nice label
+ m->nicelabel.c[0] = 0;
+ GetUserSpecifiedFriendlyComputerName(&m->nicelabel);
+ if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Computer");
+
+ // Set up the RFC 1034-compliant label
+ m->hostlabel.c[0] = 0;
+ GetUserSpecifiedRFC1034ComputerName(&m->hostlabel);
+ if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Computer");
+
+ mDNS_SetFQDN(m);
+
+ sa.sa_family = AF_INET;
+ m->p->unicastSocket4 = -1;
+ if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket4);
+#if HAVE_IPV6
+ sa.sa_family = AF_INET6;
+ m->p->unicastSocket6 = -1;
+ if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket6);
+#endif
+
+ // Tell mDNS core about the network interfaces on this machine.
+ if (err == mStatus_NoError) err = SetupInterfaceList(m);
+
+ // Tell mDNS core about DNS Servers
+ mDNS_Lock(m);
+ if (err == mStatus_NoError) ParseDNSServers(m, uDNS_SERVERS_FILE);
+ mDNS_Unlock(m);
+
+ if (err == mStatus_NoError)
+ {
+ err = WatchForInterfaceChange(m);
+ // Failure to observe interface changes is non-fatal.
+ if (err != mStatus_NoError)
+ {
+ fprintf(stderr, "mDNS(%d) WARNING: Unable to detect interface changes (%d).\n", getpid(), err);
+ err = mStatus_NoError;
+ }
+ }
+
+ // We don't do asynchronous initialization on the Posix platform, so by the time
+ // we get here the setup will already have succeeded or failed. If it succeeded,
+ // we should just call mDNSCoreInitComplete() immediately.
+ if (err == mStatus_NoError)
+ mDNSCoreInitComplete(m, mStatus_NoError);
+
+ return PosixErrorToStatus(err);
+}
+
+// mDNS core calls this routine to clean up the platform-specific data.
+// In our case all we need to do is to tear down every network interface.
+mDNSexport void mDNSPlatformClose(mDNS *const m)
+{
+ assert(m != NULL);
+ ClearInterfaceList(m);
+ if (m->p->unicastSocket4 != -1) assert(close(m->p->unicastSocket4) == 0);
+#if HAVE_IPV6
+ if (m->p->unicastSocket6 != -1) assert(close(m->p->unicastSocket6) == 0);
+#endif
+}
+
+mDNSexport mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m)
+{
+ int err;
+ ClearInterfaceList(m);
+ err = SetupInterfaceList(m);
+ return PosixErrorToStatus(err);
+}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Locking
+#endif
+
+// On the Posix platform, locking is a no-op because we only ever enter
+// mDNS core on the main thread.
+
+// mDNS core calls this routine when it wants to prevent
+// the platform from reentering mDNS core code.
+mDNSexport void mDNSPlatformLock (const mDNS *const m)
+{
+ (void) m; // Unused
+}
+
+// mDNS core calls this routine when it release the lock taken by
+// mDNSPlatformLock and allow the platform to reenter mDNS core code.
+mDNSexport void mDNSPlatformUnlock (const mDNS *const m)
+{
+ (void) m; // Unused
+}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Strings
+#endif
+
+// mDNS core calls this routine to copy C strings.
+// On the Posix platform this maps directly to the ANSI C strcpy.
+mDNSexport void mDNSPlatformStrCopy(void *dst, const void *src)
+{
+ strcpy((char *)dst, (char *)src);
+}
+
+// mDNS core calls this routine to get the length of a C string.
+// On the Posix platform this maps directly to the ANSI C strlen.
+mDNSexport mDNSu32 mDNSPlatformStrLen (const void *src)
+{
+ return strlen((char*)src);
+}
+
+// mDNS core calls this routine to copy memory.
+// On the Posix platform this maps directly to the ANSI C memcpy.
+mDNSexport void mDNSPlatformMemCopy(void *dst, const void *src, mDNSu32 len)
+{
+ memcpy(dst, src, len);
+}
+
+// mDNS core calls this routine to test whether blocks of memory are byte-for-byte
+// identical. On the Posix platform this is a simple wrapper around ANSI C memcmp.
+mDNSexport mDNSBool mDNSPlatformMemSame(const void *dst, const void *src, mDNSu32 len)
+{
+ return memcmp(dst, src, len) == 0;
+}
+
+// If the caller wants to know the exact return of memcmp, then use this instead
+// of mDNSPlatformMemSame
+mDNSexport int mDNSPlatformMemCmp(const void *dst, const void *src, mDNSu32 len)
+{
+ return (memcmp(dst, src, len));
+}
+
+mDNSexport void mDNSPlatformQsort(void *base, int nel, int width, int (*compar)(const void *, const void *))
+{
+ return (qsort(base, nel, width, compar));
+}
+
+// DNSSEC stub functions
+mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q)
+{
+ (void)m;
+ (void)dv;
+ (void)q;
+}
+
+mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode)
+{
+ (void)m;
+ (void)crlist;
+ (void)negcr;
+ (void)rcode;
+ return mDNSfalse;
+}
+
+mDNSexport void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value)
+{
+ (void)m;
+ (void)action;
+ (void)type;
+ (void)value;
+}
+
+// Proxy stub functions
+mDNSexport mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit)
+{
+ (void) q;
+ (void) h;
+ (void) msg;
+ (void) ptr;
+ (void) limit;
+
+ return ptr;
+}
+
+mDNSexport void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[], mDNSu32 OpIf)
+{
+ (void) m;
+ (void) IpIfArr;
+ (void) OpIf;
+}
+
+mDNSexport void DNSProxyTerminate(mDNS *const m)
+{
+ (void) m;
+}
+
+// mDNS core calls this routine to clear blocks of memory.
+// On the Posix platform this is a simple wrapper around ANSI C memset.
+mDNSexport void mDNSPlatformMemZero(void *dst, mDNSu32 len)
+{
+ memset(dst, 0, len);
+}
+
+mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(malloc(len)); }
+mDNSexport void mDNSPlatformMemFree (void *mem) { free(mem); }
+
+mDNSexport mDNSu32 mDNSPlatformRandomSeed(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return(tv.tv_usec);
+}
+
+mDNSexport mDNSs32 mDNSPlatformOneSecond = 1024;
+
+mDNSexport mStatus mDNSPlatformTimeInit(void)
+{
+ // No special setup is required on Posix -- we just use gettimeofday();
+ // This is not really safe, because gettimeofday can go backwards if the user manually changes the date or time
+ // We should find a better way to do this
+ return(mStatus_NoError);
+}
+
+mDNSexport mDNSs32 mDNSPlatformRawTime()
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ // tv.tv_sec is seconds since 1st January 1970 (GMT, with no adjustment for daylight savings time)
+ // tv.tv_usec is microseconds since the start of this second (i.e. values 0 to 999999)
+ // We use the lower 22 bits of tv.tv_sec for the top 22 bits of our result
+ // and we multiply tv.tv_usec by 16 / 15625 to get a value in the range 0-1023 to go in the bottom 10 bits.
+ // This gives us a proper modular (cyclic) counter that has a resolution of roughly 1ms (actually 1/1024 second)
+ // and correctly cycles every 2^22 seconds (4194304 seconds = approx 48 days).
+ return((tv.tv_sec << 10) | (tv.tv_usec * 16 / 15625));
+}
+
+mDNSexport mDNSs32 mDNSPlatformUTC(void)
+{
+ return time(NULL);
+}
+
+mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration)
+{
+ (void) m;
+ (void) InterfaceID;
+ (void) EthAddr;
+ (void) IPAddr;
+ (void) iteration;
+}
+
+mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf)
+{
+ (void) rr;
+ (void) intf;
+
+ return 1;
+}
+
+mDNSexport mDNSBool mDNSPlatformValidQuestionForInterface(DNSQuestion *q, const NetworkInterfaceInfo *intf)
+{
+ (void) q;
+ (void) intf;
+
+ return 1;
+}
+
+// Used for debugging purposes. For now, just set the buffer to zero
+mDNSexport void mDNSPlatformFormatTime(unsigned long te, mDNSu8 *buf, int bufsize)
+{
+ (void) te;
+ if (bufsize) buf[0] = 0;
+}
+
+mDNSexport void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu32 seq, mDNSu32 ack, mDNSu16 win)
+{
+ (void) sadd; // Unused
+ (void) dadd; // Unused
+ (void) lport; // Unused
+ (void) rport; // Unused
+ (void) seq; // Unused
+ (void) ack; // Unused
+ (void) win; // Unused
+}
+
+mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti)
+{
+ (void) m; // Unused
+ (void) laddr; // Unused
+ (void) raddr; // Unused
+ (void) lport; // Unused
+ (void) rport; // Unused
+ (void) mti; // Unused
+
+ return mStatus_NoError;
+}
+
+mDNSexport mStatus mDNSPlatformGetRemoteMacAddr(mDNS *const m, mDNSAddr *raddr)
+{
+ (void) raddr; // Unused
+ (void) m; // Unused
+
+ return mStatus_NoError;
+}
+
+mDNSexport mStatus mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname)
+{
+ (void) spsaddr; // Unused
+ (void) ifname; // Unused
+
+ return mStatus_NoError;
+}
+
+mDNSexport mStatus mDNSPlatformClearSPSMACAddr(void)
+{
+ return mStatus_NoError;
+}
+
+mDNSexport mDNSu16 mDNSPlatformGetUDPPort(UDPSocket *sock)
+{
+ (void) sock; // unused
+
+ return (mDNSu16)-1;
+}
+
+mDNSexport mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID)
+{
+ (void) InterfaceID; // unused
+
+ return mDNSfalse;
+}
+
+mDNSexport mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q)
+{
+ (void) m;
+ (void) q;
+ return mDNStrue;
+}
+
+mDNSexport mDNSs32 mDNSPlatformGetServiceID(mDNS *const m, DNSQuestion *q)
+{
+ (void) m;
+ (void) q;
+ return -1;
+}
+
+mDNSexport void mDNSPlatformSetDelegatePID(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q)
+{
+ (void) src;
+ (void) dst;
+ (void) q;
+}
+
+mDNSexport mDNSs32 mDNSPlatformGetPID()
+{
+ return 0;
+}
+
+mDNSlocal void mDNSPosixAddToFDSet(int *nfds, fd_set *readfds, int s)
+{
+ if (*nfds < s + 1) *nfds = s + 1;
+ FD_SET(s, readfds);
+}
+
+mDNSexport void mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, struct timeval *timeout)
+{
+ mDNSs32 ticks;
+ struct timeval interval;
+
+ // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do
+ mDNSs32 nextevent = mDNS_Execute(m);
+
+ // 2. Build our list of active file descriptors
+ PosixNetworkInterface *info = (PosixNetworkInterface *)(m->HostInterfaces);
+ if (m->p->unicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket4);
+#if HAVE_IPV6
+ if (m->p->unicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket6);
+#endif
+ while (info)
+ {
+ if (info->multicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket4);
+#if HAVE_IPV6
+ if (info->multicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket6);
+#endif
+ info = (PosixNetworkInterface *)(info->coreIntf.next);
+ }
+
+ // 3. Calculate the time remaining to the next scheduled event (in struct timeval format)
+ ticks = nextevent - mDNS_TimeNow(m);
+ if (ticks < 1) ticks = 1;
+ interval.tv_sec = ticks >> 10; // The high 22 bits are seconds
+ interval.tv_usec = ((ticks & 0x3FF) * 15625) / 16; // The low 10 bits are 1024ths
+
+ // 4. If client's proposed timeout is more than what we want, then reduce it
+ if (timeout->tv_sec > interval.tv_sec ||
+ (timeout->tv_sec == interval.tv_sec && timeout->tv_usec > interval.tv_usec))
+ *timeout = interval;
+}
+
+mDNSexport void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds)
+{
+ PosixNetworkInterface *info;
+ assert(m != NULL);
+ assert(readfds != NULL);
+ info = (PosixNetworkInterface *)(m->HostInterfaces);
+
+ if (m->p->unicastSocket4 != -1 && FD_ISSET(m->p->unicastSocket4, readfds))
+ {
+ FD_CLR(m->p->unicastSocket4, readfds);
+ SocketDataReady(m, NULL, m->p->unicastSocket4);
+ }
+#if HAVE_IPV6
+ if (m->p->unicastSocket6 != -1 && FD_ISSET(m->p->unicastSocket6, readfds))
+ {
+ FD_CLR(m->p->unicastSocket6, readfds);
+ SocketDataReady(m, NULL, m->p->unicastSocket6);
+ }
+#endif
+
+ while (info)
+ {
+ if (info->multicastSocket4 != -1 && FD_ISSET(info->multicastSocket4, readfds))
+ {
+ FD_CLR(info->multicastSocket4, readfds);
+ SocketDataReady(m, info, info->multicastSocket4);
+ }
+#if HAVE_IPV6
+ if (info->multicastSocket6 != -1 && FD_ISSET(info->multicastSocket6, readfds))
+ {
+ FD_CLR(info->multicastSocket6, readfds);
+ SocketDataReady(m, info, info->multicastSocket6);
+ }
+#endif
+ info = (PosixNetworkInterface *)(info->coreIntf.next);
+ }
+}
+
+// update gMaxFD
+mDNSlocal void DetermineMaxEventFD(void)
+{
+ PosixEventSource *iSource;
+
+ gMaxFD = 0;
+ for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next)
+ if (gMaxFD < iSource->fd)
+ gMaxFD = iSource->fd;
+}
+
+// Add a file descriptor to the set that mDNSPosixRunEventLoopOnce() listens to.
+mStatus mDNSPosixAddFDToEventLoop(int fd, mDNSPosixEventCallback callback, void *context)
+{
+ PosixEventSource *newSource;
+
+ if (gEventSources.LinkOffset == 0)
+ InitLinkedList(&gEventSources, offsetof(PosixEventSource, Next));
+
+ if (fd >= (int) FD_SETSIZE || fd < 0)
+ return mStatus_UnsupportedErr;
+ if (callback == NULL)
+ return mStatus_BadParamErr;
+
+ newSource = (PosixEventSource*) malloc(sizeof *newSource);
+ if (NULL == newSource)
+ return mStatus_NoMemoryErr;
+
+ newSource->Callback = callback;
+ newSource->Context = context;
+ newSource->fd = fd;
+
+ AddToTail(&gEventSources, newSource);
+ FD_SET(fd, &gEventFDs);
+
+ DetermineMaxEventFD();
+
+ return mStatus_NoError;
+}
+
+// Remove a file descriptor from the set that mDNSPosixRunEventLoopOnce() listens to.
+mStatus mDNSPosixRemoveFDFromEventLoop(int fd)
+{
+ PosixEventSource *iSource;
+
+ for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next)
+ {
+ if (fd == iSource->fd)
+ {
+ FD_CLR(fd, &gEventFDs);
+ RemoveFromList(&gEventSources, iSource);
+ free(iSource);
+ DetermineMaxEventFD();
+ return mStatus_NoError;
+ }
+ }
+ return mStatus_NoSuchNameErr;
+}
+
+// Simply note the received signal in gEventSignals.
+mDNSlocal void NoteSignal(int signum)
+{
+ sigaddset(&gEventSignals, signum);
+}
+
+// Tell the event package to listen for signal and report it in mDNSPosixRunEventLoopOnce().
+mStatus mDNSPosixListenForSignalInEventLoop(int signum)
+{
+ struct sigaction action;
+ mStatus err;
+
+ mDNSPlatformMemZero(&action, sizeof action); // more portable than member-wise assignment
+ action.sa_handler = NoteSignal;
+ err = sigaction(signum, &action, (struct sigaction*) NULL);
+
+ sigaddset(&gEventSignalSet, signum);
+
+ return err;
+}
+
+// Tell the event package to stop listening for signal in mDNSPosixRunEventLoopOnce().
+mStatus mDNSPosixIgnoreSignalInEventLoop(int signum)
+{
+ struct sigaction action;
+ mStatus err;
+
+ mDNSPlatformMemZero(&action, sizeof action); // more portable than member-wise assignment
+ action.sa_handler = SIG_DFL;
+ err = sigaction(signum, &action, (struct sigaction*) NULL);
+
+ sigdelset(&gEventSignalSet, signum);
+
+ return err;
+}
+
+// Do a single pass through the attendent event sources and dispatch any found to their callbacks.
+// Return as soon as internal timeout expires, or a signal we're listening for is received.
+mStatus mDNSPosixRunEventLoopOnce(mDNS *m, const struct timeval *pTimeout,
+ sigset_t *pSignalsReceived, mDNSBool *pDataDispatched)
+{
+ fd_set listenFDs = gEventFDs;
+ int fdMax = 0, numReady;
+ struct timeval timeout = *pTimeout;
+
+ // Include the sockets that are listening to the wire in our select() set
+ mDNSPosixGetFDSet(m, &fdMax, &listenFDs, &timeout); // timeout may get modified
+ if (fdMax < gMaxFD)
+ fdMax = gMaxFD;
+
+ numReady = select(fdMax + 1, &listenFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout);
+
+ // If any data appeared, invoke its callback
+ if (numReady > 0)
+ {
+ PosixEventSource *iSource;
+
+ (void) mDNSPosixProcessFDSet(m, &listenFDs); // call this first to process wire data for clients
+
+ for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next)
+ {
+ if (FD_ISSET(iSource->fd, &listenFDs))
+ {
+ iSource->Callback(iSource->fd, 0, iSource->Context);
+ break; // in case callback removed elements from gEventSources
+ }
+ }
+ *pDataDispatched = mDNStrue;
+ }
+ else
+ *pDataDispatched = mDNSfalse;
+
+ (void) sigprocmask(SIG_BLOCK, &gEventSignalSet, (sigset_t*) NULL);
+ *pSignalsReceived = gEventSignals;
+ sigemptyset(&gEventSignals);
+ (void) sigprocmask(SIG_UNBLOCK, &gEventSignalSet, (sigset_t*) NULL);
+
+ return mStatus_NoError;
+}
diff --git a/mDNSResponder/mDNSPosix/mDNSPosix.h b/mDNSResponder/mDNSPosix/mDNSPosix.h
new file mode 100755
index 00000000..f9fcea78
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/mDNSPosix.h
@@ -0,0 +1,85 @@
+/* -*- 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.
+ */
+
+#ifndef __mDNSPlatformPosix_h
+#define __mDNSPlatformPosix_h
+
+#include <signal.h>
+#include <sys/time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// PosixNetworkInterface is a record extension of the core NetworkInterfaceInfo
+// type that supports extra fields needed by the Posix platform.
+//
+// IMPORTANT: coreIntf must be the first field in the structure because
+// we cast between pointers to the two different types regularly.
+
+typedef struct PosixNetworkInterface PosixNetworkInterface;
+
+struct PosixNetworkInterface
+{
+ NetworkInterfaceInfo coreIntf;
+ const char * intfName;
+ PosixNetworkInterface * aliasIntf;
+ int index;
+ int multicastSocket4;
+#if HAVE_IPV6
+ int multicastSocket6;
+#endif
+};
+
+// This is a global because debugf_() needs to be able to check its value
+extern int gMDNSPlatformPosixVerboseLevel;
+
+struct mDNS_PlatformSupport_struct
+{
+ int unicastSocket4;
+#if HAVE_IPV6
+ int unicastSocket6;
+#endif
+};
+
+#define uDNS_SERVERS_FILE "/etc/resolv.conf"
+extern int ParseDNSServers(mDNS *m, const char *filePath);
+extern mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m);
+// See comment in implementation.
+
+// Call mDNSPosixGetFDSet before calling select(), to update the parameters
+// as may be necessary to meet the needs of the mDNSCore code.
+// The timeout pointer MUST NOT be NULL.
+// Set timeout->tv_sec to 0x3FFFFFFF if you want to have effectively no timeout
+// After calling mDNSPosixGetFDSet(), call select(nfds, &readfds, NULL, NULL, &timeout); as usual
+// After select() returns, call mDNSPosixProcessFDSet() to let mDNSCore do its work
+extern void mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, struct timeval *timeout);
+extern void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds);
+
+typedef void (*mDNSPosixEventCallback)(int fd, short filter, void *context);
+
+extern mStatus mDNSPosixAddFDToEventLoop( int fd, mDNSPosixEventCallback callback, void *context);
+extern mStatus mDNSPosixRemoveFDFromEventLoop( int fd);
+extern mStatus mDNSPosixListenForSignalInEventLoop( int signum);
+extern mStatus mDNSPosixIgnoreSignalInEventLoop( int signum);
+extern mStatus mDNSPosixRunEventLoopOnce( mDNS *m, const struct timeval *pTimeout, sigset_t *pSignalsReceived, mDNSBool *pDataDispatched);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/mDNSResponder/mDNSPosix/mDNSUNP.c b/mDNSResponder/mDNSPosix/mDNSUNP.c
new file mode 100755
index 00000000..b392fc74
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/mDNSUNP.c
@@ -0,0 +1,719 @@
+/* -*- 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.
+ */
+
+#include "mDNSUNP.h"
+
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+
+/* Some weird platforms derived from 4.4BSD Lite (e.g. EFI) need the ALIGN(P)
+ macro, usually defined in <sys/param.h> or someplace like that, to make sure the
+ CMSG_NXTHDR macro is well-formed. On such platforms, the symbol NEED_ALIGN_MACRO
+ should be set to the name of the header to include to get the ALIGN(P) macro.
+ */
+#ifdef NEED_ALIGN_MACRO
+#include NEED_ALIGN_MACRO
+#endif
+
+/* Solaris defined SIOCGIFCONF etc in <sys/sockio.h> but
+ other platforms don't even have that include file. So,
+ if we haven't yet got a definition, let's try to find
+ <sys/sockio.h>.
+ */
+
+#ifndef SIOCGIFCONF
+ #include <sys/sockio.h>
+#endif
+
+/* sockaddr_dl is only referenced if we're using IP_RECVIF,
+ so only include the header in that case.
+ */
+
+#ifdef IP_RECVIF
+ #include <net/if_dl.h>
+#endif
+
+#if defined(AF_INET6) && HAVE_IPV6 && !HAVE_LINUX
+#include <net/if_var.h>
+#include <netinet/in_var.h>
+// Note: netinet/in_var.h implicitly includes netinet6/in6_var.h for us
+#endif
+
+#if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX
+#include <netdb.h>
+#include <arpa/inet.h>
+
+/* Converts a prefix length to IPv6 network mask */
+void plen_to_mask(int plen, char *addr) {
+ int i;
+ int colons=7; /* Number of colons in IPv6 address */
+ int bits_in_block=16; /* Bits per IPv6 block */
+ for(i=0; i<=colons; i++) {
+ int block, ones=0xffff, ones_in_block;
+ if (plen>bits_in_block) ones_in_block=bits_in_block;
+ else ones_in_block=plen;
+ block = ones & (ones << (bits_in_block-ones_in_block));
+ i==0 ? sprintf(addr, "%x", block) : sprintf(addr, "%s:%x", addr, block);
+ plen -= ones_in_block;
+ }
+}
+
+/* Gets IPv6 interface information from the /proc filesystem in linux*/
+struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases)
+{
+ struct ifi_info *ifi, *ifihead, **ifipnext, *ifipold, **ifiptr;
+ FILE *fp;
+ char addr[8][5];
+ int flags, myflags, index, plen, scope;
+ char ifname[9], lastname[IFNAMSIZ];
+ char addr6[32+7+1]; /* don't forget the seven ':' */
+ struct addrinfo hints, *res0;
+ struct sockaddr_in6 *sin6;
+ struct in6_addr *addrptr;
+ int err;
+ int sockfd = -1;
+ struct ifreq ifr;
+
+ res0=NULL;
+ ifihead = NULL;
+ ifipnext = &ifihead;
+ lastname[0] = 0;
+
+ if ((fp = fopen(PROC_IFINET6_PATH, "r")) != NULL) {
+ sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (sockfd < 0) {
+ goto gotError;
+ }
+ while (fscanf(fp,
+ "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %8s\n",
+ addr[0],addr[1],addr[2],addr[3],
+ addr[4],addr[5],addr[6],addr[7],
+ &index, &plen, &scope, &flags, ifname) != EOF) {
+
+ myflags = 0;
+ if (strncmp(lastname, ifname, IFNAMSIZ) == 0) {
+ if (doaliases == 0)
+ continue; /* already processed this interface */
+ myflags = IFI_ALIAS;
+ }
+ memcpy(lastname, ifname, IFNAMSIZ);
+ ifi = (struct ifi_info*)calloc(1, sizeof(struct ifi_info));
+ if (ifi == NULL) {
+ goto gotError;
+ }
+
+ ifipold = *ifipnext; /* need this later */
+ ifiptr = ifipnext;
+ *ifipnext = ifi; /* prev points to this new one */
+ ifipnext = &ifi->ifi_next; /* pointer to next one goes here */
+
+ sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
+ addr[0],addr[1],addr[2],addr[3],
+ addr[4],addr[5],addr[6],addr[7]);
+
+ /* Add address of the interface */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_flags = AI_NUMERICHOST;
+ err = getaddrinfo(addr6, NULL, &hints, &res0);
+ if (err) {
+ goto gotError;
+ }
+ ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in6));
+ if (ifi->ifi_addr == NULL) {
+ goto gotError;
+ }
+ memcpy(ifi->ifi_addr, res0->ai_addr, sizeof(struct sockaddr_in6));
+
+ /* Add netmask of the interface */
+ char ipv6addr[INET6_ADDRSTRLEN];
+ plen_to_mask(plen, ipv6addr);
+ ifi->ifi_netmask = calloc(1, sizeof(struct sockaddr_in6));
+ if (ifi->ifi_addr == NULL) {
+ goto gotError;
+ }
+ sin6=calloc(1, sizeof(struct sockaddr_in6));
+ addrptr=calloc(1, sizeof(struct in6_addr));
+ inet_pton(family, ipv6addr, addrptr);
+ sin6->sin6_family=family;
+ sin6->sin6_addr=*addrptr;
+ sin6->sin6_scope_id=scope;
+ memcpy(ifi->ifi_netmask, sin6, sizeof(struct sockaddr_in6));
+ free(sin6);
+
+
+ /* Add interface name */
+ memcpy(ifi->ifi_name, ifname, IFI_NAME);
+
+ /* Add interface index */
+ ifi->ifi_index = index;
+
+ /* Add interface flags*/
+ memcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
+ if (errno == EADDRNOTAVAIL) {
+ /*
+ * If the main interface is configured with no IP address but
+ * an alias interface exists with an IP address, you get
+ * EADDRNOTAVAIL for the main interface
+ */
+ free(ifi->ifi_addr);
+ free(ifi);
+ ifipnext = ifiptr;
+ *ifipnext = ifipold;
+ continue;
+ } else {
+ goto gotError;
+ }
+ }
+ ifi->ifi_flags = ifr.ifr_flags;
+ freeaddrinfo(res0);
+ res0=NULL;
+ }
+ }
+ goto done;
+
+gotError:
+ if (ifihead != NULL) {
+ free_ifi_info(ifihead);
+ ifihead = NULL;
+ }
+ if (res0 != NULL) {
+ freeaddrinfo(res0);
+ res0=NULL;
+ }
+done:
+ if (sockfd != -1) {
+ assert(close(sockfd) == 0);
+ }
+ return(ifihead); /* pointer to first structure in linked list */
+}
+#endif // defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX
+
+struct ifi_info *get_ifi_info(int family, int doaliases)
+{
+ int junk;
+ struct ifi_info *ifi, *ifihead, **ifipnext, *ifipold, **ifiptr;
+ int sockfd, sockf6, len, lastlen, flags, myflags;
+#ifdef NOT_HAVE_IF_NAMETOINDEX
+ int index = 200;
+#endif
+ char *ptr, *buf, lastname[IFNAMSIZ], *cptr;
+ struct ifconf ifc;
+ struct ifreq *ifr, ifrcopy;
+ struct sockaddr_in *sinptr;
+
+#if defined(AF_INET6) && HAVE_IPV6
+ struct sockaddr_in6 *sinptr6;
+#endif
+
+#if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX
+ if (family == AF_INET6) return get_ifi_info_linuxv6(family, doaliases);
+#endif
+
+ sockfd = -1;
+ sockf6 = -1;
+ buf = NULL;
+ ifihead = NULL;
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0) {
+ goto gotError;
+ }
+
+ lastlen = 0;
+ len = 100 * sizeof(struct ifreq); /* initial buffer size guess */
+ for ( ; ; ) {
+ buf = (char*)malloc(len);
+ if (buf == NULL) {
+ goto gotError;
+ }
+ ifc.ifc_len = len;
+ ifc.ifc_buf = buf;
+ if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
+ if (errno != EINVAL || lastlen != 0) {
+ goto gotError;
+ }
+ } else {
+ if (ifc.ifc_len == lastlen)
+ break; /* success, len has not changed */
+ lastlen = ifc.ifc_len;
+ }
+ len += 10 * sizeof(struct ifreq); /* increment */
+ free(buf);
+ }
+ ifihead = NULL;
+ ifipnext = &ifihead;
+ lastname[0] = 0;
+/* end get_ifi_info1 */
+
+/* include get_ifi_info2 */
+ for (ptr = buf; ptr < buf + ifc.ifc_len; ) {
+ ifr = (struct ifreq *) ptr;
+
+ /* Advance to next one in buffer */
+ if (sizeof(struct ifreq) > sizeof(ifr->ifr_name) + GET_SA_LEN(ifr->ifr_addr))
+ ptr += sizeof(struct ifreq);
+ else
+ ptr += sizeof(ifr->ifr_name) + GET_SA_LEN(ifr->ifr_addr);
+
+// fprintf(stderr, "intf %p name=%s AF=%d\n", index, ifr->ifr_name, ifr->ifr_addr.sa_family);
+
+ if (ifr->ifr_addr.sa_family != family)
+ continue; /* ignore if not desired address family */
+
+ myflags = 0;
+ if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL)
+ *cptr = 0; /* replace colon will null */
+ if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) {
+ if (doaliases == 0)
+ continue; /* already processed this interface */
+ myflags = IFI_ALIAS;
+ }
+ memcpy(lastname, ifr->ifr_name, IFNAMSIZ);
+
+ ifrcopy = *ifr;
+ if (ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy) < 0) {
+ goto gotError;
+ }
+
+ flags = ifrcopy.ifr_flags;
+ if ((flags & IFF_UP) == 0)
+ continue; /* ignore if interface not up */
+
+ ifi = (struct ifi_info*)calloc(1, sizeof(struct ifi_info));
+ if (ifi == NULL) {
+ goto gotError;
+ }
+ ifipold = *ifipnext; /* need this later */
+ ifiptr = ifipnext;
+ *ifipnext = ifi; /* prev points to this new one */
+ ifipnext = &ifi->ifi_next; /* pointer to next one goes here */
+
+ ifi->ifi_flags = flags; /* IFF_xxx values */
+ ifi->ifi_myflags = myflags; /* IFI_xxx values */
+#ifndef NOT_HAVE_IF_NAMETOINDEX
+ ifi->ifi_index = if_nametoindex(ifr->ifr_name);
+#else
+ ifrcopy = *ifr;
+#ifdef SIOCGIFINDEX
+ if ( 0 >= ioctl(sockfd, SIOCGIFINDEX, &ifrcopy))
+ ifi->ifi_index = ifrcopy.ifr_index;
+ else
+#endif
+ ifi->ifi_index = index++; /* SIOCGIFINDEX is broken on Solaris 2.5ish, so fake it */
+#endif
+ memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME);
+ ifi->ifi_name[IFI_NAME-1] = '\0';
+/* end get_ifi_info2 */
+/* include get_ifi_info3 */
+ switch (ifr->ifr_addr.sa_family) {
+ case AF_INET:
+ sinptr = (struct sockaddr_in *) &ifr->ifr_addr;
+ if (ifi->ifi_addr == NULL) {
+ ifi->ifi_addr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in));
+ if (ifi->ifi_addr == NULL) {
+ goto gotError;
+ }
+ memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));
+
+#ifdef SIOCGIFNETMASK
+ if (ioctl(sockfd, SIOCGIFNETMASK, &ifrcopy) < 0) {
+ if (errno == EADDRNOTAVAIL) {
+ /*
+ * If the main interface is configured with no IP address but
+ * an alias interface exists with an IP address, you get
+ * EADDRNOTAVAIL for the main interface
+ */
+ free(ifi->ifi_addr);
+ free(ifi);
+ ifipnext = ifiptr;
+ *ifipnext = ifipold;
+ continue;
+ } else {
+ goto gotError;
+ }
+ }
+
+ ifi->ifi_netmask = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in));
+ if (ifi->ifi_netmask == NULL) goto gotError;
+ sinptr = (struct sockaddr_in *) &ifrcopy.ifr_addr;
+ /* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */
+#ifndef NOT_HAVE_SA_LEN
+ sinptr->sin_len = sizeof(struct sockaddr_in);
+#endif
+ sinptr->sin_family = AF_INET;
+ memcpy(ifi->ifi_netmask, sinptr, sizeof(struct sockaddr_in));
+#endif
+
+#ifdef SIOCGIFBRDADDR
+ if (flags & IFF_BROADCAST) {
+ if (ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy) < 0) {
+ goto gotError;
+ }
+ sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr;
+ /* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */
+#ifndef NOT_HAVE_SA_LEN
+ sinptr->sin_len = sizeof( struct sockaddr_in );
+#endif
+ sinptr->sin_family = AF_INET;
+ ifi->ifi_brdaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in));
+ if (ifi->ifi_brdaddr == NULL) {
+ goto gotError;
+ }
+ memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));
+ }
+#endif
+
+#ifdef SIOCGIFDSTADDR
+ if (flags & IFF_POINTOPOINT) {
+ if (ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy) < 0) {
+ goto gotError;
+ }
+ sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr;
+ /* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */
+#ifndef NOT_HAVE_SA_LEN
+ sinptr->sin_len = sizeof( struct sockaddr_in );
+#endif
+ sinptr->sin_family = AF_INET;
+ ifi->ifi_dstaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in));
+ if (ifi->ifi_dstaddr == NULL) {
+ goto gotError;
+ }
+ memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in));
+ }
+#endif
+ }
+ break;
+
+#if defined(AF_INET6) && HAVE_IPV6
+ case AF_INET6:
+ sinptr6 = (struct sockaddr_in6 *) &ifr->ifr_addr;
+ if (ifi->ifi_addr == NULL) {
+ ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in6));
+ if (ifi->ifi_addr == NULL) {
+ goto gotError;
+ }
+
+ /* Some platforms (*BSD) inject the prefix in IPv6LL addresses */
+ /* We need to strip that out */
+ if (IN6_IS_ADDR_LINKLOCAL(&sinptr6->sin6_addr))
+ sinptr6->sin6_addr.s6_addr[2] = sinptr6->sin6_addr.s6_addr[3] = 0;
+ memcpy(ifi->ifi_addr, sinptr6, sizeof(struct sockaddr_in6));
+
+#ifdef SIOCGIFNETMASK_IN6
+ {
+ struct in6_ifreq ifr6;
+ if (sockf6 == -1)
+ sockf6 = socket(AF_INET6, SOCK_DGRAM, 0);
+ memset(&ifr6, 0, sizeof(ifr6));
+ memcpy(&ifr6.ifr_name, &ifr->ifr_name, sizeof(ifr6.ifr_name ));
+ memcpy(&ifr6.ifr_ifru.ifru_addr, &ifr->ifr_addr, sizeof(ifr6.ifr_ifru.ifru_addr));
+ if (ioctl(sockf6, SIOCGIFNETMASK_IN6, &ifr6) < 0) {
+ if (errno == EADDRNOTAVAIL) {
+ /*
+ * If the main interface is configured with no IP address but
+ * an alias interface exists with an IP address, you get
+ * EADDRNOTAVAIL for the main interface
+ */
+ free(ifi->ifi_addr);
+ free(ifi);
+ ifipnext = ifiptr;
+ *ifipnext = ifipold;
+ continue;
+ } else {
+ goto gotError;
+ }
+ }
+ ifi->ifi_netmask = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in6));
+ if (ifi->ifi_netmask == NULL) goto gotError;
+ sinptr6 = (struct sockaddr_in6 *) &ifr6.ifr_ifru.ifru_addr;
+ memcpy(ifi->ifi_netmask, sinptr6, sizeof(struct sockaddr_in6));
+ }
+#endif
+ }
+ break;
+#endif
+
+ default:
+ break;
+ }
+ }
+ goto done;
+
+gotError:
+ if (ifihead != NULL) {
+ free_ifi_info(ifihead);
+ ifihead = NULL;
+ }
+
+done:
+ if (buf != NULL) {
+ free(buf);
+ }
+ if (sockfd != -1) {
+ junk = close(sockfd);
+ assert(junk == 0);
+ }
+ if (sockf6 != -1) {
+ junk = close(sockf6);
+ assert(junk == 0);
+ }
+ return(ifihead); /* pointer to first structure in linked list */
+}
+/* end get_ifi_info3 */
+
+/* include free_ifi_info */
+void
+free_ifi_info(struct ifi_info *ifihead)
+{
+ struct ifi_info *ifi, *ifinext;
+
+ for (ifi = ifihead; ifi != NULL; ifi = ifinext) {
+ if (ifi->ifi_addr != NULL)
+ free(ifi->ifi_addr);
+ if (ifi->ifi_netmask != NULL)
+ free(ifi->ifi_netmask);
+ if (ifi->ifi_brdaddr != NULL)
+ free(ifi->ifi_brdaddr);
+ if (ifi->ifi_dstaddr != NULL)
+ free(ifi->ifi_dstaddr);
+ ifinext = ifi->ifi_next; /* can't fetch ifi_next after free() */
+ free(ifi); /* the ifi_info{} itself */
+ }
+}
+/* end free_ifi_info */
+
+ssize_t
+recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp,
+ struct sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp, u_char *ttl)
+{
+ struct msghdr msg;
+ struct iovec iov[1];
+ ssize_t n;
+
+#ifdef CMSG_FIRSTHDR
+ struct cmsghdr *cmptr;
+ union {
+ struct cmsghdr cm;
+ char control[1024];
+ } control_un;
+
+ *ttl = 255; // If kernel fails to provide TTL data then assume the TTL was 255 as it should be
+
+ msg.msg_control = control_un.control;
+ msg.msg_controllen = sizeof(control_un.control);
+ msg.msg_flags = 0;
+#else
+ memset(&msg, 0, sizeof(msg)); /* make certain msg_accrightslen = 0 */
+#endif /* CMSG_FIRSTHDR */
+
+ msg.msg_name = (char *) sa;
+ msg.msg_namelen = *salenptr;
+ iov[0].iov_base = (char *)ptr;
+ iov[0].iov_len = nbytes;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ if ( (n = recvmsg(fd, &msg, *flagsp)) < 0)
+ return(n);
+
+ *salenptr = msg.msg_namelen; /* pass back results */
+ if (pktp) {
+ /* 0.0.0.0, i/f = -1 */
+ /* We set the interface to -1 so that the caller can
+ tell whether we returned a meaningful value or
+ just some default. Previously this code just
+ set the value to 0, but I'm concerned that 0
+ might be a valid interface value.
+ */
+ memset(pktp, 0, sizeof(struct my_in_pktinfo));
+ pktp->ipi_ifindex = -1;
+ }
+/* end recvfrom_flags1 */
+
+/* include recvfrom_flags2 */
+#ifndef CMSG_FIRSTHDR
+ #warning CMSG_FIRSTHDR not defined. Will not be able to determine destination address, received interface, etc.
+ *flagsp = 0; /* pass back results */
+ return(n);
+#else
+
+ *flagsp = msg.msg_flags; /* pass back results */
+ if (msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr) ||
+ (msg.msg_flags & MSG_CTRUNC) || pktp == NULL)
+ return(n);
+
+ for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL;
+ cmptr = CMSG_NXTHDR(&msg, cmptr)) {
+
+#ifdef IP_PKTINFO
+#if in_pktinfo_definition_is_missing
+ struct in_pktinfo
+ {
+ int ipi_ifindex;
+ struct in_addr ipi_spec_dst;
+ struct in_addr ipi_addr;
+ };
+#endif
+ if (cmptr->cmsg_level == IPPROTO_IP &&
+ cmptr->cmsg_type == IP_PKTINFO) {
+ struct in_pktinfo *tmp;
+ struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr;
+
+ tmp = (struct in_pktinfo *) CMSG_DATA(cmptr);
+ sin->sin_family = AF_INET;
+ sin->sin_addr = tmp->ipi_addr;
+ sin->sin_port = 0;
+ pktp->ipi_ifindex = tmp->ipi_ifindex;
+ continue;
+ }
+#endif
+
+#ifdef IP_RECVDSTADDR
+ if (cmptr->cmsg_level == IPPROTO_IP &&
+ cmptr->cmsg_type == IP_RECVDSTADDR) {
+ struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr;
+
+ sin->sin_family = AF_INET;
+ sin->sin_addr = *(struct in_addr*)CMSG_DATA(cmptr);
+ sin->sin_port = 0;
+ continue;
+ }
+#endif
+
+#ifdef IP_RECVIF
+ if (cmptr->cmsg_level == IPPROTO_IP &&
+ cmptr->cmsg_type == IP_RECVIF) {
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA(cmptr);
+#ifndef HAVE_BROKEN_RECVIF_NAME
+ int nameLen = (sdl->sdl_nlen < IFI_NAME - 1) ? sdl->sdl_nlen : (IFI_NAME - 1);
+ strncpy(pktp->ipi_ifname, sdl->sdl_data, nameLen);
+#endif
+ pktp->ipi_ifindex = sdl->sdl_index;
+#ifdef HAVE_BROKEN_RECVIF_NAME
+ if (sdl->sdl_index == 0) {
+ pktp->ipi_ifindex = *(uint_t*)sdl;
+ }
+#endif
+ assert(pktp->ipi_ifname[IFI_NAME - 1] == 0);
+ // null terminated because of memset above
+ continue;
+ }
+#endif
+
+#ifdef IP_RECVTTL
+ if (cmptr->cmsg_level == IPPROTO_IP &&
+ cmptr->cmsg_type == IP_RECVTTL) {
+ *ttl = *(u_char*)CMSG_DATA(cmptr);
+ continue;
+ }
+ else if (cmptr->cmsg_level == IPPROTO_IP &&
+ cmptr->cmsg_type == IP_TTL) { // some implementations seem to send IP_TTL instead of IP_RECVTTL
+ *ttl = *(int*)CMSG_DATA(cmptr);
+ continue;
+ }
+#endif
+
+#if defined(IPV6_PKTINFO) && HAVE_IPV6
+ if (cmptr->cmsg_level == IPPROTO_IPV6 &&
+ cmptr->cmsg_type == IPV6_2292_PKTINFO) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&pktp->ipi_addr;
+ struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmptr);
+
+ sin6->sin6_family = AF_INET6;
+#ifndef NOT_HAVE_SA_LEN
+ sin6->sin6_len = sizeof(*sin6);
+#endif
+ sin6->sin6_addr = ip6_info->ipi6_addr;
+ sin6->sin6_flowinfo = 0;
+ sin6->sin6_scope_id = 0;
+ sin6->sin6_port = 0;
+ pktp->ipi_ifindex = ip6_info->ipi6_ifindex;
+ continue;
+ }
+#endif
+
+#if defined(IPV6_HOPLIMIT) && HAVE_IPV6
+ if (cmptr->cmsg_level == IPPROTO_IPV6 &&
+ cmptr->cmsg_type == IPV6_2292_HOPLIMIT) {
+ *ttl = *(int*)CMSG_DATA(cmptr);
+ continue;
+ }
+#endif
+ assert(0); // unknown ancillary data
+ }
+ return(n);
+#endif /* CMSG_FIRSTHDR */
+}
+
+// **********************************************************************************************
+
+// daemonize the process. Adapted from "Unix Network Programming" vol 1 by Stevens, section 12.4.
+// Returns 0 on success, -1 on failure.
+
+#ifdef NOT_HAVE_DAEMON
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+
+int daemon(int nochdir, int noclose)
+{
+ switch (fork())
+ {
+ case -1: return (-1); // Fork failed
+ case 0: break; // Child -- continue
+ default: _exit(0); // Parent -- exit
+ }
+
+ if (setsid() == -1) return(-1);
+
+ signal(SIGHUP, SIG_IGN);
+
+ switch (fork()) // Fork again, primarily for reasons of Unix trivia
+ {
+ case -1: return (-1); // Fork failed
+ case 0: break; // Child -- continue
+ default: _exit(0); // Parent -- exit
+ }
+
+ if (!nochdir) (void)chdir("/");
+ umask(0);
+
+ if (!noclose)
+ {
+ int fd = open("/dev/null", O_RDWR, 0);
+ if (fd != -1)
+ {
+ // Avoid unnecessarily duplicating a file descriptor to itself
+ if (fd != STDIN_FILENO) (void)dup2(fd, STDIN_FILENO);
+ if (fd != STDOUT_FILENO) (void)dup2(fd, STDOUT_FILENO);
+ if (fd != STDERR_FILENO) (void)dup2(fd, STDERR_FILENO);
+ if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO)
+ (void)close (fd);
+ }
+ }
+ return (0);
+}
+#endif /* NOT_HAVE_DAEMON */
diff --git a/mDNSResponder/mDNSPosix/mDNSUNP.h b/mDNSResponder/mDNSPosix/mDNSUNP.h
new file mode 100755
index 00000000..cc81b7d3
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/mDNSUNP.h
@@ -0,0 +1,130 @@
+/* -*- 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.
+ */
+
+#ifndef __mDNSUNP_h
+#define __mDNSUNP_h
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+#ifdef HAVE_LINUX
+#include <linux/socket.h>
+#define IPV6_2292_PKTINFO IPV6_2292PKTINFO
+#define IPV6_2292_HOPLIMIT IPV6_2292HOPLIMIT
+#else
+// The following are the supported non-linux posix OSes -
+// netbsd, freebsd and openbsd.
+#if HAVE_IPV6
+#define IPV6_2292_PKTINFO 19
+#define IPV6_2292_HOPLIMIT 20
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef NOT_HAVE_SOCKLEN_T
+typedef unsigned int socklen_t;
+#endif
+
+#if !defined(_SS_MAXSIZE)
+#if HAVE_IPV6
+#define sockaddr_storage sockaddr_in6
+#else
+#define sockaddr_storage sockaddr
+#endif // HAVE_IPV6
+#endif // !defined(_SS_MAXSIZE)
+
+#ifndef NOT_HAVE_SA_LEN
+#define GET_SA_LEN(X) (sizeof(struct sockaddr) > ((struct sockaddr*)&(X))->sa_len ? \
+ sizeof(struct sockaddr) : ((struct sockaddr*)&(X))->sa_len )
+#elif HAVE_IPV6
+#define GET_SA_LEN(X) (((struct sockaddr*)&(X))->sa_family == AF_INET ? sizeof(struct sockaddr_in) : \
+ ((struct sockaddr*)&(X))->sa_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr))
+#else
+#define GET_SA_LEN(X) (((struct sockaddr*)&(X))->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr))
+#endif
+
+#define IFI_NAME 16 /* same as IFNAMSIZ in <net/if.h> */
+#define IFI_HADDR 8 /* allow for 64-bit EUI-64 in future */
+
+// Renamed from my_in_pktinfo because in_pktinfo is used by Linux.
+
+struct my_in_pktinfo {
+ struct sockaddr_storage ipi_addr;
+ int ipi_ifindex; /* received interface index */
+ char ipi_ifname[IFI_NAME]; /* received interface name */
+};
+
+/* From the text (Stevens, section 20.2): */
+/* 'As an example of recvmsg we will write a function named recvfrom_flags that */
+/* is similar to recvfrom but also returns: */
+/* 1. the returned msg_flags value, */
+/* 2. the destination addres of the received datagram (from the IP_RECVDSTADDR socket option, and */
+/* 3. the index of the interface on which the datagram was received (the IP_RECVIF socket option).' */
+extern ssize_t recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp,
+ struct sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp, u_char *ttl);
+
+struct ifi_info {
+ char ifi_name[IFI_NAME]; /* interface name, null terminated */
+ u_char ifi_haddr[IFI_HADDR]; /* hardware address */
+ u_short ifi_hlen; /* #bytes in hardware address: 0, 6, 8 */
+ short ifi_flags; /* IFF_xxx constants from <net/if.h> */
+ short ifi_myflags; /* our own IFI_xxx flags */
+ int ifi_index; /* interface index */
+ struct sockaddr *ifi_addr; /* primary address */
+ struct sockaddr *ifi_netmask;
+ struct sockaddr *ifi_brdaddr; /* broadcast address */
+ struct sockaddr *ifi_dstaddr; /* destination address */
+ struct ifi_info *ifi_next; /* next of these structures */
+};
+
+#if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX
+#define PROC_IFINET6_PATH "/proc/net/if_inet6"
+extern struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases);
+#endif
+
+#if defined(AF_INET6) && HAVE_IPV6
+#define INET6_ADDRSTRLEN 46 /*Maximum length of IPv6 address */
+#endif
+
+
+
+#define IFI_ALIAS 1 /* ifi_addr is an alias */
+
+/* From the text (Stevens, section 16.6): */
+/* 'Since many programs need to know all the interfaces on a system, we will develop a */
+/* function of our own named get_ifi_info that returns a linked list of structures, one */
+/* for each interface that is currently "up."' */
+extern struct ifi_info *get_ifi_info(int family, int doaliases);
+
+/* 'The free_ifi_info function, which takes a pointer that was */
+/* returned by get_ifi_info and frees all the dynamic memory.' */
+extern void free_ifi_info(struct ifi_info *);
+
+#ifdef NOT_HAVE_DAEMON
+extern int daemon(int nochdir, int noclose);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/mDNSResponder/mDNSPosix/mdnsd.sh b/mDNSResponder/mDNSPosix/mdnsd.sh
new file mode 100644
index 00000000..14fef9b4
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/mdnsd.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+# Emacs settings: -*- tab-width: 4 -*-
+#
+# Copyright (c) 2002-2006 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.
+#
+# Linux /etc/init.d script to start/stop the mdnsd daemon.
+#
+# The following lines are used by the *BSD rcorder system to decide
+# the order it's going to run the rc.d scripts at startup time.
+# PROVIDE: mdnsd
+# REQUIRE: NETWORKING
+
+if [ -r /usr/sbin/mdnsd ]; then
+ DAEMON=/usr/sbin/mdnsd
+else
+ DAEMON=/usr/local/sbin/mdnsd
+fi
+
+test -r $DAEMON || exit 0
+
+# Some systems have start-stop-daemon, some don't.
+if [ -r /sbin/start-stop-daemon ]; then
+ START="start-stop-daemon --start --quiet --exec"
+ # Suse Linux doesn't work with symbolic signal names, but we really don't need
+ # to specify "-s TERM" since SIGTERM (15) is the default stop signal anway
+ # STOP="start-stop-daemon --stop -s TERM --quiet --oknodo --exec"
+ STOP="start-stop-daemon --stop --quiet --oknodo --exec"
+else
+ killmdnsd() {
+ kill -TERM `cat /var/run/mdnsd.pid`
+ }
+ START=
+ STOP=killmdnsd
+fi
+
+case "$1" in
+ start)
+ echo -n "Starting Apple Darwin Multicast DNS / DNS Service Discovery daemon:"
+ echo -n " mdnsd"
+ $START $DAEMON
+ echo "."
+ ;;
+ stop)
+ echo -n "Stopping Apple Darwin Multicast DNS / DNS Service Discovery daemon:"
+ echo -n " mdnsd" ; $STOP $DAEMON
+ echo "."
+ ;;
+ reload|restart|force-reload)
+ echo -n "Restarting Apple Darwin Multicast DNS / DNS Service Discovery daemon:"
+ $STOP $DAEMON
+ sleep 1
+ $START $DAEMON
+ echo -n " mdnsd"
+ ;;
+ *)
+ echo "Usage: /etc/init.d/mDNS {start|stop|reload|restart}"
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/mDNSResponder/mDNSPosix/nss_ReadMe.txt b/mDNSResponder/mDNSPosix/nss_ReadMe.txt
new file mode 100755
index 00000000..2e3023c0
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/nss_ReadMe.txt
@@ -0,0 +1,125 @@
+# Readme for libnss_mdns
+
+Andrew White <Andrew.White@nicta.com.au>
+June 2004
+
+Before using this software, see "Licensing" at bottom of this file.
+
+
+# Introduction
+
+This code implements a module for the Name Service Switch to perform
+hostname lookups using the Darwin mDNSResponder / mdnsd. This code has
+been tested on Debian and Redhat Linux. It may work on other platforms.
+It *will not* work on Darwin or Mac OS X - the necessary functionality is
+already built into the operation system.
+
+
+# Building and Installing:
+
+See "ReadMe.txt" for instructions on building and installing.
+
+When you run "make install" as described in that file:
+o libnss_mdns-0.2.so and libnss_mdns.so.2 are installed in /lib,
+o manual pages libnss_mdns(8) and nss_mdns.conf(5) are installed,
+o nss_mdns.conf is installed in /etc, and
+o /etc/nsswitch.conf is modified to add "mdns" on the "hosts:" line
+
+This will cause dns lookups to be passed via mdnsd before trying the dns.
+
+
+# Testing
+
+For most purposes, 'ping myhostname.local' will tell you if mdns is
+working. If MDNS_VERBOSE was set in nss_mdns.c during compilation then
+lots of chatty debug messages will be dumped to LOG_DEBUG in syslog.
+Otherwise, nss_mdns will only log if something isn't behaving quite right.
+
+
+# Implementation details
+
+libnss_mdns provides alternative back-end implementations of the libc
+functions gethostbyname, gethostbyname2 and gethostbyaddr, using the Name
+Service Switch mechanism. More information on writing nsswitch modules
+can be found via 'info libc "Name Service Switch"', if installed.
+
+
+# Licensing
+
+This software is licensed under the NICTA Public Software License, version
+1.0, printed below:
+
+NICTA Public Software Licence
+Version 1.0
+
+Copyright © 2004 National ICT Australia Ltd
+
+All rights reserved.
+
+By this licence, National ICT Australia Ltd (NICTA) grants permission,
+free of charge, to any person who obtains a copy of this software
+and any associated documentation files ("the Software") to use and
+deal with the Software in source code and binary forms without
+restriction, with or without modification, and to permit persons
+to whom the Software is furnished to do so, provided that the
+following conditions are met:
+
+- Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimers.
+- Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimers in
+ the documentation and/or other materials provided with the
+ distribution.
+- The name of NICTA may not be used to endorse or promote products
+ derived from this Software without specific prior written permission.
+
+EXCEPT AS EXPRESSLY STATED IN THIS LICENCE AND TO THE FULL EXTENT
+PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS-IS" AND
+NICTA MAKES NO REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY
+REPRESENTATIONS, WARRANTIES OR CONDITIONS REGARDING THE CONTENTS
+OR ACCURACY OF THE SOFTWARE, OR OF TITLE, MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, THE ABSENCE OF LATENT
+OR OTHER DEFECTS, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR
+NOT DISCOVERABLE.
+
+TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL
+NICTA BE LIABLE ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+NEGLIGENCE) FOR ANY LOSS OR DAMAGE WHATSOEVER, INCLUDING (WITHOUT
+LIMITATION) LOSS OF PRODUCTION OR OPERATION TIME, LOSS, DAMAGE OR
+CORRUPTION OF DATA OR RECORDS; OR LOSS OF ANTICIPATED SAVINGS,
+OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR OTHER ECONOMIC LOSS;
+OR ANY SPECIAL, INCIDENTAL, INDIRECT, CONSEQUENTIAL, PUNITIVE OR
+EXEMPLARY DAMAGES ARISING OUT OF OR IN CONNECTION WITH THIS LICENCE,
+THE SOFTWARE OR THE USE OF THE SOFTWARE, EVEN IF NICTA HAS BEEN
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+If applicable legislation implies warranties or conditions, or
+imposes obligations or liability on NICTA in respect of the Software
+that cannot be wholly or partly excluded, restricted or modified,
+NICTA's liability is limited, to the full extent permitted by the
+applicable legislation, at its option, to:
+
+a. in the case of goods, any one or more of the following:
+ i. the replacement of the goods or the supply of equivalent goods;
+ ii. the repair of the goods;
+ iii. the payment of the cost of replacing the goods or of acquiring
+ equivalent goods;
+ iv. the payment of the cost of having the goods repaired; or
+b. in the case of services:
+ i. the supplying of the services again; or
+ ii. the payment of the cost of having the services supplied
+ again.
+
+
+# Links:
+
+NICTA
+ http://www.nicta.com.au/
+Darwin
+ http://developer.apple.com/darwin/
+DNS service discovery and link-local
+ http://http://zeroconf.org/
+ http://http://multicastdns.org/
+ http://http://dns-sd.org/
+ http://http://dotlocal.org/
diff --git a/mDNSResponder/mDNSPosix/nss_mdns.c b/mDNSResponder/mDNSPosix/nss_mdns.c
new file mode 100755
index 00000000..afadb3c6
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/nss_mdns.c
@@ -0,0 +1,2723 @@
+/*
+ NICTA Public Software Licence
+ Version 1.0
+
+ Copyright © 2004 National ICT Australia Ltd
+
+ All rights reserved.
+
+ By this licence, National ICT Australia Ltd (NICTA) grants permission,
+ free of charge, to any person who obtains a copy of this software
+ and any associated documentation files ("the Software") to use and
+ deal with the Software in source code and binary forms without
+ restriction, with or without modification, and to permit persons
+ to whom the Software is furnished to do so, provided that the
+ following conditions are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimers.
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimers in
+ the documentation and/or other materials provided with the
+ distribution.
+ - The name of NICTA may not be used to endorse or promote products
+ derived from this Software without specific prior written permission.
+
+ EXCEPT AS EXPRESSLY STATED IN THIS LICENCE AND TO THE FULL EXTENT
+ PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS-IS" AND
+ NICTA MAKES NO REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY
+ KIND, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY
+ REPRESENTATIONS, WARRANTIES OR CONDITIONS REGARDING THE CONTENTS
+ OR ACCURACY OF THE SOFTWARE, OR OF TITLE, MERCHANTABILITY, FITNESS
+ FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, THE ABSENCE OF LATENT
+ OR OTHER DEFECTS, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR
+ NOT DISCOVERABLE.
+
+ TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL
+ NICTA BE LIABLE ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+ NEGLIGENCE) FOR ANY LOSS OR DAMAGE WHATSOEVER, INCLUDING (WITHOUT
+ LIMITATION) LOSS OF PRODUCTION OR OPERATION TIME, LOSS, DAMAGE OR
+ CORRUPTION OF DATA OR RECORDS; OR LOSS OF ANTICIPATED SAVINGS,
+ OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR OTHER ECONOMIC LOSS;
+ OR ANY SPECIAL, INCIDENTAL, INDIRECT, CONSEQUENTIAL, PUNITIVE OR
+ EXEMPLARY DAMAGES ARISING OUT OF OR IN CONNECTION WITH THIS LICENCE,
+ THE SOFTWARE OR THE USE OF THE SOFTWARE, EVEN IF NICTA HAS BEEN
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+ If applicable legislation implies warranties or conditions, or
+ imposes obligations or liability on NICTA in respect of the Software
+ that cannot be wholly or partly excluded, restricted or modified,
+ NICTA's liability is limited, to the full extent permitted by the
+ applicable legislation, at its option, to:
+
+ a. in the case of goods, any one or more of the following:
+ i. the replacement of the goods or the supply of equivalent goods;
+ ii. the repair of the goods;
+ iii. the payment of the cost of replacing the goods or of acquiring
+ equivalent goods;
+ iv. the payment of the cost of having the goods repaired; or
+ b. in the case of services:
+ i. the supplying of the services again; or
+ ii. the payment of the cost of having the services supplied
+ again.
+ */
+
+/*
+ NSSwitch Implementation of mDNS interface.
+
+ Andrew White (Andrew.White@nicta.com.au)
+ May 2004
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+#include <pthread.h>
+#include <ctype.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+#define BIND_8_COMPAT 1
+#include <arpa/nameser.h>
+
+#include <dns_sd.h>
+
+
+//----------
+// Public functions
+
+/*
+ Count the number of dots in a name string.
+ */
+int
+count_dots (const char * name);
+
+
+/*
+ Test whether a domain name is local.
+
+ Returns
+ 1 if name ends with ".local" or ".local."
+ 0 otherwise
+ */
+int
+islocal (const char * name);
+
+
+/*
+ Format an address structure as a string appropriate for DNS reverse (PTR)
+ lookup, based on address type.
+
+ Parameters
+ prefixlen
+ Prefix length, in bits. When formatting, this will be rounded up
+ to the nearest appropriate size. If -1, assume maximum.
+ buf
+ Output buffer. Must be long enough to hold largest possible
+ output.
+ Returns
+ Pointer to (first character of) output buffer,
+ or NULL on error.
+ */
+char *
+format_reverse_addr (int af, const void * addr, int prefixlen, char * buf);
+
+
+/*
+ Format an address structure as a string appropriate for DNS reverse (PTR)
+ lookup for AF_INET. Output is in .in-addr.arpa domain.
+
+ Parameters
+ prefixlen
+ Prefix length, in bits. When formatting, this will be rounded up
+ to the nearest byte (8). If -1, assume 32.
+ buf
+ Output buffer. Must be long enough to hold largest possible
+ output. For AF_INET, this is 29 characters (including null).
+ Returns
+ Pointer to (first character of) output buffer,
+ or NULL on error.
+ */
+char *
+format_reverse_addr_in (
+ const struct in_addr * addr,
+ int prefixlen,
+ char * buf
+ );
+#define DNS_PTR_AF_INET_SIZE 29
+
+/*
+ Format an address structure as a string appropriate for DNS reverse (PTR)
+ lookup for AF_INET6. Output is in .ip6.arpa domain.
+
+ Parameters
+ prefixlen
+ Prefix length, in bits. When formatting, this will be rounded up
+ to the nearest nibble (4). If -1, assume 128.
+ buf
+ Output buffer. Must be long enough to hold largest possible
+ output. For AF_INET6, this is 72 characters (including null).
+ Returns
+ Pointer to (first character of) output buffer,
+ or NULL on error.
+ */
+char *
+format_reverse_addr_in6 (
+ const struct in6_addr * addr,
+ int prefixlen,
+ char * buf
+ );
+#define DNS_PTR_AF_INET6_SIZE 72
+
+
+/*
+ Compare whether the given dns name has the given domain suffix.
+ A single leading '.' on the name or leading or trailing '.' on the
+ domain is ignored for the purposes of the comparison.
+ Multiple leading or trailing '.'s are an error. Other DNS syntax
+ errors are not checked for. The comparison is case insensitive.
+
+ Returns
+ 1 on success (match)
+ 0 on failure (no match)
+ < 0 on error
+ */
+int
+cmp_dns_suffix (const char * name, const char * domain);
+enum
+{
+ CMP_DNS_SUFFIX_SUCCESS = 1,
+ CMP_DNS_SUFFIX_FAILURE = 0,
+ CMP_DNS_SUFFIX_BAD_NAME = 1,
+ CMP_DNS_SUFFIX_BAD_DOMAIN = -2
+};
+
+typedef int ns_type_t;
+typedef int ns_class_t;
+
+/*
+ Convert a DNS resource record (RR) code to an address family (AF) code.
+
+ Parameters
+ rrtype
+ resource record type (from nameser.h)
+
+ Returns
+ Appropriate AF code (from socket.h), or AF_UNSPEC if an appropriate
+ mapping couldn't be determined
+ */
+int
+rr_to_af (ns_type_t rrtype);
+
+
+/*
+ Convert an address family (AF) code to a DNS resource record (RR) code.
+
+ Parameters
+ int
+ address family code (from socket.h)
+ Returns
+ Appropriate RR code (from nameser.h), or ns_t_invalid if an appropriate
+ mapping couldn't be determined
+ */
+ns_type_t
+af_to_rr (int af);
+
+
+/*
+ Convert a string to an address family (case insensitive).
+
+ Returns
+ Matching AF code, or AF_UNSPEC if no match found.
+ */
+int
+str_to_af (const char * str);
+
+
+/*
+ Convert a string to an ns_class_t (case insensitive).
+
+ Returns
+ Matching ns_class_t, or ns_c_invalid if no match found.
+ */
+ns_class_t
+str_to_ns_class (const char * str);
+
+
+/*
+ Convert a string to an ns_type_t (case insensitive).
+
+ Returns
+ Matching ns_type_t, or ns_t_invalid if no match found.
+ */
+ns_type_t
+str_to_ns_type (const char * str);
+
+
+/*
+ Convert an address family code to a string.
+
+ Returns
+ String representation of AF,
+ or NULL if address family unrecognised or invalid.
+ */
+const char *
+af_to_str (int in);
+
+
+/*
+ Convert an ns_class_t code to a string.
+
+ Returns
+ String representation of ns_class_t,
+ or NULL if ns_class_t unrecognised or invalid.
+ */
+const char *
+ns_class_to_str (ns_class_t in);
+
+
+/*
+ Convert an ns_type_t code to a string.
+
+ Returns
+ String representation of ns_type_t,
+ or NULL if ns_type_t unrecognised or invalid.
+ */
+const char *
+ns_type_to_str (ns_type_t in);
+
+
+/*
+ Convert DNS rdata in label format (RFC1034, RFC1035) to a name.
+
+ On error, partial data is written to name (as much as was successfully
+ processed) and an error code is returned. Errors include a name too
+ long for the buffer and a pointer in the label (which cannot be
+ resolved).
+
+ Parameters
+ rdata
+ Rdata formatted as series of labels.
+ rdlen
+ Length of rdata buffer.
+ name
+ Buffer to store fully qualified result in.
+ By RFC1034 section 3.1, a 255 character buffer (256 characters
+ including null) is long enough for any legal name.
+ name_len
+ Number of characters available in name buffer, not including
+ trailing null.
+
+ Returns
+ Length of name buffer (not including trailing null).
+ < 0 on error.
+ A return of 0 implies the empty domain.
+ */
+static int
+dns_rdata_to_name (const unsigned char * rdata, int rdlen, char * name, unsigned int name_len);
+enum
+{
+ DNS_RDATA_TO_NAME_BAD_FORMAT = -1,
+ // Format is broken. Usually because we ran out of data
+ // (according to rdata) before the labels said we should.
+ DNS_RDATA_TO_NAME_TOO_LONG = -2,
+ // The converted rdata is longer than the name buffer.
+ DNS_RDATA_TO_NAME_PTR = -3,
+ // The rdata contains a pointer.
+};
+
+#define DNS_LABEL_MAXLEN 63
+// Maximum length of a single DNS label
+#define DNS_NAME_MAXLEN 256
+// Maximum length of a DNS name
+
+//----------
+// Public types
+
+typedef int errcode_t;
+// Used for 0 = success, non-zero = error code functions
+
+
+//----------
+// Public functions
+
+/*
+ Test whether a domain name is in a domain covered by nss_mdns.
+ The name is assumed to be fully qualified (trailing dot optional);
+ unqualified names will be processed but may return unusual results
+ if the unqualified prefix happens to match a domain suffix.
+
+ Returns
+ 1 success
+ 0 failure
+ -1 error, check errno
+ */
+int
+config_is_mdns_suffix (const char * name);
+
+
+/*
+ Loads all relevant data from configuration file. Other code should
+ rarely need to call this function, since all other public configuration
+ functions do so implicitly. Once loaded, configuration info doesn't
+ change.
+
+ Returns
+ 0 configuration ready
+ non-zero configuration error code
+ */
+errcode_t
+init_config ();
+
+#define ENTNAME hostent
+#define DATABASE "hosts"
+
+#include <nss.h>
+// For nss_status
+#include <netdb.h>
+// For hostent
+#include <sys/types.h>
+// For size_t
+
+typedef enum nss_status nss_status;
+typedef struct hostent hostent;
+
+/*
+ gethostbyname implementation
+
+ name:
+ name to look up
+ result_buf:
+ resulting entry
+ buf:
+ auxillary buffer
+ buflen:
+ length of auxillary buffer
+ errnop:
+ pointer to errno
+ h_errnop:
+ pointer to h_errno
+ */
+nss_status
+_nss_mdns_gethostbyname_r (
+ const char *name,
+ hostent * result_buf,
+ char *buf,
+ size_t buflen,
+ int *errnop,
+ int *h_errnop
+ );
+
+
+/*
+ gethostbyname2 implementation
+
+ name:
+ name to look up
+ af:
+ address family
+ result_buf:
+ resulting entry
+ buf:
+ auxillary buffer
+ buflen:
+ length of auxillary buffer
+ errnop:
+ pointer to errno
+ h_errnop:
+ pointer to h_errno
+ */
+nss_status
+_nss_mdns_gethostbyname2_r (
+ const char *name,
+ int af,
+ hostent * result_buf,
+ char *buf,
+ size_t buflen,
+ int *errnop,
+ int *h_errnop
+ );
+
+
+/*
+ gethostbyaddr implementation
+
+ addr:
+ address structure to look up
+ len:
+ length of address structure
+ af:
+ address family
+ result_buf:
+ resulting entry
+ buf:
+ auxillary buffer
+ buflen:
+ length of auxillary buffer
+ errnop:
+ pointer to errno
+ h_errnop:
+ pointer to h_errno
+ */
+nss_status
+_nss_mdns_gethostbyaddr_r (
+ const void *addr,
+ socklen_t len,
+ int af,
+ hostent * result_buf,
+ char *buf,
+ size_t buflen,
+ int *errnop,
+ int *h_errnop
+ );
+
+
+//----------
+// Types and Constants
+
+const int MDNS_VERBOSE = 0;
+// This enables verbose syslog messages
+// If zero, only "imporant" messages will appear in syslog
+
+#define k_hostname_maxlen 256
+// As per RFC1034 and RFC1035
+#define k_aliases_max 15
+#define k_addrs_max 15
+
+typedef struct buf_header
+{
+ char hostname [k_hostname_maxlen + 1];
+ char * aliases [k_aliases_max + 1];
+ char * addrs [k_addrs_max + 1];
+} buf_header_t;
+
+typedef struct result_map
+{
+ int done;
+ nss_status status;
+ hostent * hostent;
+ buf_header_t * header;
+ int aliases_count;
+ int addrs_count;
+ char * buffer;
+ int addr_idx;
+ // Index for addresses - grow from low end
+ // Index points to first empty space
+ int alias_idx;
+ // Index for aliases - grow from high end
+ // Index points to lowest entry
+ int r_errno;
+ int r_h_errno;
+} result_map_t;
+
+static const struct timeval
+k_select_time = { 0, 500000 };
+// 0 seconds, 500 milliseconds
+
+//----------
+// Local prototypes
+
+static nss_status
+mdns_gethostbyname2 (
+ const char *name,
+ int af,
+ hostent * result_buf,
+ char *buf,
+ size_t buflen,
+ int *errnop,
+ int *h_errnop
+ );
+
+
+/*
+ Lookup name using mDNS server
+ */
+static nss_status
+mdns_lookup_name (
+ const char * fullname,
+ int af,
+ result_map_t * result
+ );
+
+/*
+ Lookup address using mDNS server
+ */
+static nss_status
+mdns_lookup_addr (
+ const void * addr,
+ socklen_t len,
+ int af,
+ const char * addr_str,
+ result_map_t * result
+ );
+
+
+/*
+ Handle incoming MDNS events
+ */
+static nss_status
+handle_events (DNSServiceRef sdref, result_map_t * result, const char * str);
+
+
+// Callback for mdns_lookup operations
+//DNSServiceQueryRecordReply mdns_lookup_callback;
+typedef void
+mdns_lookup_callback_t
+(
+ DNSServiceRef sdref,
+ DNSServiceFlags flags,
+ uint32_t interface_index,
+ DNSServiceErrorType error_code,
+ const char *fullname,
+ uint16_t rrtype,
+ uint16_t rrclass,
+ uint16_t rdlen,
+ const void *rdata,
+ uint32_t ttl,
+ void *context
+);
+
+mdns_lookup_callback_t mdns_lookup_callback;
+
+
+static int
+init_result (
+ result_map_t * result,
+ hostent * result_buf,
+ char * buf,
+ size_t buflen
+ );
+
+static int
+callback_body_ptr (
+ const char * fullname,
+ result_map_t * result,
+ int rdlen,
+ const void * rdata
+ );
+
+static void *
+add_address_to_buffer (result_map_t * result, const void * data, int len);
+static char *
+add_alias_to_buffer (result_map_t * result, const char * data, int len);
+static char *
+add_hostname_len (result_map_t * result, const char * fullname, int len);
+static char *
+add_hostname_or_alias (result_map_t * result, const char * data, int len);
+
+static void *
+contains_address (result_map_t * result, const void * data, int len);
+static char *
+contains_alias (result_map_t * result, const char * data);
+
+
+static const char *
+is_applicable_name (
+ result_map_t * result,
+ const char * name,
+ char * lookup_name
+ );
+
+static const char *
+is_applicable_addr (
+ result_map_t * result,
+ const void * addr,
+ int af,
+ char * addr_str
+ );
+
+
+// Error code functions
+
+static nss_status
+set_err (result_map_t * result, nss_status status, int err, int herr);
+
+static nss_status set_err_notfound (result_map_t * result);
+static nss_status set_err_bad_hostname (result_map_t * result);
+static nss_status set_err_buf_too_small (result_map_t * result);
+static nss_status set_err_internal_resource_full (result_map_t * result);
+static nss_status set_err_system (result_map_t * result);
+static nss_status set_err_mdns_failed (result_map_t * result);
+static nss_status set_err_success (result_map_t * result);
+
+
+//----------
+// Global variables
+
+
+//----------
+// NSS functions
+
+nss_status
+_nss_mdns_gethostbyname_r (
+ const char *name,
+ hostent * result_buf,
+ char *buf,
+ size_t buflen,
+ int *errnop,
+ int *h_errnop
+ )
+{
+ if (MDNS_VERBOSE)
+ syslog (LOG_DEBUG,
+ "mdns: Called nss_mdns_gethostbyname with %s",
+ name
+ );
+
+ return
+ mdns_gethostbyname2 (
+ name, AF_INET, result_buf, buf, buflen, errnop, h_errnop
+ );
+}
+
+
+nss_status
+_nss_mdns_gethostbyname2_r (
+ const char *name,
+ int af,
+ hostent * result_buf,
+ char *buf,
+ size_t buflen,
+ int *errnop,
+ int *h_errnop
+ )
+{
+ if (MDNS_VERBOSE)
+ syslog (LOG_DEBUG,
+ "mdns: Called nss_mdns_gethostbyname2 with %s",
+ name
+ );
+
+ return
+ mdns_gethostbyname2 (
+ name, af, result_buf, buf, buflen, errnop, h_errnop
+ );
+}
+
+
+nss_status
+_nss_mdns_gethostbyaddr_r (
+ const void *addr,
+ socklen_t len,
+ int af,
+ hostent * result_buf,
+ char *buf,
+ size_t buflen,
+ int *errnop,
+ int *h_errnop
+ )
+{
+ char addr_str [NI_MAXHOST + 1];
+ result_map_t result;
+ int err_status;
+
+ if (inet_ntop (af, addr, addr_str, NI_MAXHOST) == NULL)
+ {
+ const char * family = af_to_str (af);
+ if (family == NULL)
+ {
+ family = "Unknown";
+ }
+
+ syslog (LOG_WARNING,
+ "mdns: Couldn't covert address, family %d (%s) in nss_mdns_gethostbyaddr: %s",
+ af,
+ family,
+ strerror (errno)
+ );
+
+ // This address family never applicable to us, so return NOT_FOUND
+
+ *errnop = ENOENT;
+ *h_errnop = HOST_NOT_FOUND;
+ return NSS_STATUS_NOTFOUND;
+ }
+ if (MDNS_VERBOSE)
+ {
+ syslog (LOG_DEBUG,
+ "mdns: Called nss_mdns_gethostbyaddr with %s",
+ addr_str
+ );
+ }
+
+ // Initialise result
+ err_status = init_result (&result, result_buf, buf, buflen);
+ if (err_status)
+ {
+ *errnop = err_status;
+ *h_errnop = NETDB_INTERNAL;
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ if (is_applicable_addr (&result, addr, af, addr_str))
+ {
+ nss_status rv;
+
+ rv = mdns_lookup_addr (addr, len, af, addr_str, &result);
+ if (rv == NSS_STATUS_SUCCESS)
+ {
+ return rv;
+ }
+ }
+
+ // Return current error status (defaults to NOT_FOUND)
+
+ *errnop = result.r_errno;
+ *h_errnop = result.r_h_errno;
+ return result.status;
+}
+
+
+//----------
+// Local functions
+
+nss_status
+mdns_gethostbyname2 (
+ const char *name,
+ int af,
+ hostent * result_buf,
+ char *buf,
+ size_t buflen,
+ int *errnop,
+ int *h_errnop
+ )
+{
+ char lookup_name [k_hostname_maxlen + 1];
+ result_map_t result;
+ int err_status;
+
+ // Initialise result
+ err_status = init_result (&result, result_buf, buf, buflen);
+ if (err_status)
+ {
+ *errnop = err_status;
+ *h_errnop = NETDB_INTERNAL;
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ if (is_applicable_name (&result, name, lookup_name))
+ {
+ // Try using mdns
+ nss_status rv;
+
+ if (MDNS_VERBOSE)
+ syslog (LOG_DEBUG,
+ "mdns: Local name: %s",
+ name
+ );
+
+ rv = mdns_lookup_name (name, af, &result);
+ if (rv == NSS_STATUS_SUCCESS)
+ {
+ return rv;
+ }
+ }
+
+ // Return current error status (defaults to NOT_FOUND)
+
+ *errnop = result.r_errno;
+ *h_errnop = result.r_h_errno;
+ return result.status;
+}
+
+
+/*
+ Lookup a fully qualified hostname using the default record type
+ for the specified address family.
+
+ Parameters
+ fullname
+ Fully qualified hostname. If not fully qualified the code will
+ still 'work', but the lookup is unlikely to succeed.
+ af
+ Either AF_INET or AF_INET6. Other families are not supported.
+ result
+ Initialised 'result' data structure.
+ */
+static nss_status
+mdns_lookup_name (
+ const char * fullname,
+ int af,
+ result_map_t * result
+ )
+{
+ // Lookup using mDNS.
+ DNSServiceErrorType errcode;
+ DNSServiceRef sdref;
+ ns_type_t rrtype;
+ nss_status status;
+
+ if (MDNS_VERBOSE)
+ syslog (LOG_DEBUG,
+ "mdns: Attempting lookup of %s",
+ fullname
+ );
+
+ switch (af)
+ {
+ case AF_INET:
+ rrtype = kDNSServiceType_A;
+ result->hostent->h_length = 4;
+ // Length of an A record
+ break;
+
+ case AF_INET6:
+ rrtype = kDNSServiceType_AAAA;
+ result->hostent->h_length = 16;
+ // Length of an AAAA record
+ break;
+
+ default:
+ syslog (LOG_WARNING,
+ "mdns: Unsupported address family %d",
+ af
+ );
+ return set_err_bad_hostname (result);
+ }
+ result->hostent->h_addrtype = af;
+
+ errcode =
+ DNSServiceQueryRecord (
+ &sdref,
+ kDNSServiceFlagsForceMulticast, // force multicast query
+ kDNSServiceInterfaceIndexAny, // all interfaces
+ fullname, // full name to query for
+ rrtype, // resource record type
+ kDNSServiceClass_IN, // internet class records
+ mdns_lookup_callback, // callback
+ result // Context - result buffer
+ );
+
+ if (errcode)
+ {
+ syslog (LOG_WARNING,
+ "mdns: Failed to initialise lookup, error %d",
+ errcode
+ );
+ return set_err_mdns_failed (result);
+ }
+
+ status = handle_events (sdref, result, fullname);
+ DNSServiceRefDeallocate (sdref);
+ return status;
+}
+
+
+/*
+ Reverse (PTR) lookup for the specified address.
+
+ Parameters
+ addr
+ Either a struct in_addr or a struct in6_addr
+ addr_len
+ size of the address
+ af
+ Either AF_INET or AF_INET6. Other families are not supported.
+ Must match addr
+ addr_str
+ Address in format suitable for PTR lookup.
+ AF_INET: a.b.c.d -> d.c.b.a.in-addr.arpa
+ AF_INET6: reverse nibble format, x.x.x...x.ip6.arpa
+ result
+ Initialised 'result' data structure.
+ */
+static nss_status
+mdns_lookup_addr (
+ const void * addr,
+ socklen_t addr_len,
+ int af,
+ const char * addr_str,
+ result_map_t * result
+ )
+{
+ DNSServiceErrorType errcode;
+ DNSServiceRef sdref;
+ nss_status status;
+
+ if (MDNS_VERBOSE)
+ syslog (LOG_DEBUG,
+ "mdns: Attempting lookup of %s",
+ addr_str
+ );
+
+ result->hostent->h_addrtype = af;
+ result->hostent->h_length = addr_len;
+
+ // Query address becomes "address" in result.
+ if (!add_address_to_buffer (result, addr, addr_len))
+ {
+ return result->status;
+ }
+
+ result->hostent->h_name [0] = 0;
+
+ errcode =
+ DNSServiceQueryRecord (
+ &sdref,
+ kDNSServiceFlagsForceMulticast, // force multicast query
+ kDNSServiceInterfaceIndexAny, // all interfaces
+ addr_str, // address string to query for
+ kDNSServiceType_PTR, // pointer RRs
+ kDNSServiceClass_IN, // internet class records
+ mdns_lookup_callback, // callback
+ result // Context - result buffer
+ );
+
+ if (errcode)
+ {
+ syslog (LOG_WARNING,
+ "mdns: Failed to initialise mdns lookup, error %d",
+ errcode
+ );
+ return set_err_mdns_failed (result);
+ }
+
+ status = handle_events (sdref, result, addr_str);
+ DNSServiceRefDeallocate (sdref);
+ return status;
+}
+
+
+/*
+ Wait on result of callback, and process it when it arrives.
+
+ Parameters
+ sdref
+ dns-sd reference
+ result
+ Initialised 'result' data structure.
+ str
+ lookup string, used for status/error reporting.
+ */
+static nss_status
+handle_events (DNSServiceRef sdref, result_map_t * result, const char * str)
+{
+ int dns_sd_fd = DNSServiceRefSockFD(sdref);
+ int nfds = dns_sd_fd + 1;
+ fd_set readfds;
+ struct timeval tv;
+ int select_result;
+
+ while (!result->done)
+ {
+ FD_ZERO(&readfds);
+ FD_SET(dns_sd_fd, &readfds);
+
+ tv = k_select_time;
+
+ select_result =
+ select (nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv);
+ if (select_result > 0)
+ {
+ if (FD_ISSET(dns_sd_fd, &readfds))
+ {
+ if (MDNS_VERBOSE)
+ syslog (LOG_DEBUG,
+ "mdns: Reply received for %s",
+ str
+ );
+ DNSServiceProcessResult(sdref);
+ }
+ else
+ {
+ syslog (LOG_WARNING,
+ "mdns: Unexpected return from select on lookup of %s",
+ str
+ );
+ }
+ }
+ else
+ {
+ // Terminate loop due to timer expiry
+ if (MDNS_VERBOSE)
+ syslog (LOG_DEBUG,
+ "mdns: %s not found - timer expired",
+ str
+ );
+ set_err_notfound (result);
+ break;
+ }
+ }
+
+ return result->status;
+}
+
+
+/*
+ Examine incoming data and add to relevant fields in result structure.
+ This routine is called from DNSServiceProcessResult where appropriate.
+ */
+void
+mdns_lookup_callback
+(
+ DNSServiceRef sdref,
+ DNSServiceFlags flags,
+ uint32_t interface_index,
+ DNSServiceErrorType error_code,
+ const char *fullname,
+ uint16_t rrtype,
+ uint16_t rrclass,
+ uint16_t rdlen,
+ const void *rdata,
+ uint32_t ttl,
+ void *context
+)
+{
+ // A single record is received
+
+ result_map_t * result = (result_map_t *) context;
+
+ (void)sdref; // Unused
+ (void)interface_index; // Unused
+ (void)ttl; // Unused
+
+ if (!(flags & kDNSServiceFlagsMoreComing) )
+ {
+ result->done = 1;
+ }
+
+ if (error_code == kDNSServiceErr_NoError)
+ {
+ ns_type_t expected_rr_type =
+ af_to_rr (result->hostent->h_addrtype);
+
+ // Idiot check class
+ if (rrclass != C_IN)
+ {
+ syslog (LOG_WARNING,
+ "mdns: Received bad RR class: expected %d (%s),"
+ " got %d (%s), RR type %d (%s)",
+ C_IN,
+ ns_class_to_str (C_IN),
+ rrclass,
+ ns_class_to_str (rrclass),
+ rrtype,
+ ns_type_to_str (rrtype)
+ );
+ return;
+ }
+
+ // If a PTR
+ if (rrtype == kDNSServiceType_PTR)
+ {
+ if (callback_body_ptr (fullname, result, rdlen, rdata) < 0)
+ return;
+ }
+ else if (rrtype == expected_rr_type)
+ {
+ if (!
+ add_hostname_or_alias (
+ result,
+ fullname,
+ strlen (fullname)
+ )
+ )
+ {
+ result->done = 1;
+ return;
+ // Abort on error
+ }
+
+ if (!add_address_to_buffer (result, rdata, rdlen) )
+ {
+ result->done = 1;
+ return;
+ // Abort on error
+ }
+ }
+ else
+ {
+ syslog (LOG_WARNING,
+ "mdns: Received bad RR type: expected %d (%s),"
+ " got %d (%s)",
+ expected_rr_type,
+ ns_type_to_str (expected_rr_type),
+ rrtype,
+ ns_type_to_str (rrtype)
+ );
+ return;
+ }
+
+ if (result->status != NSS_STATUS_SUCCESS)
+ set_err_success (result);
+ }
+ else
+ {
+ // For now, dump message to syslog and continue
+ syslog (LOG_WARNING,
+ "mdns: callback returned error %d",
+ error_code
+ );
+ }
+}
+
+static int
+callback_body_ptr (
+ const char * fullname,
+ result_map_t * result,
+ int rdlen,
+ const void * rdata
+ )
+{
+ char result_name [k_hostname_maxlen + 1];
+ int rv;
+
+ // Fullname should be .in-addr.arpa or equivalent, which we're
+ // not interested in. Ignore it.
+
+ rv = dns_rdata_to_name (rdata, rdlen, result_name, k_hostname_maxlen);
+ if (rv < 0)
+ {
+ const char * errmsg;
+
+ switch (rv)
+ {
+ case DNS_RDATA_TO_NAME_BAD_FORMAT:
+ errmsg = "mdns: PTR '%s' result badly formatted ('%s...')";
+ break;
+
+ case DNS_RDATA_TO_NAME_TOO_LONG:
+ errmsg = "mdns: PTR '%s' result too long ('%s...')";
+ break;
+
+ case DNS_RDATA_TO_NAME_PTR:
+ errmsg = "mdns: PTR '%s' result contained pointer ('%s...')";
+ break;
+
+ default:
+ errmsg = "mdns: PTR '%s' result conversion failed ('%s...')";
+ }
+
+ syslog (LOG_WARNING,
+ errmsg,
+ fullname,
+ result_name
+ );
+
+ return -1;
+ }
+
+ if (MDNS_VERBOSE)
+ {
+ syslog (LOG_DEBUG,
+ "mdns: PTR '%s' resolved to '%s'",
+ fullname,
+ result_name
+ );
+ }
+
+ // Data should be a hostname
+ if (!
+ add_hostname_or_alias (
+ result,
+ result_name,
+ rv
+ )
+ )
+ {
+ result->done = 1;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ Add an address to the buffer.
+
+ Parameter
+ result
+ Result structure to write to
+ data
+ Incoming address data buffer
+ Must be 'int' aligned
+ len
+ Length of data buffer (in bytes)
+ Must match data alignment
+
+ Result
+ Pointer to start of newly written data,
+ or NULL on error.
+ If address already exists in buffer, returns pointer to that instead.
+ */
+static void *
+add_address_to_buffer (result_map_t * result, const void * data, int len)
+{
+ int new_addr;
+ void * start;
+ void * temp;
+
+ if ((temp = contains_address (result, data, len)))
+ {
+ return temp;
+ }
+
+ if (result->addrs_count >= k_addrs_max)
+ {
+ // Not enough addr slots
+ set_err_internal_resource_full (result);
+ syslog (LOG_ERR,
+ "mdns: Internal address buffer full; increase size"
+ );
+ return NULL;
+ }
+
+ // Idiot check
+ if (len != result->hostent->h_length)
+ {
+ syslog (LOG_WARNING,
+ "mdns: Unexpected rdata length for address. Expected %d, got %d",
+ result->hostent->h_length,
+ len
+ );
+ // XXX And continue for now.
+ }
+
+ new_addr = result->addr_idx + len;
+
+ if (new_addr > result->alias_idx)
+ {
+ // Not enough room
+ set_err_buf_too_small (result);
+ if (MDNS_VERBOSE)
+ syslog (LOG_DEBUG,
+ "mdns: Ran out of buffer when adding address %d",
+ result->addrs_count + 1
+ );
+ return NULL;
+ }
+
+ start = result->buffer + result->addr_idx;
+ memcpy (start, data, len);
+ result->addr_idx = new_addr;
+ result->header->addrs [result->addrs_count] = start;
+ result->addrs_count++;
+ result->header->addrs [result->addrs_count] = NULL;
+
+ return start;
+}
+
+
+static void *
+contains_address (result_map_t * result, const void * data, int len)
+{
+ int i;
+
+ // Idiot check
+ if (len != result->hostent->h_length)
+ {
+ syslog (LOG_WARNING,
+ "mdns: Unexpected rdata length for address. Expected %d, got %d",
+ result->hostent->h_length,
+ len
+ );
+ // XXX And continue for now.
+ }
+
+ for (i = 0; result->header->addrs [i]; i++)
+ {
+ if (memcmp (result->header->addrs [i], data, len) == 0)
+ {
+ return result->header->addrs [i];
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+ Add an alias to the buffer.
+
+ Parameter
+ result
+ Result structure to write to
+ data
+ Incoming alias (null terminated)
+ len
+ Length of data buffer (in bytes), including trailing null
+
+ Result
+ Pointer to start of newly written data,
+ or NULL on error
+ If alias already exists in buffer, returns pointer to that instead.
+ */
+static char *
+add_alias_to_buffer (result_map_t * result, const char * data, int len)
+{
+ int new_alias;
+ char * start;
+ char * temp;
+
+ if ((temp = contains_alias (result, data)))
+ {
+ return temp;
+ }
+
+ if (result->aliases_count >= k_aliases_max)
+ {
+ // Not enough alias slots
+ set_err_internal_resource_full (result);
+ syslog (LOG_ERR,
+ "mdns: Internal alias buffer full; increase size"
+ );
+ return NULL;
+ }
+
+ new_alias = result->alias_idx - len;
+
+ if (new_alias < result->addr_idx)
+ {
+ // Not enough room
+ set_err_buf_too_small (result);
+ if (MDNS_VERBOSE)
+ syslog (LOG_DEBUG,
+ "mdns: Ran out of buffer when adding alias %d",
+ result->aliases_count + 1
+ );
+ return NULL;
+ }
+
+ start = result->buffer + new_alias;
+ memcpy (start, data, len);
+ result->alias_idx = new_alias;
+ result->header->aliases [result->aliases_count] = start;
+ result->aliases_count++;
+ result->header->aliases [result->aliases_count] = NULL;
+
+ return start;
+}
+
+
+static char *
+contains_alias (result_map_t * result, const char * alias)
+{
+ int i;
+
+ for (i = 0; result->header->aliases [i]; i++)
+ {
+ if (strcmp (result->header->aliases [i], alias) == 0)
+ {
+ return result->header->aliases [i];
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+ Add fully qualified hostname to result.
+
+ Parameter
+ result
+ Result structure to write to
+ fullname
+ Fully qualified hostname
+
+ Result
+ Pointer to start of hostname buffer,
+ or NULL on error (usually hostname too long)
+ */
+
+static char *
+add_hostname_len (result_map_t * result, const char * fullname, int len)
+{
+ if (len >= k_hostname_maxlen)
+ {
+ set_err_bad_hostname (result);
+ syslog (LOG_WARNING,
+ "mdns: Hostname too long '%.*s': len %d, max %d",
+ len,
+ fullname,
+ len,
+ k_hostname_maxlen
+ );
+ return NULL;
+ }
+
+ result->hostent->h_name =
+ strcpy (result->header->hostname, fullname);
+
+ return result->header->hostname;
+}
+
+
+/*
+ Add fully qualified name as hostname or alias.
+
+ If hostname is not fully qualified this is not an error, but the data
+ returned may be not what the application wanted.
+
+ Parameter
+ result
+ Result structure to write to
+ data
+ Incoming alias (null terminated)
+ len
+ Length of data buffer (in bytes), including trailing null
+
+ Result
+ Pointer to start of newly written data,
+ or NULL on error
+ If alias or hostname already exists, returns pointer to that instead.
+ */
+static char *
+add_hostname_or_alias (result_map_t * result, const char * data, int len)
+{
+ char * hostname = result->hostent->h_name;
+
+ if (*hostname)
+ {
+ if (strcmp (hostname, data) == 0)
+ {
+ return hostname;
+ }
+ else
+ {
+ return add_alias_to_buffer (result, data, len);
+ }
+ }
+ else
+ {
+ return add_hostname_len (result, data, len);
+ }
+}
+
+
+static int
+init_result (
+ result_map_t * result,
+ hostent * result_buf,
+ char * buf,
+ size_t buflen
+ )
+{
+ if (buflen < sizeof (buf_header_t))
+ {
+ return ERANGE;
+ }
+
+ result->hostent = result_buf;
+ result->header = (buf_header_t *) buf;
+ result->header->hostname[0] = 0;
+ result->aliases_count = 0;
+ result->header->aliases[0] = NULL;
+ result->addrs_count = 0;
+ result->header->addrs[0] = NULL;
+ result->buffer = buf + sizeof (buf_header_t);
+ result->addr_idx = 0;
+ result->alias_idx = buflen - sizeof (buf_header_t);
+ result->done = 0;
+ set_err_notfound (result);
+
+ // Point hostent to the right buffers
+ result->hostent->h_name = result->header->hostname;
+ result->hostent->h_aliases = result->header->aliases;
+ result->hostent->h_addr_list = result->header->addrs;
+
+ return 0;
+}
+
+/*
+ Set the status in the result.
+
+ Parameters
+ result
+ Result structure to update
+ status
+ New nss_status value
+ err
+ New errno value
+ herr
+ New h_errno value
+
+ Returns
+ New status value
+ */
+static nss_status
+set_err (result_map_t * result, nss_status status, int err, int herr)
+{
+ result->status = status;
+ result->r_errno = err;
+ result->r_h_errno = herr;
+
+ return status;
+}
+
+static nss_status
+set_err_notfound (result_map_t * result)
+{
+ return set_err (result, NSS_STATUS_NOTFOUND, ENOENT, HOST_NOT_FOUND);
+}
+
+static nss_status
+set_err_bad_hostname (result_map_t * result)
+{
+ return set_err (result, NSS_STATUS_TRYAGAIN, ENOENT, NO_RECOVERY);
+}
+
+static nss_status
+set_err_buf_too_small (result_map_t * result)
+{
+ return set_err (result, NSS_STATUS_TRYAGAIN, ERANGE, NETDB_INTERNAL);
+}
+
+static nss_status
+set_err_internal_resource_full (result_map_t * result)
+{
+ return set_err (result, NSS_STATUS_RETURN, ERANGE, NO_RECOVERY);
+}
+
+static nss_status
+set_err_system (result_map_t * result)
+{
+ return set_err (result, NSS_STATUS_UNAVAIL, errno, NETDB_INTERNAL);
+}
+
+static nss_status
+set_err_mdns_failed (result_map_t * result)
+{
+ return set_err (result, NSS_STATUS_TRYAGAIN, EAGAIN, TRY_AGAIN);
+}
+
+static nss_status
+set_err_success (result_map_t * result)
+{
+ result->status = NSS_STATUS_SUCCESS;
+ return result->status;
+}
+
+
+/*
+ Test whether name is applicable for mdns to process, and if so copy into
+ lookup_name buffer (if non-NULL).
+
+ Returns
+ Pointer to name to lookup up, if applicable, or NULL otherwise.
+ */
+static const char *
+is_applicable_name (
+ result_map_t * result,
+ const char * name,
+ char * lookup_name
+ )
+{
+ int match = config_is_mdns_suffix (name);
+ if (match > 0)
+ {
+ if (lookup_name)
+ {
+ strncpy (lookup_name, name, k_hostname_maxlen + 1);
+ return lookup_name;
+ }
+ else
+ {
+ return name;
+ }
+ }
+ else
+ {
+ if (match < 0)
+ {
+ set_err_system (result);
+ }
+ return NULL;
+ }
+}
+
+/*
+ Test whether address is applicable for mdns to process, and if so copy into
+ addr_str buffer as an address suitable for ptr lookup.
+
+ Returns
+ Pointer to name to lookup up, if applicable, or NULL otherwise.
+ */
+static const char *
+is_applicable_addr (
+ result_map_t * result,
+ const void * addr,
+ int af,
+ char * addr_str
+ )
+{
+ int match;
+
+ if (!format_reverse_addr (af, addr, -1, addr_str))
+ {
+ if (MDNS_VERBOSE)
+ syslog (LOG_DEBUG,
+ "mdns: Failed to create reverse address"
+ );
+ return NULL;
+ }
+
+ if (MDNS_VERBOSE)
+ syslog (LOG_DEBUG,
+ "mdns: Reverse address: %s",
+ addr_str
+ );
+
+ match = config_is_mdns_suffix (addr_str);
+ if (match > 0)
+ {
+ return addr_str;
+ }
+ else
+ {
+ if (match < 0)
+ {
+ set_err_system (result);
+ }
+ return NULL;
+ }
+}
+
+//----------
+// Types and Constants
+
+const char * k_conf_file = "/etc/nss_mdns.conf";
+#define CONF_LINE_SIZE 1024
+
+const char k_comment_char = '#';
+
+const char * k_keyword_domain = "domain";
+
+const char * k_default_domains [] =
+{
+ "local",
+ "254.169.in-addr.arpa",
+ "8.e.f.ip6.int",
+ "8.e.f.ip6.arpa",
+ "9.e.f.ip6.int",
+ "9.e.f.ip6.arpa",
+ "a.e.f.ip6.int",
+ "a.e.f.ip6.arpa",
+ "b.e.f.ip6.int",
+ "b.e.f.ip6.arpa",
+ NULL
+ // Always null terminated
+};
+
+// Linked list of domains
+typedef struct domain_entry
+{
+ char * domain;
+ struct domain_entry * next;
+} domain_entry_t;
+
+
+// Config
+typedef struct
+{
+ domain_entry_t * domains;
+} config_t;
+
+const config_t k_empty_config =
+{
+ NULL
+};
+
+
+// Context - tracks position in config file, used for error reporting
+typedef struct
+{
+ const char * filename;
+ int linenum;
+} config_file_context_t;
+
+
+//----------
+// Local prototypes
+
+static errcode_t
+load_config (config_t * conf);
+
+static errcode_t
+process_config_line (
+ config_t * conf,
+ char * line,
+ config_file_context_t * context
+ );
+
+static char *
+get_next_word (char * input, char **next);
+
+static errcode_t
+default_config (config_t * conf);
+
+static errcode_t
+add_domain (config_t * conf, const char * domain);
+
+static int
+contains_domain (const config_t * conf, const char * domain);
+
+static int
+contains_domain_suffix (const config_t * conf, const char * addr);
+
+
+//----------
+// Global variables
+
+static config_t * g_config = NULL;
+// Configuration info
+
+pthread_mutex_t g_config_mutex =
+#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
+ PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+#else
+ PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+
+//----------
+// Configuration functions
+
+
+/*
+ Initialise the configuration from the config file.
+
+ Returns
+ 0 success
+ non-zero error code on failure
+ */
+errcode_t
+init_config ()
+{
+ if (g_config)
+ {
+ /*
+ Safe to test outside mutex.
+ If non-zero, initialisation is complete and g_config can be
+ safely used read-only. If zero, then we do proper mutex
+ testing before initialisation.
+ */
+ return 0;
+ }
+ else
+ {
+ int errcode = -1;
+ int presult;
+ config_t * temp_config;
+
+ // Acquire mutex
+ presult = pthread_mutex_lock (&g_config_mutex);
+ if (presult)
+ {
+ syslog (LOG_ERR,
+ "mdns: Fatal mutex lock error in nss_mdns:init_config, %s:%d: %d: %s",
+ __FILE__, __LINE__, presult, strerror (presult)
+ );
+ return presult;
+ }
+
+ // Test again now we have mutex, in case initialisation occurred while
+ // we were waiting
+ if (!g_config)
+ {
+ temp_config = (config_t *) malloc (sizeof (config_t));
+ if (temp_config)
+ {
+ // Note: This code will leak memory if initialisation fails
+ // repeatedly. This should only happen in the case of a memory
+ // error, so I'm not sure if it's a meaningful problem. - AW
+ *temp_config = k_empty_config;
+ errcode = load_config (temp_config);
+
+ if (!errcode)
+ {
+ g_config = temp_config;
+ }
+ }
+ else
+ {
+ syslog (LOG_ERR,
+ "mdns: Can't allocate memory in nss_mdns:init_config, %s:%d",
+ __FILE__, __LINE__
+ );
+ errcode = errno;
+ }
+ }
+
+ presult = pthread_mutex_unlock (&g_config_mutex);
+ if (presult)
+ {
+ syslog (LOG_ERR,
+ "mdns: Fatal mutex unlock error in nss_mdns:init_config, %s:%d: %d: %s",
+ __FILE__, __LINE__, presult, strerror (presult)
+ );
+ errcode = presult;
+ }
+
+ return errcode;
+ }
+}
+
+
+int
+config_is_mdns_suffix (const char * name)
+{
+ int errcode = init_config ();
+ if (!errcode)
+ {
+ return contains_domain_suffix (g_config, name);
+ }
+ else
+ {
+ errno = errcode;
+ return -1;
+ }
+}
+
+
+//----------
+// Local functions
+
+static errcode_t
+load_config (config_t * conf)
+{
+ FILE * cf;
+ char line [CONF_LINE_SIZE];
+ config_file_context_t context;
+
+ context.filename = k_conf_file;
+ context.linenum = 0;
+
+
+ cf = fopen (context.filename, "r");
+ if (!cf)
+ {
+ syslog (LOG_INFO,
+ "mdns: Couldn't open nss_mdns configuration file %s, using default.",
+ context.filename
+ );
+ return default_config (conf);
+ }
+
+ while (fgets (line, CONF_LINE_SIZE, cf))
+ {
+ int errcode;
+ context.linenum++;
+ errcode = process_config_line (conf, line, &context);
+ if (errcode)
+ {
+ // Critical error, give up
+ fclose(cf);
+ return errcode;
+ }
+ }
+
+ fclose (cf);
+
+ return 0;
+}
+
+
+/*
+ Parse a line of the configuration file.
+ For each keyword recognised, perform appropriate handling.
+ If the keyword is not recognised, print a message to syslog
+ and continue.
+
+ Returns
+ 0 success, or recoverable config file error
+ non-zero serious system error, processing aborted
+ */
+static errcode_t
+process_config_line (
+ config_t * conf,
+ char * line,
+ config_file_context_t * context
+ )
+{
+ char * curr = line;
+ char * word;
+
+ word = get_next_word (curr, &curr);
+ if (!word || word [0] == k_comment_char)
+ {
+ // Nothing interesting on this line
+ return 0;
+ }
+
+ if (strcmp (word, k_keyword_domain) == 0)
+ {
+ word = get_next_word (curr, &curr);
+ if (word)
+ {
+ int errcode = add_domain (conf, word);
+ if (errcode)
+ {
+ // something badly wrong, bail
+ return errcode;
+ }
+
+ if (get_next_word (curr, NULL))
+ {
+ syslog (LOG_WARNING,
+ "%s, line %d: ignored extra text found after domain",
+ context->filename,
+ context->linenum
+ );
+ }
+ }
+ else
+ {
+ syslog (LOG_WARNING,
+ "%s, line %d: no domain specified",
+ context->filename,
+ context->linenum
+ );
+ }
+ }
+ else
+ {
+ syslog (LOG_WARNING,
+ "%s, line %d: unknown keyword %s - skipping",
+ context->filename,
+ context->linenum,
+ word
+ );
+ }
+
+ return 0;
+}
+
+
+/*
+ Get next word (whitespace separated) from input string.
+ A null character is written into the first whitespace character following
+ the word.
+
+ Parameters
+ input
+ Input string. This string is modified by get_next_word.
+ next
+ If non-NULL and the result is non-NULL, a pointer to the
+ character following the end of the word (after the null)
+ is written to 'next'.
+ If no word is found, the original value is unchanged.
+ If the word extended to the end of the string, 'next' points
+ to the trailling NULL.
+ It is safe to pass 'str' as 'input' and '&str' as 'next'.
+ Returns
+ Pointer to the first non-whitespace character (and thus word) found.
+ if no word is found, returns NULL.
+ */
+static char *
+get_next_word (char * input, char **next)
+{
+ char * curr = input;
+ char * result;
+
+ while (isspace (*curr))
+ {
+ curr++;
+ }
+
+ if (*curr == 0)
+ {
+ return NULL;
+ }
+
+ result = curr;
+ while (*curr && !isspace (*curr))
+ {
+ curr++;
+ }
+ if (*curr)
+ {
+ *curr = 0;
+ if (next)
+ {
+ *next = curr+1;
+ }
+ }
+ else
+ {
+ if (next)
+ {
+ *next = curr;
+ }
+ }
+
+ return result;
+}
+
+
+static errcode_t
+default_config (config_t * conf)
+{
+ int i;
+ for (i = 0; k_default_domains [i]; i++)
+ {
+ int errcode =
+ add_domain (conf, k_default_domains [i]);
+ if (errcode)
+ {
+ // Something has gone (badly) wrong - let's bail
+ return errcode;
+ }
+ }
+
+ return 0;
+}
+
+
+static errcode_t
+add_domain (config_t * conf, const char * domain)
+{
+ if (!contains_domain (conf, domain))
+ {
+ domain_entry_t * d =
+ (domain_entry_t *) malloc (sizeof (domain_entry_t));
+ if (!d)
+ {
+ syslog (LOG_ERR,
+ "mdns: Can't allocate memory in nss_mdns:init_config, %s:%d",
+ __FILE__, __LINE__
+ );
+ return ENOMEM;
+ }
+
+ d->domain = strdup (domain);
+ if (!d->domain)
+ {
+ syslog (LOG_ERR,
+ "mdns: Can't allocate memory in nss_mdns:init_config, %s:%d",
+ __FILE__, __LINE__
+ );
+ free (d);
+ return ENOMEM;
+ }
+ d->next = conf->domains;
+ conf->domains = d;
+ }
+
+ return 0;
+}
+
+
+static int
+contains_domain (const config_t * conf, const char * domain)
+{
+ const domain_entry_t * curr = conf->domains;
+
+ while (curr != NULL)
+ {
+ if (strcasecmp (curr->domain, domain) == 0)
+ {
+ return 1;
+ }
+
+ curr = curr->next;
+ }
+
+ return 0;
+}
+
+
+static int
+contains_domain_suffix (const config_t * conf, const char * addr)
+{
+ const domain_entry_t * curr = conf->domains;
+
+ while (curr != NULL)
+ {
+ if (cmp_dns_suffix (addr, curr->domain) > 0)
+ {
+ return 1;
+ }
+
+ curr = curr->next;
+ }
+
+ return 0;
+}
+
+//----------
+// Types and Constants
+
+static const char * k_local_suffix = "local";
+static const char k_dns_separator = '.';
+
+static const unsigned int k_label_maxlen = DNS_LABEL_MAXLEN;
+// Label entries longer than this are actually pointers.
+
+typedef struct
+{
+ int value;
+ const char * name;
+ const char * comment;
+} table_entry_t;
+
+static const table_entry_t k_table_af [] =
+{
+ { AF_UNSPEC, NULL, NULL },
+ { AF_LOCAL, "LOCAL", NULL },
+ { AF_UNIX, "UNIX", NULL },
+ { AF_INET, "INET", NULL },
+ { AF_INET6, "INET6", NULL }
+};
+static const int k_table_af_size =
+ sizeof (k_table_af) / sizeof (*k_table_af);
+
+static const char * k_table_ns_class [] =
+{
+ NULL,
+ "IN"
+};
+static const int k_table_ns_class_size =
+ sizeof (k_table_ns_class) / sizeof (*k_table_ns_class);
+
+static const char * k_table_ns_type [] =
+{
+ NULL,
+ "A",
+ "NS",
+ "MD",
+ "MF",
+ "CNAME",
+ "SOA",
+ "MB",
+ "MG",
+ "MR",
+ "NULL",
+ "WKS",
+ "PTR",
+ "HINFO",
+ "MINFO",
+ "MX",
+ "TXT",
+ "RP",
+ "AFSDB",
+ "X25",
+ "ISDN",
+ "RT",
+ "NSAP",
+ NULL,
+ "SIG",
+ "KEY",
+ "PX",
+ "GPOS",
+ "AAAA",
+ "LOC",
+ "NXT",
+ "EID",
+ "NIMLOC",
+ "SRV",
+ "ATMA",
+ "NAPTR",
+ "KX",
+ "CERT",
+ "A6",
+ "DNAME",
+ "SINK",
+ "OPT"
+};
+static const int k_table_ns_type_size =
+ sizeof (k_table_ns_type) / sizeof (*k_table_ns_type);
+
+
+//----------
+// Local prototypes
+
+static int
+simple_table_index (const char * table [], int size, const char * str);
+
+static int
+table_index_name (const table_entry_t table [], int size, const char * str);
+
+static int
+table_index_value (const table_entry_t table [], int size, int n);
+
+
+//----------
+// Global variables
+
+
+//----------
+// Util functions
+
+int
+count_dots (const char * name)
+{
+ int count = 0;
+ int i;
+ for (i = 0; name[i]; i++)
+ {
+ if (name [i] == k_dns_separator)
+ count++;
+ }
+
+ return count;
+}
+
+
+int
+islocal (const char * name)
+{
+ return cmp_dns_suffix (name, k_local_suffix) > 0;
+}
+
+
+int
+rr_to_af (ns_type_t rrtype)
+{
+ switch (rrtype)
+ {
+ case kDNSServiceType_A:
+ return AF_INET;
+
+ case kDNSServiceType_AAAA:
+ return AF_INET6;
+
+ default:
+ return AF_UNSPEC;
+ }
+}
+
+
+ns_type_t
+af_to_rr (int af)
+{
+ switch (af)
+ {
+ case AF_INET:
+ return kDNSServiceType_A;
+
+ case AF_INET6:
+ return kDNSServiceType_AAAA;
+
+ default:
+ //return ns_t_invalid;
+ return 0;
+ }
+}
+
+
+int
+str_to_af (const char * str)
+{
+ int result =
+ table_index_name (k_table_af, k_table_af_size, str);
+ if (result < 0)
+ result = 0;
+
+ return k_table_af [result].value;
+}
+
+
+ns_class_t
+str_to_ns_class (const char * str)
+{
+ return (ns_class_t)
+ simple_table_index (k_table_ns_class, k_table_ns_class_size, str);
+}
+
+
+ns_type_t
+str_to_ns_type (const char * str)
+{
+ return (ns_type_t)
+ simple_table_index (k_table_ns_type, k_table_ns_type_size, str);
+}
+
+
+const char *
+af_to_str (int in)
+{
+ int result =
+ table_index_value (k_table_af, k_table_af_size, in);
+ if (result < 0)
+ result = 0;
+
+ return k_table_af [result].name;
+}
+
+
+const char *
+ns_class_to_str (ns_class_t in)
+{
+ if (in < k_table_ns_class_size)
+ return k_table_ns_class [in];
+ else
+ return NULL;
+}
+
+
+const char *
+ns_type_to_str (ns_type_t in)
+{
+ if (in < k_table_ns_type_size)
+ return k_table_ns_type [in];
+ else
+ return NULL;
+}
+
+
+char *
+format_reverse_addr_in (
+ const struct in_addr * addr,
+ int prefixlen,
+ char * buf
+ )
+{
+ char * curr = buf;
+ int i;
+
+ const uint8_t * in_addr_a = (uint8_t *) addr;
+
+ if (prefixlen > 32)
+ return NULL;
+ if (prefixlen < 0)
+ prefixlen = 32;
+
+ i = (prefixlen + 7) / 8;
+ // divide prefixlen into bytes, rounding up
+
+ while (i > 0)
+ {
+ i--;
+ curr += sprintf (curr, "%d.", in_addr_a [i]);
+ }
+ sprintf (curr, "in-addr.arpa");
+
+ return buf;
+}
+
+
+char *
+format_reverse_addr_in6 (
+ const struct in6_addr * addr,
+ int prefixlen,
+ char * buf
+ )
+{
+ char * curr = buf;
+ int i;
+
+ const uint8_t * in_addr_a = (uint8_t *) addr;
+
+ if (prefixlen > 128)
+ return NULL;
+ if (prefixlen < 0)
+ prefixlen = 128;
+
+ i = (prefixlen + 3) / 4;
+ // divide prefixlen into nibbles, rounding up
+
+ // Special handling for first
+ if (i % 2)
+ {
+ curr += sprintf (curr, "%d.", (in_addr_a [i/2] >> 4) & 0x0F);
+ }
+ i >>= 1;
+ // Convert i to bytes (divide by 2)
+
+ while (i > 0)
+ {
+ uint8_t val;
+
+ i--;
+ val = in_addr_a [i];
+ curr += sprintf (curr, "%x.%x.", val & 0x0F, (val >> 4) & 0x0F);
+ }
+ sprintf (curr, "ip6.arpa");
+
+ return buf;
+}
+
+
+char *
+format_reverse_addr (
+ int af,
+ const void * addr,
+ int prefixlen,
+ char * buf
+ )
+{
+ switch (af)
+ {
+ case AF_INET:
+ return
+ format_reverse_addr_in (
+ (struct in_addr *) addr, prefixlen, buf
+ );
+ break;
+
+ case AF_INET6:
+ return
+ format_reverse_addr_in6 (
+ (struct in6_addr *) addr, prefixlen, buf
+ );
+ break;
+
+ default:
+ return NULL;
+ }
+}
+
+
+int
+cmp_dns_suffix (const char * name, const char * domain)
+{
+ const char * nametail;
+ const char * domaintail;
+
+ // Idiot checks
+ if (*name == 0 || *name == k_dns_separator)
+ {
+ // Name can't be empty or start with separator
+ return CMP_DNS_SUFFIX_BAD_NAME;
+ }
+
+ if (*domain == 0)
+ {
+ return CMP_DNS_SUFFIX_SUCCESS;
+ // trivially true
+ }
+
+ if (*domain == k_dns_separator)
+ {
+ // drop leading separator from domain
+ domain++;
+ if (*domain == k_dns_separator)
+ {
+ return CMP_DNS_SUFFIX_BAD_DOMAIN;
+ }
+ }
+
+ // Find ends of strings
+ for (nametail = name; *nametail; nametail++)
+ ;
+ for (domaintail = domain; *domaintail; domaintail++)
+ ;
+
+ // Shuffle back to last real character, and drop any trailing '.'
+ // while we're at it.
+ nametail--;
+ if (*nametail == k_dns_separator)
+ {
+ nametail--;
+ if (*nametail == k_dns_separator)
+ {
+ return CMP_DNS_SUFFIX_BAD_NAME;
+ }
+ }
+ domaintail--;
+ if (*domaintail == k_dns_separator)
+ {
+ domaintail--;
+ if (*domaintail == k_dns_separator)
+ {
+ return CMP_DNS_SUFFIX_BAD_DOMAIN;
+ }
+ }
+
+ // Compare.
+ while (
+ nametail >= name
+ && domaintail >= domain
+ && tolower(*nametail) == tolower(*domaintail))
+ {
+ nametail--;
+ domaintail--;
+ }
+
+ /* A successful finish will be one of the following:
+ (leading and trailing . ignored)
+
+ name : domain2.domain1
+ domain: domain2.domain1
+ ^
+
+ name : domain3.domain2.domain1
+ domain: domain2.domain1
+ ^
+ */
+ if (
+ domaintail < domain
+ && (nametail < name || *nametail == k_dns_separator)
+ )
+ {
+ return CMP_DNS_SUFFIX_SUCCESS;
+ }
+ else
+ {
+ return CMP_DNS_SUFFIX_FAILURE;
+ }
+}
+
+
+static int
+dns_rdata_to_name (const unsigned char * rdata, int rdlen, char * name, unsigned int name_len)
+{
+ int i = 0;
+ // Index into 'name'
+ const unsigned char * rdata_curr = rdata;
+
+ if (rdlen == 0) return DNS_RDATA_TO_NAME_BAD_FORMAT;
+
+ /*
+ In RDATA, a DNS name is stored as a series of labels.
+ Each label consists of a length octet (max value 63)
+ followed by the data for that label.
+ The series is terminated with a length 0 octet.
+ A length octet beginning with bits 11 is a pointer to
+ somewhere else in the payload, but we don't support these
+ since we don't have access to the entire payload.
+
+ See RFC1034 section 3.1 and RFC1035 section 3.1.
+ */
+ while (1)
+ {
+ unsigned int term_len = *rdata_curr;
+ rdata_curr++;
+
+ if (term_len == 0)
+ {
+ break;
+ // 0 length record terminates label
+ }
+ else if (term_len > k_label_maxlen)
+ {
+ name [i] = 0;
+ return DNS_RDATA_TO_NAME_PTR;
+ }
+ else if (rdata_curr + term_len > rdata + rdlen)
+ {
+ name [i] = 0;
+ return DNS_RDATA_TO_NAME_BAD_FORMAT;
+ }
+
+ if (name_len < i + term_len + 1)
+ // +1 is separator
+ {
+ name [i] = 0;
+ return DNS_RDATA_TO_NAME_TOO_LONG;
+ }
+
+ memcpy (name + i, rdata_curr, term_len);
+
+ i += term_len;
+ rdata_curr += term_len;
+
+ name [i] = k_dns_separator;
+ i++;
+ }
+
+ name [i] = 0;
+ return i;
+}
+
+
+//----------
+// Local functions
+
+/*
+ Find the index of an string entry in a table. A case insenitive match
+ is performed. If no entry is found, 0 is returned.
+
+ Parameters
+ table
+ Lookup table
+ Table entries may be NULL. NULL entries will never match.
+ size
+ number of entries in table
+ str
+ lookup string
+
+ Result
+ index of first matching entry, or 0 if no matches
+ */
+static int
+simple_table_index (const char * table [], int size, const char * str)
+{
+ int i;
+ for (i = 0; i < size; i++)
+ {
+ if (
+ table [i]
+ && (strcasecmp (table [i], str) == 0)
+ )
+ {
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ Find the index of a name in a table.
+
+ Parameters
+ table
+ array of table_entry_t records. The name field is compared
+ (ignoring case) to the input string.
+ size
+ number of entries in table
+ str
+ lookup string
+
+ Result
+ index of first matching entry, or -1 if no matches
+ */
+static int
+table_index_name (const table_entry_t table [], int size, const char * str)
+{
+ int i;
+ for (i = 0; i < size; i++)
+ {
+ if (
+ table [i].name
+ && (strcasecmp (table [i].name, str) == 0)
+ )
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+
+/*
+ Find the index of a value a table.
+
+ Parameters
+ table
+ array of table_entry_t records. The value field is compared to
+ the input value
+ size
+ number of entries in table
+ n
+ lookup value
+
+ Result
+ index of first matching entry, or -1 if no matches
+ */
+static int
+table_index_value (const table_entry_t table [], int size, int n)
+{
+ int i;
+ for (i = 0; i < size; i++)
+ {
+ if (table [i].value == n)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
diff --git a/mDNSResponder/mDNSPosix/nss_mdns.conf b/mDNSResponder/mDNSPosix/nss_mdns.conf
new file mode 100755
index 00000000..bdcdad7c
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/nss_mdns.conf
@@ -0,0 +1,13 @@
+# Defaut configuration file for nss_mdns
+
+# Applicable domains
+domain local
+domain 254.169.in-addr.arpa
+domain 8.e.f.ip6.int
+domain 9.e.f.ip6.int
+domain a.e.f.ip6.int
+domain b.e.f.ip6.int
+domain 8.e.f.ip6.arpa
+domain 9.e.f.ip6.arpa
+domain a.e.f.ip6.arpa
+domain b.e.f.ip6.arpa
diff --git a/mDNSResponder/mDNSPosix/nss_mdns.conf.5 b/mDNSResponder/mDNSPosix/nss_mdns.conf.5
new file mode 100755
index 00000000..7dbefa22
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/nss_mdns.conf.5
@@ -0,0 +1,135 @@
+.\"
+.\" See section LICENSE for license information.
+.\"
+.Dd June 15, 2004
+.Dt NSS_MDNS.CONF 5
+.Os
+.Sh NAME
+.Nm nss_mdns.conf
+.Nd configuration file for
+.Xr libnss_mdns 8 .
+.Sh DESCRIPTION
+This file describes the domains that
+.Xr libnss_mdns 8
+is to support. If a lookup domain is not in this list, then
+.Li NSS_STATUS_NOTFOUND
+will be returned to libc and processing will continue according to
+.Xr nsswitch.conf 5 .
+.Ss Configuration file format
+Lines containing only whitespace or lines whose first non-whitespace character is
+.Ql #
+are ignored. No single line may be greater than 1023 characters plus end-of-line.
+.Pp
+.D1 Ic domain Ar x.y.z
+.Pp
+Enable use of
+.Xr libnss_mdns 8
+to lookup DNS entries in the
+.Ql x.y.z
+domain. Leading and trailing dots are dropped.
+.Pp
+Reverse (PTR) lookups are enabled using their DNS names. IPv6 names use
+.Qq nibble format .
+.Pp
+.Dl domain 254.169.in-addr.arpa
+.Dl domain 0.8.e.f.ip6.arpa
+.Ss Default configuration
+If the configuration file cannot be found then the following is assumed.
+.Bd -literal -offset indent
+domain local
+domain 0.8.e.f.ip6.int
+domain 0.8.e.f.ip6.arpa
+domain 254.169.in-addr.arpa
+.Ed
+.Sh SEE ALSO
+.\" Cross-references should be ordered by section (low to high), then in
+.\" alphabetical order.
+.Xr nsswitch.conf 5 ,
+.Xr libnss_mdns 8
+.\" .Sh STANDARDS
+.Sh HISTORY
+.Xr libnss_mdns 8
+was originally written for
+.An NICTA Bq http://www.nicta.com.au/ .
+.Sh AUTHORS
+.An "Andrew White" Bq Andrew.White@nicta.com.au
+.Sh LICENSE
+This software is licensed under the NICTA Public Source License version 1.0
+.Ss NICTA Public Software Licence
+Version 1.0
+.Pp
+Copyright 2004 National ICT Australia Ltd
+.Pp
+All rights reserved.
+.Pp
+By this licence, National ICT Australia Ltd (NICTA) grants permission,
+free of charge, to any person who obtains a copy of this software
+and any associated documentation files ("the Software") to use and
+deal with the Software in source code and binary forms without
+restriction, with or without modification, and to permit persons
+to whom the Software is furnished to do so, provided that the
+following conditions are met:
+.Bl -bullet
+.It
+Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimers.
+.It
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimers in
+the documentation and/or other materials provided with the
+distribution.
+.It
+The name of NICTA may not be used to endorse or promote products
+derived from this Software without specific prior written permission.
+.El
+.Pp
+EXCEPT AS EXPRESSLY STATED IN THIS LICENCE AND TO THE FULL EXTENT
+PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS-IS" AND
+NICTA MAKES NO REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY
+REPRESENTATIONS, WARRANTIES OR CONDITIONS REGARDING THE CONTENTS
+OR ACCURACY OF THE SOFTWARE, OR OF TITLE, MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, THE ABSENCE OF LATENT
+OR OTHER DEFECTS, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR
+NOT DISCOVERABLE.
+.Pp
+TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL
+NICTA BE LIABLE ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+NEGLIGENCE) FOR ANY LOSS OR DAMAGE WHATSOEVER, INCLUDING (WITHOUT
+LIMITATION) LOSS OF PRODUCTION OR OPERATION TIME, LOSS, DAMAGE OR
+CORRUPTION OF DATA OR RECORDS; OR LOSS OF ANTICIPATED SAVINGS,
+OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR OTHER ECONOMIC LOSS;
+OR ANY SPECIAL, INCIDENTAL, INDIRECT, CONSEQUENTIAL, PUNITIVE OR
+EXEMPLARY DAMAGES ARISING OUT OF OR IN CONNECTION WITH THIS LICENCE,
+THE SOFTWARE OR THE USE OF THE SOFTWARE, EVEN IF NICTA HAS BEEN
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+.Pp
+If applicable legislation implies warranties or conditions, or
+imposes obligations or liability on NICTA in respect of the Software
+that cannot be wholly or partly excluded, restricted or modified,
+NICTA's liability is limited, to the full extent permitted by the
+applicable legislation, at its option, to:
+.Pp
+.Bl -tag -width "a." -compact
+.It a.
+in the case of goods, any one or more of the following:
+.Bl -tag -width "iii." -compact
+.It i.
+the replacement of the goods or the supply of equivalent goods;
+.It ii.
+the repair of the goods;
+.It iii.
+the payment of the cost of replacing the goods or of acquiring
+equivalent goods;
+.It iv.
+the payment of the cost of having the goods repaired; or
+.El
+.It b.
+in the case of services:
+.Bl -tag -width "iii." -compact
+.It i.
+the supplying of the services again; or
+.It ii.
+the payment of the cost of having the services supplied again.
+.El
+.El
diff --git a/mDNSResponder/mDNSPosix/parselog.py b/mDNSResponder/mDNSPosix/parselog.py
new file mode 100755
index 00000000..f39d0f23
--- /dev/null
+++ b/mDNSResponder/mDNSPosix/parselog.py
@@ -0,0 +1,247 @@
+#!/usr/bin/python
+# Emacs settings: -*- tab-width: 4 -*-
+#
+# Copyright (c) 2002-2003 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.
+#
+# parselog.py, written and contributed by Kevin Marks
+#
+# Requires OS X 10.3 Panther or later, for Python and Core Graphics Python APIs
+# Invoke from the command line with "parselog.py fname" where fname is a log file made by mDNSNetMonitor
+#
+# Caveats:
+# It expects plain ASCII, and doesn't handle spaces in record names very well right now
+# There's a procedure you can follow to 'sanitize' an mDNSNetMonitor log file to make it more paletable to parselog.py:
+# 1. Run mDNSNetMonitor in a terminal window.
+# When you have enough traffic, type Ctrl-C and save the content of the terminal window to disk.
+# Alternatively, you can use "mDNSNetMonitor > logfile" to write the text directly to a file.
+# You now have a UTF-8 text file.
+# 2. Open the UTF-8 text file using BBEdit or some other text editor.
+# (These instructions are for BBEdit, which I highly recommend you use when doing this.)
+# 3. Make sure BBEdit correctly interprets the file as UTF-8.
+# Either set your "Text Files Opening" preference to "UTF-8 no BOM", and drop the file onto BBEdit,
+# or manually open the File using "File -> Open" and make sure the "Read As" setting is set to "UTF-8 no BOM"
+# Check in the document pulldown menu in the window toolbar to make sure that it says "Encoding: UTF-8 no BOM"
+# 4. Use "Tools -> Convert to ASCII" to replace all special characters with their seven-bit ascii equivalents.
+# (e.g. curly quotes are converted to straight quotes)
+# 5. Do a grep search and replace. (Cmd-F; make sure Grep checkbox is turned on.)
+# Enter this search text : ^(.................\(................\S*) (.* -> .*)$
+# Enter this replacement text: \1-\2
+# Click "Replace All"
+# Press Cmd-Opt-= repeatedly until there are no more instances to be replaced.
+# You now have text file with all spaces in names changed to hyphens
+# 6. Save the new file. You can save it as "UTF-8 no BOM", or as "Mac Roman". It really doesn't matter which --
+# the file now contains only seven-bit ascii, so it's all the same no matter how you save it.
+# 7. Run "parselog.py fname"
+# 8. Open the resulting fname.pdf file with a PDF viewer like Preview on OS X
+#
+# Key to what you see:
+# Time is on the horizontal axis
+# Individual machines are shown on the vertical axis
+# Filled red circle: Normal query Hollow red circle: Query requesting unicast reply
+# Filled orange circle: Probe (service starting) Hollow orange circle: First probe (requesting unicast reply)
+# Filled green circle: Normal answer Hollow green circle: Goodbye message (record going away)
+# Hollow blue circle: Legacy query (from old client)
+
+from CoreGraphics import *
+import math # for pi
+
+import string
+import sys, os
+import re
+
+def parselog(inFile):
+ f = open(inFile)
+ hunt = 'getTime'
+ ipList = {}
+ querySource = {}
+ plotPoints = []
+ maxTime=0
+ minTime = 36*60*60
+ spaceExp = re.compile(r'\s+')
+ print "Reading " + inFile
+ while 1:
+ lines = f.readlines(100000)
+ if not lines:
+ break
+ for line in lines:
+ if (hunt == 'skip'):
+ if (line == '\n' or line == '\r' or line ==''):
+ hunt = 'getTime'
+# else:
+# msg = ("skipped" , line)
+# print msg
+ elif (hunt == 'getTime'):
+ if (line == "^C\n" ):
+ break
+ time = line.split(' ')[0].split(':')
+ if (len(time)<3):
+ #print "bad time, skipping",time
+ hunt = 'skip'
+ else:
+ hunt = 'getIP'
+ #print (("getTime:%s" % (line)), time)
+ elif (hunt == 'getIP'):
+ ip = line.split(' ',1)
+ ip = ip[0]
+ secs=0
+ for t in time:
+ secs = secs*60 +float(t)
+ if (secs>maxTime):
+ maxTime=secs
+ if (secs<minTime):
+ minTime=secs
+ if (not ip in ipList):
+ ipList[ip] = [len(ipList), "", ""]
+ #print (("getIP:%s" % (line)), time, secs)
+ hunt = 'getQA'
+ elif (hunt == 'getQA'):
+ qaList = spaceExp.split(line)
+ # qaList[0] Source Address
+ # qaList[1] Operation type (PU/PM/QU/QM/AN etc.)
+ # qaList[2] Record type (PTR/SRV/TXT etc.)
+ # For QU/QM/LQ:
+ # qaList[3] RR name
+ # For PU/PM/AN/AN+/AD/AD+/KA:
+ # qaList[3] TTL
+ # qaList[4] RR name
+ # qaList[5...] "->" symbol and following rdata
+ #print qaList
+ if (qaList[0] == ip):
+ if (qaList[1] == '(QU)' or qaList[1] == '(LQ)' or qaList[1] == '(PU)'):
+ plotPoints.append([secs, ipList[ip][0], (qaList[1])[1:-1]])
+ elif (qaList[1] == '(QM)'):
+ plotPoints.append([secs, ipList[ip][0], (qaList[1])[1:-1]])
+ querySource[qaList[3]] = len(plotPoints)-1
+ elif (qaList[1] == '(PM)'):
+ plotPoints.append([secs, ipList[ip][0], (qaList[1])[1:-1]])
+ querySource[qaList[4]] = len(plotPoints)-1
+ elif (qaList[1] == '(AN)' or qaList[1] == '(AN+)' or qaList[1] == '(DE)'):
+ plotPoints.append([secs, ipList[ip][0], (qaList[1])[1:-1]])
+ try:
+ theQuery = querySource[qaList[4]]
+ theDelta = secs - plotPoints[theQuery][0]
+ if (theDelta < 1.0):
+ plotPoints[-1].append(querySource[qaList[4]])
+ #print "Answer AN+ %s points to %d" % (qaList[4],querySource[qaList[4]])
+ except:
+ #print "Couldn't find any preceeding question for", qaList
+ pass
+ elif (qaList[1] != '(KA)' and qaList[1] != '(AD)' and qaList[1] != '(AD+)'):
+ print "Operation unknown", qaList
+
+ if (qaList[1] == '(AN)' or qaList[1] == '(AN+)' or qaList[1] == '(AD)' or qaList[1] == '(AD+)'):
+ if (qaList[2] == 'HINFO'):
+ ipList[ip][1] = qaList[4]
+ ipList[ip][2] = string.join(qaList[6:])
+ #print ipList[ip][1]
+ elif (qaList[2] == 'AAAA'):
+ if (ipList[ip][1] == ""):
+ ipList[ip][1] = qaList[4]
+ ipList[ip][2] = "Panther"
+ elif (qaList[2] == 'Addr'):
+ if (ipList[ip][1] == ""):
+ ipList[ip][1] = qaList[4]
+ ipList[ip][2] = "Jaguar"
+ else:
+ if (line == '\n'):
+ hunt = 'getTime'
+ else:
+ hunt = 'skip'
+ f.close()
+ #print plotPoints
+ #print querySource
+ #width=20.0*(maxTime-minTime)
+ if (maxTime < minTime + 10.0):
+ maxTime = minTime + 10.0
+ typesize = 12
+ width=20.0*(maxTime-minTime)
+ pageHeight=(len(ipList)+1) * typesize
+ scale = width/(maxTime-minTime)
+ leftMargin = typesize * 60
+ bottomMargin = typesize
+ pageRect = CGRectMake (-leftMargin, -bottomMargin, leftMargin + width, bottomMargin + pageHeight) # landscape
+ outFile = "%s.pdf" % (".".join(inFile.split('.')[:-1]))
+ c = CGPDFContextCreateWithFilename (outFile, pageRect)
+ print "Writing " + outFile
+ ourColourSpace = c.getColorSpace()
+ # QM/QU red solid/hollow
+ # PM/PU orange solid/hollow
+ # LQ blue hollow
+ # AN/DA green solid/hollow
+ #colourLookup = {"L":(0.0,0.0,.75), "Q":(.75,0.0,0.0), "P":(.75,0.5,0.0), "A":(0.0,0.75,0.0), "D":(0.0,0.75,0.0), "?":(.25,0.25,0.25)}
+ colourLookup = {"L":(0.0,0.0,1.0), "Q":(1.0,0.0,0.0), "P":(1.0,0.8,0.0), "A":(0.0,1.0,0.0), "D":(0.0,1.0,0.0), "?":(1.0,1.0,1.0)}
+ c.beginPage (pageRect)
+ c.setRGBFillColor(.75,0.0,0.0,1.0)
+ c.setRGBStrokeColor(.25,0.75,0.25,1.0)
+ c.setLineWidth(0.25)
+ for point in plotPoints:
+ #c.addArc((point[0]-minTime)*scale,point[1]*typesize+6,5,0,2*math.pi,1)
+ c.addArc((point[0]-minTime)*scale,point[1]*typesize+6,typesize/4,0,2*math.pi,1)
+ theColour = colourLookup[(point[2])[0]]
+ if (((point[2])[0]) != "L") and (((point[2])[0]) != "Q") and (((point[2])[0]) != "P") and (((point[2])[0]) != "A") and (((point[2])[0]) != "D"):
+ print "Unknown", point
+ if ((point[2])[-1] == "M" or (point[2])[0]== "A"):
+ c.setRGBFillColor(theColour[0],theColour[1],theColour[2],.5)
+ c.fillPath()
+ else:
+ c.setRGBStrokeColor(theColour[0],theColour[1],theColour[2],.5)
+ c.setLineWidth(1.0)
+ c.strokePath()
+ c.setRGBStrokeColor(.25,0.75,0.25,1.0)
+ c.setLineWidth(0.25)
+ for index in point[3:]:
+ c.beginPath()
+ c.moveToPoint((point[0]-minTime)*scale,point[1]*typesize+6)
+ c.addLineToPoint(((plotPoints[index])[0]-minTime)*scale,(plotPoints[index])[1]*typesize+6)
+ c.closePath()
+ c.strokePath()
+ c.setRGBFillColor (0,0,0, 1)
+ c.setTextDrawingMode (kCGTextFill)
+ c.setTextMatrix (CGAffineTransformIdentity)
+ c.selectFont ('Gill Sans', typesize, kCGEncodingMacRoman)
+ c.setRGBStrokeColor(0.25,0.0,0.0,1.0)
+ c.setLineWidth(0.1)
+ for ip,[height,hname,hinfo] in ipList.items():
+ c.beginPath()
+ c.moveToPoint(pageRect.origin.x,height*typesize+6)
+ c.addLineToPoint(width,height*typesize+6)
+ c.closePath()
+ c.strokePath()
+ c.showTextAtPoint(pageRect.origin.x + 2, height*typesize + 2, ip, len(ip))
+ c.showTextAtPoint(pageRect.origin.x + 2 + typesize*8, height*typesize + 2, hname, len(hname))
+ c.showTextAtPoint(pageRect.origin.x + 2 + typesize*25, height*typesize + 2, hinfo, len(hinfo))
+ for time in range (int(minTime),int(maxTime)+1):
+ c.beginPath()
+ c.moveToPoint((time-minTime)*scale,pageRect.origin.y)
+ c.addLineToPoint((time-minTime)*scale,pageHeight)
+ c.closePath()
+ if (int(time) % 10 == 0):
+ theHours = time/3600
+ theMinutes = time/60 % 60
+ theSeconds = time % 60
+ theTimeString = '%d:%02d:%02d' % (theHours, theMinutes, theSeconds)
+ # Should measure string width, but don't know how to do that
+ theStringWidth = typesize * 3.5
+ c.showTextAtPoint((time-minTime)*scale - theStringWidth/2, pageRect.origin.y + 2, theTimeString, len(theTimeString))
+ c.setLineWidth(0.3)
+ else:
+ c.setLineWidth(0.1)
+ c.strokePath()
+ c.endPage()
+ c.finish()
+
+
+for arg in sys.argv[1:]:
+ parselog(arg)