xref: /netbsd/tests/lib/libc/net/h_dns_server.c (revision a5c183f0)
1*a5c183f0Sgson /*	$NetBSD: h_dns_server.c,v 1.4 2014/03/29 16:10:54 gson Exp $	*/
220882d81Sgson 
320882d81Sgson /*-
420882d81Sgson  * Copyright (c) 2013 The NetBSD Foundation, Inc.
520882d81Sgson  * All rights reserved.
620882d81Sgson  *
720882d81Sgson  * This code is derived from software contributed to The NetBSD Foundation
820882d81Sgson  * by Andreas Gustafsson.
920882d81Sgson  *
1020882d81Sgson  * Redistribution and use in source and binary forms, with or without
1120882d81Sgson  * modification, are permitted provided that the following conditions
1220882d81Sgson  * are met:
1320882d81Sgson  * 1. Redistributions of source code must retain the above copyright
1420882d81Sgson  *    notice, this list of conditions and the following disclaimer.
1520882d81Sgson  * 2. Redistributions in binary form must reproduce the above copyright
1620882d81Sgson  *    notice, this list of conditions and the following disclaimer in the
1720882d81Sgson  *    documentation and/or other materials provided with the distribution.
1820882d81Sgson  *
1920882d81Sgson  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2020882d81Sgson  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2120882d81Sgson  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2220882d81Sgson  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2320882d81Sgson  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2420882d81Sgson  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2520882d81Sgson  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2620882d81Sgson  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2720882d81Sgson  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2820882d81Sgson  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2920882d81Sgson  * POSSIBILITY OF SUCH DAMAGE.
3020882d81Sgson  */
3120882d81Sgson 
3220882d81Sgson /*
3320882d81Sgson  * A minimal DNS server capable of providing canned answers to the
3420882d81Sgson  * specific queries issued by t_hostent.sh and nothing more.
3520882d81Sgson  */
3620882d81Sgson 
3720882d81Sgson #include <sys/cdefs.h>
38*a5c183f0Sgson __RCSID("$NetBSD: h_dns_server.c,v 1.4 2014/03/29 16:10:54 gson Exp $");
3920882d81Sgson 
4020882d81Sgson #include <ctype.h>
4120882d81Sgson #include <err.h>
4220882d81Sgson #include <errno.h>
4320882d81Sgson #include <fcntl.h>
4420882d81Sgson #include <memory.h>
4520882d81Sgson #include <stdio.h>
4620882d81Sgson #include <stdlib.h>
4720882d81Sgson #include <unistd.h>
4820882d81Sgson 
4920882d81Sgson #include <sys/socket.h>
5020882d81Sgson 
5120882d81Sgson #include <netinet/in.h>
5220882d81Sgson #include <netinet6/in6.h>
5320882d81Sgson 
5420882d81Sgson union sockaddr_either {
5520882d81Sgson 	struct sockaddr s;
5620882d81Sgson 	struct sockaddr_in sin;
5720882d81Sgson 	struct sockaddr_in6 sin6;
5820882d81Sgson };
5920882d81Sgson 
6081f6f38eSchristos #ifdef DEBUG
6181f6f38eSchristos #define DPRINTF(...)	fprintf(stderr, __VA_ARGS__)
6281f6f38eSchristos #else
6381f6f38eSchristos #define DPRINTF(...)
6481f6f38eSchristos #endif
6581f6f38eSchristos 
6620882d81Sgson /* A DNS question and its corresponding answer */
6720882d81Sgson 
6820882d81Sgson struct dns_data {
6920882d81Sgson 	size_t qname_size;
7020882d81Sgson 	const char *qname; /* Wire-encode question name */
7120882d81Sgson 	int qtype;
7220882d81Sgson 	size_t answer_size;
7320882d81Sgson 	const char *answer; /* One wire-encoded answer RDATA */
7420882d81Sgson };
7520882d81Sgson 
7620882d81Sgson /* Convert C string constant to length + data pair */
7720882d81Sgson #define STR_DATA(s) sizeof(s) - 1, s
7820882d81Sgson 
7920882d81Sgson /* Canned DNS queestion-answer pairs */
8020882d81Sgson struct dns_data data[] = {
8120882d81Sgson 	/* Forward mappings */
8220882d81Sgson 	/* localhost IN A -> 127.0.0.1 */
8320882d81Sgson 	{ STR_DATA("\011localhost\000"), 1,
8420882d81Sgson 	  STR_DATA("\177\000\000\001") },
8520882d81Sgson 	/* localhost IN AAAA -> ::1 */
8620882d81Sgson 	{ STR_DATA("\011localhost\000"), 28,
8720882d81Sgson 	  STR_DATA("\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001") },
8820882d81Sgson 	/* sixthavenue.astron.com IN A -> 38.117.134.16 */
8920882d81Sgson 	{ STR_DATA("\013sixthavenue\006astron\003com\000"), 1,
9020882d81Sgson 	  STR_DATA("\046\165\206\020") },
9120882d81Sgson 	/* sixthavenue.astron.com IN AAAA -> 2620:106:3003:1f00:3e4a:92ff:fef4:e180 */
9220882d81Sgson 	{ STR_DATA("\013sixthavenue\006astron\003com\000"), 28,
9320882d81Sgson 	  STR_DATA("\x26\x20\x01\x06\x30\x03\x1f\x00\x3e\x4a\x92\xff\xfe\xf4\xe1\x80") },
9420882d81Sgson 	/* Reverse mappings */
9520882d81Sgson 	{ STR_DATA("\0011\0010\0010\003127\007in-addr\004arpa\000"), 12,
9620882d81Sgson 	  STR_DATA("\011localhost\000") },
9720882d81Sgson 	{ STR_DATA("\0011\0010\0010\0010\0010\0010\0010\0010"
9820882d81Sgson 		   "\0010\0010\0010\0010\0010\0010\0010\0010"
9920882d81Sgson 		   "\0010\0010\0010\0010\0010\0010\0010\0010"
10020882d81Sgson 		   "\0010\0010\0010\0010\0010\0010\0010\0010"
10120882d81Sgson 		   "\003ip6\004arpa\000"), 12,
10220882d81Sgson 	  STR_DATA("\011localhost\000") },
10320882d81Sgson 	{ STR_DATA("\00216\003134\003117\00238"
10420882d81Sgson 		   "\007in-addr\004arpa\000"), 12,
10520882d81Sgson 	  STR_DATA("\013sixthavenue\006astron\003com\000") },
10620882d81Sgson 	{ STR_DATA("\0010\0018\0011\001e\0014\001f\001e\001f"
10720882d81Sgson 		   "\001f\001f\0012\0019\001a\0014\001e\0013"
10820882d81Sgson 		   "\0010\0010\001f\0011\0013\0010\0010\0013"
10920882d81Sgson 		   "\0016\0010\0011\0010\0010\0012\0016\0012"
11020882d81Sgson 		   "\003ip6\004arpa\000"), 12,
11120882d81Sgson 	  STR_DATA("\013sixthavenue\006astron\003com\000") },
11220882d81Sgson 	/* End marker */
11320882d81Sgson 	{ STR_DATA(""), 0, STR_DATA("") }
11420882d81Sgson };
11520882d81Sgson 
11620882d81Sgson /*
11720882d81Sgson  * Compare two DNS names for equality.	If equal, return their
11820882d81Sgson  * length, and if not, return zero.  Does not handle compression.
11920882d81Sgson  */
12020882d81Sgson static int
name_eq(const unsigned char * a,const unsigned char * b)12120882d81Sgson name_eq(const unsigned char *a, const unsigned char *b) {
12220882d81Sgson 	const unsigned char *a_save = a;
12320882d81Sgson 	for (;;) {
12420882d81Sgson 		int i;
12520882d81Sgson 		int lena = *a++;
12620882d81Sgson 		int lenb = *b++;
12720882d81Sgson 		if (lena != lenb)
12820882d81Sgson 			return 0;
12920882d81Sgson 		if (lena == 0)
13020882d81Sgson 			return a - a_save;
13120882d81Sgson 		for (i = 0; i < lena; i++)
13220882d81Sgson 			if (tolower(a[i]) != tolower(b[i]))
13320882d81Sgson 				return 0;
13420882d81Sgson 		a += lena;
13520882d81Sgson 		b += lena;
13620882d81Sgson 	}
13720882d81Sgson }
13820882d81Sgson 
13981f6f38eSchristos #ifdef DEBUG
14081f6f38eSchristos static char *
name2str(const void * v,char * buf,size_t buflen)14181f6f38eSchristos name2str(const void *v, char *buf, size_t buflen) {
14281f6f38eSchristos 	const unsigned char *a = v;
14381f6f38eSchristos 	char *b = buf;
14481f6f38eSchristos 	char *eb = buf + buflen;
14581f6f38eSchristos 
14681f6f38eSchristos #define ADDC(c) do { \
14781f6f38eSchristos 		if (b < eb) \
14881f6f38eSchristos 			*b++ = c; \
14981f6f38eSchristos 		else \
15081f6f38eSchristos 			return NULL; \
15181f6f38eSchristos 	} while (/*CONSTCOND*/0)
15281f6f38eSchristos 	for (int did = 0;; did++) {
15381f6f38eSchristos 		int lena = *a++;
15481f6f38eSchristos 		if (lena == 0) {
15581f6f38eSchristos 			ADDC('\0');
15681f6f38eSchristos 			return buf;
15781f6f38eSchristos 		}
15881f6f38eSchristos 		if (did)
15981f6f38eSchristos 			ADDC('.');
16081f6f38eSchristos 		for (int i = 0; i < lena; i++)
16181f6f38eSchristos 			ADDC(a[i]);
16281f6f38eSchristos 		a += lena;
16381f6f38eSchristos 	}
16481f6f38eSchristos }
16581f6f38eSchristos #endif
16681f6f38eSchristos 
main(int argc,char ** argv)16720882d81Sgson int main(int argc, char **argv) {
16820882d81Sgson 	int s, r, protocol;
16920882d81Sgson 	union sockaddr_either saddr;
17020882d81Sgson 	struct dns_data *dp;
17120882d81Sgson 	unsigned char *p;
17220882d81Sgson 	char pidfile_name[40];
17320882d81Sgson 	FILE *f;
17420882d81Sgson 	int one = 1;
17581f6f38eSchristos #ifdef DEBUG
17681f6f38eSchristos 	char buf1[1024], buf2[1024];
17781f6f38eSchristos #endif
17820882d81Sgson 
17920882d81Sgson 	if (argc < 2 || ((protocol = argv[1][0]) != '4' && protocol != '6'))
18020882d81Sgson 		errx(1, "usage: dns_server 4 | 6");
18120882d81Sgson 	s = socket(protocol == '4' ? PF_INET : PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
18220882d81Sgson 	if (s < 0)
18320882d81Sgson 		err(1, "socket");
18420882d81Sgson 	if (protocol == '4') {
18520882d81Sgson 		memset(&saddr.sin, 0, sizeof(saddr.sin));
18620882d81Sgson 		saddr.sin.sin_family = AF_INET;
18720882d81Sgson 		saddr.sin.sin_len = sizeof(saddr.sin);
18820882d81Sgson 		saddr.sin.sin_port = htons(53);
18920882d81Sgson 		saddr.sin.sin_addr.s_addr = INADDR_ANY;
19020882d81Sgson 	} else {
19120882d81Sgson 		static struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT;
19220882d81Sgson 		memset(&saddr.sin6, 0, sizeof(saddr.sin6));
19320882d81Sgson 		saddr.sin6.sin6_family = AF_INET6;
19420882d81Sgson 		saddr.sin6.sin6_len = sizeof(saddr.sin6);
19520882d81Sgson 		saddr.sin6.sin6_port = htons(53);
19620882d81Sgson 		saddr.sin6.sin6_addr = loopback;
19720882d81Sgson 	}
19820882d81Sgson 
19920882d81Sgson 	r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
20020882d81Sgson 	if (r < 0)
20120882d81Sgson 		err(1, "setsockopt");
20220882d81Sgson 
20320882d81Sgson 	r = bind(s,
20420882d81Sgson 		 (struct sockaddr *) &saddr,
20520882d81Sgson 		 protocol == '4' ? sizeof(struct sockaddr_in) :
20620882d81Sgson 				   sizeof(struct sockaddr_in6));
20720882d81Sgson 	if (r < 0)
20820882d81Sgson 		err(1, "bind");
20920882d81Sgson 
21020882d81Sgson 	snprintf(pidfile_name, sizeof pidfile_name,
21120882d81Sgson 		 "dns_server_%c.pid", protocol);
21220882d81Sgson 	f = fopen(pidfile_name, "w");
21320882d81Sgson 	fprintf(f, "%d", getpid());
21420882d81Sgson 	fclose(f);
21581f6f38eSchristos #ifdef DEBUG
216*a5c183f0Sgson 	daemon(0, 1);
21781f6f38eSchristos #else
218*a5c183f0Sgson 	daemon(0, 0);
21981f6f38eSchristos #endif
22020882d81Sgson 
22120882d81Sgson 	for (;;) {
22220882d81Sgson 		unsigned char buf[512];
22320882d81Sgson 		union sockaddr_either from;
22420882d81Sgson 		ssize_t nrecv, nsent;
22520882d81Sgson 		socklen_t fromlen =
22620882d81Sgson 			protocol == '4' ? sizeof(struct sockaddr_in) :
22720882d81Sgson 					  sizeof(struct sockaddr_in6);
22820882d81Sgson 		memset(buf, 0, sizeof buf);
22920882d81Sgson 		nrecv = recvfrom(s, buf, sizeof buf, 0, &from.s, &fromlen);
23020882d81Sgson 		if (nrecv < 0)
23120882d81Sgson 			err(1, "recvfrom");
23281f6f38eSchristos 		if (nrecv < 12) {
23381f6f38eSchristos 			DPRINTF("Too short %zd\n", nrecv);
23481f6f38eSchristos 			continue;
23581f6f38eSchristos 		}
23681f6f38eSchristos 		if ((buf[2] & 0x80) != 0) {
23781f6f38eSchristos 			DPRINTF("Not a query 0x%x\n", buf[2]);
23881f6f38eSchristos 			continue;
23981f6f38eSchristos 		}
24081f6f38eSchristos 		if (!(buf[4] == 0 && buf[5] == 1)) {
24181f6f38eSchristos 			DPRINTF("QCOUNT is not 1 0x%x 0x%x\n", buf[4], buf[5]);
24220882d81Sgson 			continue; /* QDCOUNT is not 1 */
24381f6f38eSchristos 		}
24420882d81Sgson 
24520882d81Sgson 		for (dp = data; dp->qname_size != 0; dp++) {
24620882d81Sgson 			int qtype, qclass;
24720882d81Sgson 			p = buf + 12; /* Point to QNAME */
24820882d81Sgson 			int n = name_eq(p, (const unsigned char *) dp->qname);
24981f6f38eSchristos 			if (n == 0) {
25081f6f38eSchristos 				DPRINTF("no match name %s != %s\n",
25181f6f38eSchristos 				    name2str(p, buf1, sizeof(buf1)),
25281f6f38eSchristos 				    name2str(dp->qname, buf2, sizeof(buf2)));
25320882d81Sgson 				continue; /* Name does not match */
25481f6f38eSchristos 			}
25581f6f38eSchristos 			DPRINTF("match name %s\n",
25681f6f38eSchristos 			    name2str(p, buf1, sizeof(buf1)));
25720882d81Sgson 			p += n; /* Skip QNAME */
25820882d81Sgson 			qtype = *p++ << 8;
25920882d81Sgson 			qtype |= *p++;
26081f6f38eSchristos 			if (qtype != dp->qtype) {
26181f6f38eSchristos 				DPRINTF("no match name 0x%x != 0x%x\n",
26281f6f38eSchristos 				    qtype, dp->qtype);
26320882d81Sgson 				continue;
26481f6f38eSchristos 			}
26581f6f38eSchristos 			DPRINTF("match type 0x%x\n", qtype);
26620882d81Sgson 			qclass = *p++ << 8;
26720882d81Sgson 			qclass |= *p++;
26881f6f38eSchristos 			if (qclass != 1) { /* IN */
26981f6f38eSchristos 				DPRINTF("no match class %d != 1\n", qclass);
27020882d81Sgson 				continue;
27181f6f38eSchristos 			}
27281f6f38eSchristos 			DPRINTF("match class %d\n", qclass);
27320882d81Sgson 			goto found;
27420882d81Sgson 		}
27520882d81Sgson 		continue;
27620882d81Sgson 	found:
27720882d81Sgson 		buf[2] |= 0x80; /* QR */
27820882d81Sgson 		buf[3] |= 0x80; /* RA */
27920882d81Sgson 		memset(buf + 6, 0, 6); /* Clear ANCOUNT, NSCOUNT, ARCOUNT */
28020882d81Sgson 		buf[7] = 1; /* ANCOUNT */
28120882d81Sgson 		memcpy(p, dp->qname, dp->qname_size);
28220882d81Sgson 		p += dp->qname_size;
28320882d81Sgson 		*p++ = dp->qtype >> 8;
28420882d81Sgson 		*p++ = dp->qtype & 0xFF;
28520882d81Sgson 		*p++ = 0;
28620882d81Sgson 		*p++ = 1; /* IN */
28720882d81Sgson 		memset(p, 0, 4); /* TTL = 0 */
28820882d81Sgson 		p += 4;
28920882d81Sgson 		*p++ = 0;		/* RDLENGTH MSB */
29020882d81Sgson 		*p++ = dp->answer_size;	/* RDLENGTH LSB */
29120882d81Sgson 		memcpy(p, dp->answer, dp->answer_size);
29220882d81Sgson 		p += dp->answer_size;
29320882d81Sgson 		nsent = sendto(s, buf, p - buf, 0, &from.s, fromlen);
29481f6f38eSchristos 		DPRINTF("sent %zd\n", nsent);
29520882d81Sgson 		if (nsent != p - buf)
29620882d81Sgson 			warn("sendto");
29720882d81Sgson 	}
29820882d81Sgson }
299