147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/* -*- Mode: C; tab-width: 4 -*-
247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Licensed under the Apache License, Version 2.0 (the "License");
647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * you may not use this file except in compliance with the License.
747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * You may obtain a copy of the License at
847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *     http://www.apache.org/licenses/LICENSE-2.0
1047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
1147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Unless required by applicable law or agreed to in writing, software
1247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * distributed under the License is distributed on an "AS IS" BASIS,
1347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * See the License for the specific language governing permissions and
1547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * limitations under the License.
1647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
1747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Formatting notes:
1847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
1947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
2047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * but for the sake of brevity here I will say just this: Curly braces are not syntactially
2147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * part of an "if" statement; they are the beginning and ending markers of a compound statement;
2247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * therefore common sense dictates that if they are part of a compound statement then they
2347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * should be indented to the same level as everything else in that compound statement.
2447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Indenting curly braces at the same level as the "if" implies that curly braces are
2547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
2647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
2747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * understand why variable y is not of type "char*" just proves the point that poor code
2847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * layout leads people to unfortunate misunderstandings about how the C language really works.)
2947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */
3047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
3147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt//*************************************************************************************************************
3247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// Incorporate mDNS.c functionality
3347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
3447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// We want to use the functionality provided by "mDNS.c",
3547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// except we'll sneak a peek at the packets before forwarding them to the normal mDNSCoreReceive() routine
3647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#define mDNSCoreReceive __MDNS__mDNSCoreReceive
3747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include "mDNS.c"
3847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#undef mDNSCoreReceive
3947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
4047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt//*************************************************************************************************************
4147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// Headers
4247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
4347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include <unistd.h>
4447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include <stdio.h>
4547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include <string.h>
4647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include <errno.h>
4747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include <sys/socket.h>
4847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include <netinet/in.h>
4947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include <netinet/in_systm.h>		// For n_long, required by <netinet/ip.h> below
5047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include <netinet/ip.h>				// For IPTOS_LOWDELAY etc.
5147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include <arpa/inet.h>
5247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include <signal.h>
5347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
5447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include "mDNSEmbeddedAPI.h"// Defines the interface to the mDNS core code
5547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include "mDNSPosix.h"    // Defines the specific types needed to run mDNS on this platform
5647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include "ExampleClientApp.h"
5747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
5847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt//*************************************************************************************************************
5947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// Globals
6047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
6147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstatic mDNS mDNSStorage;       // mDNS core uses this to store its globals
6247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstatic mDNS_PlatformSupport PlatformStorage;  // Stores this platform's globals
6347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#define RR_CACHE_SIZE 500
6447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstatic CacheEntity gRRCache[RR_CACHE_SIZE];
6547e4cebad7397422144bb03a21f3f7682c062c4aRobert GreenwaltmDNSexport const char ProgramName[] = "mDNSIdentify";
6647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
6747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstatic volatile int StopNow;	// 0 means running, 1 means stop because we got an answer, 2 means stop because of Ctrl-C
6847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstatic volatile int NumAnswers, NumAddr, NumAAAA, NumHINFO;
6947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstatic char hostname[MAX_ESCAPED_DOMAIN_NAME], hardware[256], software[256];
7047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstatic mDNSAddr lastsrc, hostaddr, target;
7147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstatic mDNSOpaque16 lastid, id;
7247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
7347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt//*************************************************************************************************************
7447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// Utilities
7547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
7647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
7747e4cebad7397422144bb03a21f3f7682c062c4aRobert GreenwaltmDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
7847e4cebad7397422144bb03a21f3f7682c062c4aRobert GreenwaltmDNSlocal mDNSu32 mprintf(const char *format, ...)
7947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
8047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	mDNSu32 length;
8147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	unsigned char buffer[512];
8247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	va_list ptr;
8347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	va_start(ptr,format);
8447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
8547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	va_end(ptr);
8647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	printf("%s", buffer);
8747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	return(length);
8847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
8947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
9047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt//*************************************************************************************************************
9147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// Main code
9247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
9347e4cebad7397422144bb03a21f3f7682c062c4aRobert GreenwaltmDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
9447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport,
9547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const mDNSInterfaceID InterfaceID)
9647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
9747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(void)dstaddr; // Unused
9847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	// Snag copy of header ID, then call through
9947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	lastid = msg->h.id;
10047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	lastsrc = *srcaddr;
10147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
10247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	// We *want* to allow off-net unicast responses here.
10347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	// For now, the simplest way to allow that is to pretend it was received via multicast so that mDNSCore doesn't reject the packet
10447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	__MDNS__mDNSCoreReceive(m, msg, end, srcaddr, srcport, &AllDNSLinkGroup_v4, dstport, InterfaceID);
10547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
10647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
10747e4cebad7397422144bb03a21f3f7682c062c4aRobert GreenwaltmDNSlocal void NameCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
10847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
10947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(void)m;		// Unused
11047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(void)question;	// Unused
11147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(void)AddRecord;// Unused
11247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (!id.NotAnInteger) id = lastid;
11347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (answer->rrtype == kDNSType_PTR || answer->rrtype == kDNSType_CNAME)
11447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		{
11547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		ConvertDomainNameToCString(&answer->rdata->u.name, hostname);
11647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		StopNow = 1;
11747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c);
11847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		}
11947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
12047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
12147e4cebad7397422144bb03a21f3f7682c062c4aRobert GreenwaltmDNSlocal void InfoCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
12247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
12347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(void)m;		// Unused
12447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(void)question;	// Unused
12547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(void)AddRecord;// Unused
12647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (answer->rrtype == kDNSType_A)
12747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		{
12847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (!id.NotAnInteger) id = lastid;
12947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		NumAnswers++;
13047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		NumAddr++;
13147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		mprintf("%##s %s %.4a\n", answer->name->c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv4);
13247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		hostaddr.type = mDNSAddrType_IPv4;	// Prefer v4 target to v6 target, for now
13347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		hostaddr.ip.v4 = answer->rdata->u.ipv4;
13447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		}
13547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	else if (answer->rrtype == kDNSType_AAAA)
13647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		{
13747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (!id.NotAnInteger) id = lastid;
13847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		NumAnswers++;
13947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		NumAAAA++;
14047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		mprintf("%##s %s %.16a\n", answer->name->c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv6);
14147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (!hostaddr.type)	// Prefer v4 target to v6 target, for now
14247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			{
14347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			hostaddr.type = mDNSAddrType_IPv6;
14447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			hostaddr.ip.v6 = answer->rdata->u.ipv6;
14547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			}
14647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		}
14747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	else if (answer->rrtype == kDNSType_HINFO)
14847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		{
14947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		mDNSu8 *p = answer->rdata->u.data;
15047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		strncpy(hardware, (char*)(p+1), p[0]);
15147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		hardware[p[0]] = 0;
15247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		p += 1 + p[0];
15347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		strncpy(software, (char*)(p+1), p[0]);
15447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		software[p[0]] = 0;
15547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		NumAnswers++;
15647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		NumHINFO++;
15747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		}
15847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
15947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	// If we've got everything we're looking for, don't need to wait any more
16047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (/*NumHINFO && */ (NumAddr || NumAAAA)) StopNow = 1;
16147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
16247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
16347e4cebad7397422144bb03a21f3f7682c062c4aRobert GreenwaltmDNSlocal void ServicesCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
16447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
16547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(void)m;		// Unused
16647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(void)question;	// Unused
16747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(void)AddRecord;// Unused
16847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	// Right now the mDNSCore targeted-query code is incomplete --
16947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	// it issues targeted queries, but accepts answers from anywhere
17047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	// For now, we'll just filter responses here so we don't get confused by responses from someone else
17147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (answer->rrtype == kDNSType_PTR && mDNSSameAddress(&lastsrc, &target))
17247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		{
17347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		NumAnswers++;
17447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c);
17547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		}
17647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
17747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
17847e4cebad7397422144bb03a21f3f7682c062c4aRobert GreenwaltmDNSlocal void WaitForAnswer(mDNS *const m, int seconds)
17947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
18047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	struct timeval end;
18147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	gettimeofday(&end, NULL);
18247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	end.tv_sec += seconds;
18347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	StopNow = 0;
18447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	NumAnswers = 0;
18547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	while (!StopNow)
18647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		{
18747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		int nfds = 0;
18847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		fd_set readfds;
18947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		struct timeval now, remain = end;
19047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		int result;
19147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
19247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		FD_ZERO(&readfds);
19347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		gettimeofday(&now, NULL);
19447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (remain.tv_usec < now.tv_usec) { remain.tv_usec += 1000000; remain.tv_sec--; }
19547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (remain.tv_sec < now.tv_sec)
19647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			{
19747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			if (!NumAnswers) printf("No response after %d seconds\n", seconds);
19847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			return;
19947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			}
20047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		remain.tv_usec -= now.tv_usec;
20147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		remain.tv_sec  -= now.tv_sec;
20247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		mDNSPosixGetFDSet(m, &nfds, &readfds, &remain);
20347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		result = select(nfds, &readfds, NULL, NULL, &remain);
20447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (result >= 0) mDNSPosixProcessFDSet(m, &readfds);
20547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		else if (errno != EINTR) StopNow = 2;
20647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		}
20747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
20847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
20947e4cebad7397422144bb03a21f3f7682c062c4aRobert GreenwaltmDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
21047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
21147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	lastsrc = zeroAddr;
21247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (qname) MakeDomainNameFromDNSNameString(&q->qname, qname);
21347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->InterfaceID      = mDNSInterface_Any;
21447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->Target           = target ? *target : zeroAddr;
21547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->TargetPort       = MulticastDNSPort;
21647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->TargetQID        = zeroID;
21747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->qtype            = qtype;
21847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->qclass           = kDNSClass_IN;
21947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->LongLived        = mDNSfalse;
22047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->ExpectUnique     = mDNSfalse;	// Don't want to stop after the first response packet
22147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->ForceMCast       = mDNStrue;		// Query via multicast, even for apparently uDNS names like 1.1.1.17.in-addr.arpa.
22247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->ReturnIntermed   = mDNStrue;
22347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->SuppressUnusable = mDNSfalse;
22447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->SearchListIndex  = 0;
22547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->AppendSearchDomains = 0;
22647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->RetryWithSearchDomains = mDNSfalse;
22747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->TimeoutQuestion  = 0;
22847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->WakeOnResolve    = 0;
22947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->qnameOrig        = mDNSNULL;
23047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->QuestionCallback = callback;
23147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	q->QuestionContext  = NULL;
23247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
23347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	//mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype));
23447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	return(mDNS_StartQuery(&mDNSStorage, q));
23547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
23647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
23747e4cebad7397422144bb03a21f3f7682c062c4aRobert GreenwaltmDNSlocal void DoOneQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
23847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
23947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	mStatus status = StartQuery(q, qname, qtype, target, callback);
24047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (status != mStatus_NoError)
24147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		StopNow = 2;
24247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	else
24347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		{
24447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		WaitForAnswer(&mDNSStorage, 4);
24547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		mDNS_StopQuery(&mDNSStorage, q);
24647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		}
24747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
24847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
24947e4cebad7397422144bb03a21f3f7682c062c4aRobert GreenwaltmDNSlocal int DoQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
25047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
25147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	DoOneQuery(q, qname, qtype, target, callback);
25247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (StopNow == 0 && NumAnswers == 0 && target && target->type)
25347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		{
25447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		mprintf("%##s %s Trying multicast\n", q->qname.c, DNSTypeName(q->qtype));
25547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		DoOneQuery(q, qname, qtype, NULL, callback);
25647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		}
25747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (StopNow == 0 && NumAnswers == 0)
25847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		mprintf("%##s %s *** No Answer ***\n", q->qname.c, DNSTypeName(q->qtype));
25947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	return(StopNow);
26047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
26147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
26247e4cebad7397422144bb03a21f3f7682c062c4aRobert GreenwaltmDNSlocal void HandleSIG(int signal)
26347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
26447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(void)signal;	// Unused
26547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	debugf("%s","");
26647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	debugf("HandleSIG");
26747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	StopNow = 2;
26847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
26947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
27047e4cebad7397422144bb03a21f3f7682c062c4aRobert GreenwaltmDNSexport int main(int argc, char **argv)
27147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
27247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
27347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	int this_arg = 1;
27447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	mStatus status;
27547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	struct in_addr s4;
27647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#if HAVE_IPV6
27747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	struct in6_addr s6;
27847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#endif
27947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	char buffer[256];
28047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	DNSQuestion q;
28147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
28247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (argc < 2) goto usage;
28347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
28447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	// Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog
28547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	mDNS_DebugMode = mDNStrue;
28647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
28747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt    // Initialise the mDNS core.
28847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	status = mDNS_Init(&mDNSStorage, &PlatformStorage,
28947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt    	gRRCache, RR_CACHE_SIZE,
29047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt    	mDNS_Init_DontAdvertiseLocalAddresses,
29147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt    	mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
29247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %d\n", (int)status); return(status); }
29347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
29447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	signal(SIGINT, HandleSIG);	// SIGINT is what you get for a Ctrl-C
29547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	signal(SIGTERM, HandleSIG);
29647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
29747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	while (this_arg < argc)
29847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		{
29947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		char *arg = argv[this_arg++];
30047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (this_arg > 2) printf("\n");
30147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
30247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		lastid = id = zeroID;
30347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		hostaddr = target = zeroAddr;
30447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		hostname[0] = hardware[0] = software[0] = 0;
30547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		NumAddr = NumAAAA = NumHINFO = 0;
30647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
30747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (inet_pton(AF_INET, arg, &s4) == 1)
30847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			{
30947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			mDNSu8 *p = (mDNSu8 *)&s4;
31047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			// Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
31147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p[3], p[2], p[1], p[0]);
31247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			printf("%s\n", buffer);
31347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			target.type = mDNSAddrType_IPv4;
31447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			target.ip.v4.NotAnInteger = s4.s_addr;
31547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback);
31647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			if (StopNow == 2) break;
31747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			}
31847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#if HAVE_IPV6
31947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		else if (inet_pton(AF_INET6, arg, &s6) == 1)
32047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			{
32147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			int i;
32247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			mDNSu8 *p = (mDNSu8 *)&s6;
32347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			for (i = 0; i < 16; i++)
32447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				{
32547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				static const char hexValues[] = "0123456789ABCDEF";
32647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				buffer[i * 4    ] = hexValues[p[15-i] & 0x0F];
32747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				buffer[i * 4 + 1] = '.';
32847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				buffer[i * 4 + 2] = hexValues[p[15-i] >> 4];
32947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				buffer[i * 4 + 3] = '.';
33047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				}
33147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa.");
33247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			target.type = mDNSAddrType_IPv6;
33347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			mDNSPlatformMemCopy(&target.ip.v6, &s6, sizeof(target.ip.v6));
33447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback);
33547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			if (StopNow == 2) break;
33647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			}
33747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#endif
33847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		else {
33947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			if (strlen(arg) >= sizeof(hostname)) {
34047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				fprintf(stderr, "hostname must be < %d characters\n", (int)sizeof(hostname));
34147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				goto usage;
34247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			}
34347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			strcpy(hostname, arg);
34447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		}
34547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
34647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		// Now we have the host name; get its A, AAAA, and HINFO
34747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (hostname[0]) DoQuery(&q, hostname, kDNSQType_ANY, &target, InfoCallback);
34847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (StopNow == 2) break;
34947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
35047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (hardware[0] || software[0])
35147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			{
35247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			printf("HINFO Hardware: %s\n", hardware);
35347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			printf("HINFO Software: %s\n", software);
35447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			}
35547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		else if (NumAnswers) printf("%s has no HINFO record\n", hostname);
35647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		else printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n");
35747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
35847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (NumAnswers)
35947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			{
36047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			// Because of the way we use lastsrc in ServicesCallback, we need to clear the cache to make sure we're getting fresh answers
36147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			mDNS *const m = &mDNSStorage;
36247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			mDNSu32 slot;
36347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			CacheGroup *cg;
36447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			CacheRecord *rr;
36547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			FORALL_CACHERECORDS(slot, cg, rr) mDNS_PurgeCacheResourceRecord(m, rr);
36647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			if (target.type == 0) target = hostaddr;		// Make sure the services query is targeted
36747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			DoQuery(&q, "_services._dns-sd._udp.local.", kDNSType_PTR, &target, ServicesCallback);
36847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			if (StopNow == 2) break;
36947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			}
37047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		}
37147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
37247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	mDNS_Close(&mDNSStorage);
37347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	return(0);
37447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
37547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltusage:
37647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	fprintf(stderr, "Usage: %s <dot-local hostname> or <IPv4 address> or <IPv6 address> ...\n", progname);
37747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	return(-1);
37847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
379