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