Home | History | Annotate | Download | only in mDNSPosix
      1 /* -*- Mode: C; tab-width: 4 -*-
      2  *
      3  * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  *
     17  * Formatting notes:
     18  * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
     19  * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
     20  * but for the sake of brevity here I will say just this: Curly braces are not syntactially
     21  * part of an "if" statement; they are the beginning and ending markers of a compound statement;
     22  * therefore common sense dictates that if they are part of a compound statement then they
     23  * should be indented to the same level as everything else in that compound statement.
     24  * Indenting curly braces at the same level as the "if" implies that curly braces are
     25  * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
     26  * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
     27  * understand why variable y is not of type "char*" just proves the point that poor code
     28  * layout leads people to unfortunate misunderstandings about how the C language really works.)
     29  */
     30 
     31 //*************************************************************************************************************
     32 // Incorporate mDNS.c functionality
     33 
     34 // We want to use the functionality provided by "mDNS.c",
     35 // except we'll sneak a peek at the packets before forwarding them to the normal mDNSCoreReceive() routine
     36 #define mDNSCoreReceive __MDNS__mDNSCoreReceive
     37 #include "mDNS.c"
     38 #undef mDNSCoreReceive
     39 
     40 //*************************************************************************************************************
     41 // Headers
     42 
     43 #include <unistd.h>
     44 #include <stdio.h>
     45 #include <string.h>
     46 #include <errno.h>
     47 #include <sys/socket.h>
     48 #include <netinet/in.h>
     49 #include <netinet/in_systm.h>		// For n_long, required by <netinet/ip.h> below
     50 #include <netinet/ip.h>				// For IPTOS_LOWDELAY etc.
     51 #include <arpa/inet.h>
     52 #include <signal.h>
     53 
     54 #include "mDNSEmbeddedAPI.h"// Defines the interface to the mDNS core code
     55 #include "mDNSPosix.h"    // Defines the specific types needed to run mDNS on this platform
     56 #include "ExampleClientApp.h"
     57 
     58 //*************************************************************************************************************
     59 // Globals
     60 
     61 static mDNS mDNSStorage;       // mDNS core uses this to store its globals
     62 static mDNS_PlatformSupport PlatformStorage;  // Stores this platform's globals
     63 #define RR_CACHE_SIZE 500
     64 static CacheEntity gRRCache[RR_CACHE_SIZE];
     65 mDNSexport const char ProgramName[] = "mDNSIdentify";
     66 
     67 static volatile int StopNow;	// 0 means running, 1 means stop because we got an answer, 2 means stop because of Ctrl-C
     68 static volatile int NumAnswers, NumAddr, NumAAAA, NumHINFO;
     69 static char hostname[MAX_ESCAPED_DOMAIN_NAME], hardware[256], software[256];
     70 static mDNSAddr lastsrc, hostaddr, target;
     71 static mDNSOpaque16 lastid, id;
     72 
     73 //*************************************************************************************************************
     74 // Utilities
     75 
     76 // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
     77 mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
     78 mDNSlocal mDNSu32 mprintf(const char *format, ...)
     79 	{
     80 	mDNSu32 length;
     81 	unsigned char buffer[512];
     82 	va_list ptr;
     83 	va_start(ptr,format);
     84 	length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
     85 	va_end(ptr);
     86 	printf("%s", buffer);
     87 	return(length);
     88 	}
     89 
     90 //*************************************************************************************************************
     91 // Main code
     92 
     93 mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
     94 	const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport,
     95 	const mDNSInterfaceID InterfaceID)
     96 	{
     97 	(void)dstaddr; // Unused
     98 	// Snag copy of header ID, then call through
     99 	lastid = msg->h.id;
    100 	lastsrc = *srcaddr;
    101 
    102 	// We *want* to allow off-net unicast responses here.
    103 	// For now, the simplest way to allow that is to pretend it was received via multicast so that mDNSCore doesn't reject the packet
    104 	__MDNS__mDNSCoreReceive(m, msg, end, srcaddr, srcport, &AllDNSLinkGroup_v4, dstport, InterfaceID);
    105 	}
    106 
    107 mDNSlocal void NameCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
    108 	{
    109 	(void)m;		// Unused
    110 	(void)question;	// Unused
    111 	(void)AddRecord;// Unused
    112 	if (!id.NotAnInteger) id = lastid;
    113 	if (answer->rrtype == kDNSType_PTR || answer->rrtype == kDNSType_CNAME)
    114 		{
    115 		ConvertDomainNameToCString(&answer->rdata->u.name, hostname);
    116 		StopNow = 1;
    117 		mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c);
    118 		}
    119 	}
    120 
    121 mDNSlocal void InfoCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
    122 	{
    123 	(void)m;		// Unused
    124 	(void)question;	// Unused
    125 	(void)AddRecord;// Unused
    126 	if (answer->rrtype == kDNSType_A)
    127 		{
    128 		if (!id.NotAnInteger) id = lastid;
    129 		NumAnswers++;
    130 		NumAddr++;
    131 		mprintf("%##s %s %.4a\n", answer->name->c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv4);
    132 		hostaddr.type = mDNSAddrType_IPv4;	// Prefer v4 target to v6 target, for now
    133 		hostaddr.ip.v4 = answer->rdata->u.ipv4;
    134 		}
    135 	else if (answer->rrtype == kDNSType_AAAA)
    136 		{
    137 		if (!id.NotAnInteger) id = lastid;
    138 		NumAnswers++;
    139 		NumAAAA++;
    140 		mprintf("%##s %s %.16a\n", answer->name->c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv6);
    141 		if (!hostaddr.type)	// Prefer v4 target to v6 target, for now
    142 			{
    143 			hostaddr.type = mDNSAddrType_IPv6;
    144 			hostaddr.ip.v6 = answer->rdata->u.ipv6;
    145 			}
    146 		}
    147 	else if (answer->rrtype == kDNSType_HINFO)
    148 		{
    149 		mDNSu8 *p = answer->rdata->u.data;
    150 		strncpy(hardware, (char*)(p+1), p[0]);
    151 		hardware[p[0]] = 0;
    152 		p += 1 + p[0];
    153 		strncpy(software, (char*)(p+1), p[0]);
    154 		software[p[0]] = 0;
    155 		NumAnswers++;
    156 		NumHINFO++;
    157 		}
    158 
    159 	// If we've got everything we're looking for, don't need to wait any more
    160 	if (/*NumHINFO && */ (NumAddr || NumAAAA)) StopNow = 1;
    161 	}
    162 
    163 mDNSlocal void ServicesCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
    164 	{
    165 	(void)m;		// Unused
    166 	(void)question;	// Unused
    167 	(void)AddRecord;// Unused
    168 	// Right now the mDNSCore targeted-query code is incomplete --
    169 	// it issues targeted queries, but accepts answers from anywhere
    170 	// For now, we'll just filter responses here so we don't get confused by responses from someone else
    171 	if (answer->rrtype == kDNSType_PTR && mDNSSameAddress(&lastsrc, &target))
    172 		{
    173 		NumAnswers++;
    174 		mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c);
    175 		}
    176 	}
    177 
    178 mDNSlocal void WaitForAnswer(mDNS *const m, int seconds)
    179 	{
    180 	struct timeval end;
    181 	gettimeofday(&end, NULL);
    182 	end.tv_sec += seconds;
    183 	StopNow = 0;
    184 	NumAnswers = 0;
    185 	while (!StopNow)
    186 		{
    187 		int nfds = 0;
    188 		fd_set readfds;
    189 		struct timeval now, remain = end;
    190 		int result;
    191 
    192 		FD_ZERO(&readfds);
    193 		gettimeofday(&now, NULL);
    194 		if (remain.tv_usec < now.tv_usec) { remain.tv_usec += 1000000; remain.tv_sec--; }
    195 		if (remain.tv_sec < now.tv_sec)
    196 			{
    197 			if (!NumAnswers) printf("No response after %d seconds\n", seconds);
    198 			return;
    199 			}
    200 		remain.tv_usec -= now.tv_usec;
    201 		remain.tv_sec  -= now.tv_sec;
    202 		mDNSPosixGetFDSet(m, &nfds, &readfds, &remain);
    203 		result = select(nfds, &readfds, NULL, NULL, &remain);
    204 		if (result >= 0) mDNSPosixProcessFDSet(m, &readfds);
    205 		else if (errno != EINTR) StopNow = 2;
    206 		}
    207 	}
    208 
    209 mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
    210 	{
    211 	lastsrc = zeroAddr;
    212 	if (qname) MakeDomainNameFromDNSNameString(&q->qname, qname);
    213 	q->InterfaceID      = mDNSInterface_Any;
    214 	q->Target           = target ? *target : zeroAddr;
    215 	q->TargetPort       = MulticastDNSPort;
    216 	q->TargetQID        = zeroID;
    217 	q->qtype            = qtype;
    218 	q->qclass           = kDNSClass_IN;
    219 	q->LongLived        = mDNSfalse;
    220 	q->ExpectUnique     = mDNSfalse;	// Don't want to stop after the first response packet
    221 	q->ForceMCast       = mDNStrue;		// Query via multicast, even for apparently uDNS names like 1.1.1.17.in-addr.arpa.
    222 	q->ReturnIntermed   = mDNStrue;
    223 	q->SuppressUnusable = mDNSfalse;
    224 	q->SearchListIndex  = 0;
    225 	q->AppendSearchDomains = 0;
    226 	q->RetryWithSearchDomains = mDNSfalse;
    227 	q->TimeoutQuestion  = 0;
    228 	q->WakeOnResolve    = 0;
    229 	q->qnameOrig        = mDNSNULL;
    230 	q->QuestionCallback = callback;
    231 	q->QuestionContext  = NULL;
    232 
    233 	//mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype));
    234 	return(mDNS_StartQuery(&mDNSStorage, q));
    235 	}
    236 
    237 mDNSlocal void DoOneQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
    238 	{
    239 	mStatus status = StartQuery(q, qname, qtype, target, callback);
    240 	if (status != mStatus_NoError)
    241 		StopNow = 2;
    242 	else
    243 		{
    244 		WaitForAnswer(&mDNSStorage, 4);
    245 		mDNS_StopQuery(&mDNSStorage, q);
    246 		}
    247 	}
    248 
    249 mDNSlocal int DoQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
    250 	{
    251 	DoOneQuery(q, qname, qtype, target, callback);
    252 	if (StopNow == 0 && NumAnswers == 0 && target && target->type)
    253 		{
    254 		mprintf("%##s %s Trying multicast\n", q->qname.c, DNSTypeName(q->qtype));
    255 		DoOneQuery(q, qname, qtype, NULL, callback);
    256 		}
    257 	if (StopNow == 0 && NumAnswers == 0)
    258 		mprintf("%##s %s *** No Answer ***\n", q->qname.c, DNSTypeName(q->qtype));
    259 	return(StopNow);
    260 	}
    261 
    262 mDNSlocal void HandleSIG(int signal)
    263 	{
    264 	(void)signal;	// Unused
    265 	debugf("%s","");
    266 	debugf("HandleSIG");
    267 	StopNow = 2;
    268 	}
    269 
    270 mDNSexport int main(int argc, char **argv)
    271 	{
    272 	const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
    273 	int this_arg = 1;
    274 	mStatus status;
    275 	struct in_addr s4;
    276 #if HAVE_IPV6
    277 	struct in6_addr s6;
    278 #endif
    279 	char buffer[256];
    280 	DNSQuestion q;
    281 
    282 	if (argc < 2) goto usage;
    283 
    284 	// Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog
    285 	mDNS_DebugMode = mDNStrue;
    286 
    287     // Initialise the mDNS core.
    288 	status = mDNS_Init(&mDNSStorage, &PlatformStorage,
    289     	gRRCache, RR_CACHE_SIZE,
    290     	mDNS_Init_DontAdvertiseLocalAddresses,
    291     	mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
    292 	if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %d\n", (int)status); return(status); }
    293 
    294 	signal(SIGINT, HandleSIG);	// SIGINT is what you get for a Ctrl-C
    295 	signal(SIGTERM, HandleSIG);
    296 
    297 	while (this_arg < argc)
    298 		{
    299 		char *arg = argv[this_arg++];
    300 		if (this_arg > 2) printf("\n");
    301 
    302 		lastid = id = zeroID;
    303 		hostaddr = target = zeroAddr;
    304 		hostname[0] = hardware[0] = software[0] = 0;
    305 		NumAddr = NumAAAA = NumHINFO = 0;
    306 
    307 		if (inet_pton(AF_INET, arg, &s4) == 1)
    308 			{
    309 			mDNSu8 *p = (mDNSu8 *)&s4;
    310 			// Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
    311 			mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p[3], p[2], p[1], p[0]);
    312 			printf("%s\n", buffer);
    313 			target.type = mDNSAddrType_IPv4;
    314 			target.ip.v4.NotAnInteger = s4.s_addr;
    315 			DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback);
    316 			if (StopNow == 2) break;
    317 			}
    318 #if HAVE_IPV6
    319 		else if (inet_pton(AF_INET6, arg, &s6) == 1)
    320 			{
    321 			int i;
    322 			mDNSu8 *p = (mDNSu8 *)&s6;
    323 			for (i = 0; i < 16; i++)
    324 				{
    325 				static const char hexValues[] = "0123456789ABCDEF";
    326 				buffer[i * 4    ] = hexValues[p[15-i] & 0x0F];
    327 				buffer[i * 4 + 1] = '.';
    328 				buffer[i * 4 + 2] = hexValues[p[15-i] >> 4];
    329 				buffer[i * 4 + 3] = '.';
    330 				}
    331 			mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa.");
    332 			target.type = mDNSAddrType_IPv6;
    333 			mDNSPlatformMemCopy(&target.ip.v6, &s6, sizeof(target.ip.v6));
    334 			DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback);
    335 			if (StopNow == 2) break;
    336 			}
    337 #endif
    338 		else {
    339 			if (strlen(arg) >= sizeof(hostname)) {
    340 				fprintf(stderr, "hostname must be < %d characters\n", (int)sizeof(hostname));
    341 				goto usage;
    342 			}
    343 			strcpy(hostname, arg);
    344 		}
    345 
    346 		// Now we have the host name; get its A, AAAA, and HINFO
    347 		if (hostname[0]) DoQuery(&q, hostname, kDNSQType_ANY, &target, InfoCallback);
    348 		if (StopNow == 2) break;
    349 
    350 		if (hardware[0] || software[0])
    351 			{
    352 			printf("HINFO Hardware: %s\n", hardware);
    353 			printf("HINFO Software: %s\n", software);
    354 			}
    355 		else if (NumAnswers) printf("%s has no HINFO record\n", hostname);
    356 		else printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n");
    357 
    358 		if (NumAnswers)
    359 			{
    360 			// Because of the way we use lastsrc in ServicesCallback, we need to clear the cache to make sure we're getting fresh answers
    361 			mDNS *const m = &mDNSStorage;
    362 			mDNSu32 slot;
    363 			CacheGroup *cg;
    364 			CacheRecord *rr;
    365 			FORALL_CACHERECORDS(slot, cg, rr) mDNS_PurgeCacheResourceRecord(m, rr);
    366 			if (target.type == 0) target = hostaddr;		// Make sure the services query is targeted
    367 			DoQuery(&q, "_services._dns-sd._udp.local.", kDNSType_PTR, &target, ServicesCallback);
    368 			if (StopNow == 2) break;
    369 			}
    370 		}
    371 
    372 	mDNS_Close(&mDNSStorage);
    373 	return(0);
    374 
    375 usage:
    376 	fprintf(stderr, "Usage: %s <dot-local hostname> or <IPv4 address> or <IPv6 address> ...\n", progname);
    377 	return(-1);
    378 	}
    379