1 /* $NetBSD: h_dns_server.c,v 1.4 2014/03/29 16:10:54 gson Exp $ */
4 * Copyright (c) 2013 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Andreas Gustafsson.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * A minimal DNS server capable of providing canned answers to the
34 * specific queries issued by t_hostent.sh and nothing more.
37 #include <sys/cdefs.h>
38 __RCSID("$NetBSD: h_dns_server.c,v 1.4 2014/03/29 16:10:54 gson Exp $");
49 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #include <netinet6/in6.h>
54 union sockaddr_either
{
56 struct sockaddr_in sin
;
57 struct sockaddr_in6 sin6
;
61 #define DPRINTF(...) fprintf(stderr, __VA_ARGS__)
66 /* A DNS question and its corresponding answer */
70 const char *qname
; /* Wire-encode question name */
73 const char *answer
; /* One wire-encoded answer RDATA */
76 /* Convert C string constant to length + data pair */
77 #define STR_DATA(s) sizeof(s) - 1, s
79 /* Canned DNS queestion-answer pairs */
80 struct dns_data data
[] = {
81 /* Forward mappings */
82 /* localhost IN A -> 127.0.0.1 */
83 { STR_DATA("\011localhost\000"), 1,
84 STR_DATA("\177\000\000\001") },
85 /* localhost IN AAAA -> ::1 */
86 { STR_DATA("\011localhost\000"), 28,
87 STR_DATA("\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001") },
88 /* sixthavenue.astron.com IN A -> 38.117.134.16 */
89 { STR_DATA("\013sixthavenue\006astron\003com\000"), 1,
90 STR_DATA("\046\165\206\020") },
91 /* sixthavenue.astron.com IN AAAA -> 2620:106:3003:1f00:3e4a:92ff:fef4:e180 */
92 { STR_DATA("\013sixthavenue\006astron\003com\000"), 28,
93 STR_DATA("\x26\x20\x01\x06\x30\x03\x1f\x00\x3e\x4a\x92\xff\xfe\xf4\xe1\x80") },
94 /* Reverse mappings */
95 { STR_DATA("\0011\0010\0010\003127\007in-addr\004arpa\000"), 12,
96 STR_DATA("\011localhost\000") },
97 { STR_DATA("\0011\0010\0010\0010\0010\0010\0010\0010"
98 "\0010\0010\0010\0010\0010\0010\0010\0010"
99 "\0010\0010\0010\0010\0010\0010\0010\0010"
100 "\0010\0010\0010\0010\0010\0010\0010\0010"
101 "\003ip6\004arpa\000"), 12,
102 STR_DATA("\011localhost\000") },
103 { STR_DATA("\00216\003134\003117\00238"
104 "\007in-addr\004arpa\000"), 12,
105 STR_DATA("\013sixthavenue\006astron\003com\000") },
106 { STR_DATA("\0010\0018\0011\001e\0014\001f\001e\001f"
107 "\001f\001f\0012\0019\001a\0014\001e\0013"
108 "\0010\0010\001f\0011\0013\0010\0010\0013"
109 "\0016\0010\0011\0010\0010\0012\0016\0012"
110 "\003ip6\004arpa\000"), 12,
111 STR_DATA("\013sixthavenue\006astron\003com\000") },
113 { STR_DATA(""), 0, STR_DATA("") }
117 * Compare two DNS names for equality. If equal, return their
118 * length, and if not, return zero. Does not handle compression.
121 name_eq(const unsigned char *a
, const unsigned char *b
) {
122 const unsigned char *a_save
= a
;
131 for (i
= 0; i
< lena
; i
++)
132 if (tolower(a
[i
]) != tolower(b
[i
]))
141 name2str(const void *v
, char *buf
, size_t buflen
) {
142 const unsigned char *a
= v
;
144 char *eb
= buf
+ buflen
;
146 #define ADDC(c) do { \
151 } while (/*CONSTCOND*/0)
152 for (int did
= 0;; did
++) {
160 for (int i
= 0; i
< lena
; i
++)
167 int main(int argc
, char **argv
) {
169 union sockaddr_either saddr
;
172 char pidfile_name
[40];
176 char buf1
[1024], buf2
[1024];
179 if (argc
< 2 || ((protocol
= argv
[1][0]) != '4' && protocol
!= '6'))
180 errx(1, "usage: dns_server 4 | 6");
181 s
= socket(protocol
== '4' ? PF_INET
: PF_INET6
, SOCK_DGRAM
, IPPROTO_UDP
);
184 if (protocol
== '4') {
185 memset(&saddr
.sin
, 0, sizeof(saddr
.sin
));
186 saddr
.sin
.sin_family
= AF_INET
;
187 saddr
.sin
.sin_len
= sizeof(saddr
.sin
);
188 saddr
.sin
.sin_port
= htons(53);
189 saddr
.sin
.sin_addr
.s_addr
= INADDR_ANY
;
191 static struct in6_addr loopback
= IN6ADDR_LOOPBACK_INIT
;
192 memset(&saddr
.sin6
, 0, sizeof(saddr
.sin6
));
193 saddr
.sin6
.sin6_family
= AF_INET6
;
194 saddr
.sin6
.sin6_len
= sizeof(saddr
.sin6
);
195 saddr
.sin6
.sin6_port
= htons(53);
196 saddr
.sin6
.sin6_addr
= loopback
;
199 r
= setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof one
);
201 err(1, "setsockopt");
204 (struct sockaddr
*) &saddr
,
205 protocol
== '4' ? sizeof(struct sockaddr_in
) :
206 sizeof(struct sockaddr_in6
));
210 snprintf(pidfile_name
, sizeof pidfile_name
,
211 "dns_server_%c.pid", protocol
);
212 f
= fopen(pidfile_name
, "w");
213 fprintf(f
, "%d", getpid());
222 unsigned char buf
[512];
223 union sockaddr_either from
;
224 ssize_t nrecv
, nsent
;
226 protocol
== '4' ? sizeof(struct sockaddr_in
) :
227 sizeof(struct sockaddr_in6
);
228 memset(buf
, 0, sizeof buf
);
229 nrecv
= recvfrom(s
, buf
, sizeof buf
, 0, &from
.s
, &fromlen
);
233 DPRINTF("Too short %zd\n", nrecv
);
236 if ((buf
[2] & 0x80) != 0) {
237 DPRINTF("Not a query 0x%x\n", buf
[2]);
240 if (!(buf
[4] == 0 && buf
[5] == 1)) {
241 DPRINTF("QCOUNT is not 1 0x%x 0x%x\n", buf
[4], buf
[5]);
242 continue; /* QDCOUNT is not 1 */
245 for (dp
= data
; dp
->qname_size
!= 0; dp
++) {
247 p
= buf
+ 12; /* Point to QNAME */
248 int n
= name_eq(p
, (const unsigned char *) dp
->qname
);
250 DPRINTF("no match name %s != %s\n",
251 name2str(p
, buf1
, sizeof(buf1
)),
252 name2str(dp
->qname
, buf2
, sizeof(buf2
)));
253 continue; /* Name does not match */
255 DPRINTF("match name %s\n",
256 name2str(p
, buf1
, sizeof(buf1
)));
257 p
+= n
; /* Skip QNAME */
260 if (qtype
!= dp
->qtype
) {
261 DPRINTF("no match name 0x%x != 0x%x\n",
265 DPRINTF("match type 0x%x\n", qtype
);
268 if (qclass
!= 1) { /* IN */
269 DPRINTF("no match class %d != 1\n", qclass
);
272 DPRINTF("match class %d\n", qclass
);
277 buf
[2] |= 0x80; /* QR */
278 buf
[3] |= 0x80; /* RA */
279 memset(buf
+ 6, 0, 6); /* Clear ANCOUNT, NSCOUNT, ARCOUNT */
280 buf
[7] = 1; /* ANCOUNT */
281 memcpy(p
, dp
->qname
, dp
->qname_size
);
283 *p
++ = dp
->qtype
>> 8;
284 *p
++ = dp
->qtype
& 0xFF;
287 memset(p
, 0, 4); /* TTL = 0 */
289 *p
++ = 0; /* RDLENGTH MSB */
290 *p
++ = dp
->answer_size
; /* RDLENGTH LSB */
291 memcpy(p
, dp
->answer
, dp
->answer_size
);
292 p
+= dp
->answer_size
;
293 nsent
= sendto(s
, buf
, p
- buf
, 0, &from
.s
, fromlen
);
294 DPRINTF("sent %zd\n", nsent
);
295 if (nsent
!= p
- buf
)