1 /* vi: set sw=4 ts=4: */
3 * Mini DNS server implementation for busybox
5 * Copyright (C) 2005 Roberto A. Foglietta (me@roberto.foglietta.name)
6 * Copyright (C) 2005 Odd Arild Olsen (oao at fibula dot no)
7 * Copyright (C) 2003 Paul Sheer
9 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
11 * Odd Arild Olsen started out with the sheerdns [1] of Paul Sheer and rewrote
12 * it into a shape which I believe is both easier to understand and maintain.
13 * I also reused the input buffer for output and removed services he did not
14 * need. [1] http://threading.2038bug.com/sheerdns/
16 * Some bugfix and minor changes was applied by Roberto A. Foglietta who made
17 * the first porting of oao' scdns to busybox also.
30 /* Cannot get bigger packets than 512 per RFC1035.
31 * In practice this can be set considerably smaller:
32 * Length of response packet is header (12B) + 2*type(4B) + 2*class(4B) +
33 * ttl(4B) + rlen(2B) + r (MAX_NAME_LEN = 21B) +
34 * 2*querystring (2 MAX_NAME_LEN = 42B), all together 90 Bytes
37 IP_STRING_LEN = sizeof(".xxx.xxx.xxx.xxx"),
38 MAX_NAME_LEN = IP_STRING_LEN - 1 + sizeof(".in-addr.arpa"),
43 /* the message from client and first part of response msg */
56 /* element of known name, ip address and reversed ip address */
58 struct dns_entry *next;
60 char rip[IP_STRING_LEN]; /* length decimal reversed IP */
64 #define OPT_verbose (option_mask32)
68 * Insert length of substrings instead of dots
70 static void undot(char *rip)
77 for (--i; i >= 0; i--) {
88 * Read hostname/IP records from file
90 static struct dns_entry *parse_conf_file(const char *fileconf)
94 struct dns_entry *m, *conf_data;
95 struct dns_entry **nextp;
100 parser = config_open(fileconf);
101 while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
105 if (inet_aton(token[1], &ip) == 0) {
106 bb_error_msg("error at line %u, skipping", parser->lineno);
111 bb_error_msg("name:%s, ip:%s", token[0], token[1]);
113 /* sizeof(*m) includes 1 byte for m->name[0] */
114 m = xzalloc(sizeof(*m) + strlen(token[0]) + 1);
120 strcpy(m->name + 1, token[0]);
122 m->ip = ip.s_addr; /* in network order */
125 sprintf(m->rip, ".%u.%u.%u.%u",
128 (uint8_t)(v32 >> 16),
133 config_close(parser);
138 * Look query up in dns records and return answer if found.
139 * qs is the query string.
141 static int table_lookup(uint8_t *as, struct dns_entry *d, uint16_t type, uint8_t *qs)
144 unsigned len = d->name[0];
145 /* d->name[len] is the last (non NUL) char */
148 q = (char *)&(qs[1]);
150 fprintf(stderr, "%d/%d p:%s q:%s %d\n",
155 if (type == htons(REQ_A)) {
156 /* search by host name */
157 if (len != 1 || d->name[1] != '*') {
158 if (strcasecmp(d->name, (char*)qs) != 0)
161 move_to_unaligned32((uint32_t *)as, d->ip);
163 fprintf(stderr, "OK as:%x\n", (int)d->ip);
167 /* search by IP-address */
168 if ((len != 1 || d->name[1] != '*')
169 /* assume (do not check) that qs ends in ".in-addr.arpa" */
170 && strncmp(d->rip, (char*)qs, strlen(d->rip)) == 0
172 strcpy((char *)as, d->name);
174 fprintf(stderr, "OK as:%s\n", as);
186 * Decode message and generate answer
190 Whenever an octet represents a numeric quantity, the left most bit
191 in the diagram is the high order or most significant bit.
192 That is, the bit labeled 0 is the most significant bit.
195 4.1.1. Header section format
197 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
198 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
200 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
201 |QR| OPCODE |AA|TC|RD|RA| Z | RCODE |
202 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
204 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
206 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
208 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
210 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
211 ID 16 bit random identifier assigned by query.
212 Used to match query/response.
213 QR message is a query (0), or a response (1).
214 OPCODE 0 standard query (QUERY)
215 1 inverse query (IQUERY)
216 2 server status request (STATUS)
217 AA Authoritative Answer - this bit is valid in responses,
218 and specifies that the responding name server is an
219 authority for the domain name in question section.
220 Note that the contents of the answer section may have
221 multiple owner names because of aliases. The AA bit
222 corresponds to the name which matches the query name, or
223 the first owner name in the answer section.
224 TC TrunCation - specifies that this message was truncated.
225 RD Recursion Desired - this bit may be set in a query and
226 is copied into the response. If RD is set, it directs
227 the name server to pursue the query recursively.
228 Recursive query support is optional.
229 RA Recursion Available - this be is set or cleared in a
230 response, and denotes whether recursive query support is
231 available in the name server.
232 Z Reserved for future use. Must be zero.
236 2 Server failure - The name server was
237 unable to process this query due to a
238 problem with the name server.
239 3 Name Error - Meaningful only for
240 responses from an authoritative name
241 server, this code signifies that the
242 domain name referenced in the query does
246 QDCOUNT number of entries in the question section.
247 ANCOUNT number of resource records in the answer section.
248 NSCOUNT number of name server resource records in the authority records section.
249 ARCOUNT number of resource records in the additional records section.
251 4.1.2. Question section format
253 The section contains QDCOUNT (usually 1) entries, each of the following format:
255 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
256 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
259 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
261 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
263 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
264 QNAME a domain name represented as a sequence of labels, where
265 each label consists of a length octet followed by that
266 number of octets. The domain name terminates with the
267 zero length octet for the null label of the root. Note
268 that this field may be an odd number of octets; no
270 QTYPE a two octet type of the query.
271 1 a host address [REQ_A const]
272 2 an authoritative name server
273 3 a mail destination (Obsolete - use MX)
274 4 a mail forwarder (Obsolete - use MX)
275 5 the canonical name for an alias
276 6 marks the start of a zone of authority
277 7 a mailbox domain name (EXPERIMENTAL)
278 8 a mail group member (EXPERIMENTAL)
279 9 a mail rename domain name (EXPERIMENTAL)
280 10 a null RR (EXPERIMENTAL)
281 11 a well known service description
282 12 a domain name pointer [REQ_PTR const]
284 14 mailbox or mail list information
288 252 a request for a transfer of an entire zone
289 253 a request for mailbox-related records (MB, MG or MR)
290 254 a request for mail agent RRs (Obsolete - see MX)
291 255 a request for all records
292 QCLASS a two octet code that specifies the class of the query.
294 (others are historic only)
297 4.1.3. Resource record format
299 The answer, authority, and additional sections all share the same
300 format: a variable number of resource records, where the number of
301 records is specified in the corresponding count field in the header.
302 Each resource record has the following format:
304 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
305 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
308 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
310 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
312 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
315 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
317 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
320 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
321 NAME a domain name to which this resource record pertains.
322 TYPE two octets containing one of the RR type codes. This
323 field specifies the meaning of the data in the RDATA
325 CLASS two octets which specify the class of the data in the
327 TTL a 32 bit unsigned integer that specifies the time
328 interval (in seconds) that the resource record may be
329 cached before it should be discarded. Zero values are
330 interpreted to mean that the RR can only be used for the
331 transaction in progress, and should not be cached.
332 RDLENGTH an unsigned 16 bit integer that specifies the length in
333 octets of the RDATA field.
334 RDATA a variable length string of octets that describes the
335 resource. The format of this information varies
336 according to the TYPE and CLASS of the resource record.
337 For example, if the TYPE is A and the CLASS is IN,
338 the RDATA field is a 4 octet ARPA Internet address.
340 4.1.4. Message compression
342 In order to reduce the size of messages, the domain system utilizes a
343 compression scheme which eliminates the repetition of domain names in a
344 message. In this scheme, an entire domain name or a list of labels at
345 the end of a domain name is replaced with a pointer to a prior occurance
348 The pointer takes the form of a two octet sequence:
349 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
351 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
352 The first two bits are ones. This allows a pointer to be distinguished
353 from a label, since the label must begin with two zero bits because
354 labels are restricted to 63 octets or less. The OFFSET field specifies
355 an offset from the start of the message (i.e., the first octet
356 of the ID field in the domain header).
357 A zero offset specifies the first byte of the ID field, etc.
359 The compression scheme allows a domain name in a message to be
360 represented as either:
361 - a sequence of labels ending in a zero octet
363 - a sequence of labels ending with a pointer
365 static int process_packet(struct dns_entry *conf_data, uint32_t conf_ttl, uint8_t *buf)
367 uint8_t answstr[MAX_NAME_LEN + 1];
368 struct dns_head *head;
369 struct dns_prop *unaligned_qprop;
370 uint8_t *from, *answb;
379 head = (struct dns_head *)buf;
380 if (head->nquer == 0) {
381 bb_error_msg("packet has 0 queries, ignored");
385 if (head->flags & htons(0x8000)) { /* QR bit */
386 bb_error_msg("response packet, ignored");
390 /* start of query string */
391 from = (void *)(head + 1);
392 /* caller guarantees strlen is <= MAX_PACK_LEN */
393 querystr_len = strlen((char *)from) + 1;
394 /* may be unaligned! */
395 unaligned_qprop = (void *)(from + querystr_len);
396 /* where to append answer block */
397 answb = (void *)(unaligned_qprop + 1);
400 /* QR = 1 "response", RCODE = 4 "Not Implemented" */
401 outr_flags = htons(0x8000 | 4);
403 move_from_unaligned16(type, &unaligned_qprop->type);
404 if (type != htons(REQ_A) && type != htons(REQ_PTR)) {
405 /* we can't handle the query type */
408 move_from_unaligned16(class, &unaligned_qprop->class);
409 if (class != htons(1)) { /* not class INET? */
412 /* OPCODE != 0 "standard query" ? */
413 if ((head->flags & htons(0x7800)) != 0) {
417 bb_info_msg("%s", (char *)from);
418 if (table_lookup(answstr, conf_data, type, from) != 0) {
420 * AA = 1 "Authoritative Answer"
421 * RCODE = 3 "Name Error" */
422 outr_flags = htons(0x8000 | 0x0400 | 3);
425 /* return an address */
427 if (type == htons(REQ_PTR)) {
428 /* return a host name */
429 outr_rlen = strlen((char *)answstr) + 1;
431 /* QR = 1 "response",
432 * AA = 1 "Authoritative Answer",
433 * RCODE = 0 "success" */
434 outr_flags = htons(0x8000 | 0x0400 | 0);
435 /* we have one answer */
436 head->nansw = htons(1);
437 /* copy query block to answer block */
438 querystr_len += sizeof(unaligned_qprop);
439 memcpy(answb, from, querystr_len);
440 answb += querystr_len;
441 /* append answer Resource Record */
442 move_to_unaligned32((uint32_t *)answb, htonl(conf_ttl));
444 move_to_unaligned32((uint16_t *)answb, htons(outr_rlen));
446 memcpy(answb, answstr, outr_rlen);
450 head->flags |= outr_flags;
451 head->nauth = head->nadd = 0;
452 head->nquer = htons(1); // why???
457 int dnsd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
458 int dnsd_main(int argc UNUSED_PARAM, char **argv)
460 const char *listen_interface = "0.0.0.0";
461 const char *fileconf = "/etc/dnsd.conf";
462 struct dns_entry *conf_data;
463 uint32_t conf_ttl = DEFAULT_TTL;
465 len_and_sockaddr *lsa, *from, *to;
469 /* Paranoid sizing: querystring x2 + ttl + outr_rlen + answstr */
470 /* I'd rather see process_packet() fixed instead... */
471 uint8_t buf[MAX_PACK_LEN * 2 + 4 + 2 + (MAX_NAME_LEN+1)];
473 opts = getopt32(argv, "vi:c:t:p:d", &listen_interface, &fileconf, &sttl, &sport);
474 //if (opts & 0x1) // -v
475 //if (opts & 0x2) // -i
476 //if (opts & 0x4) // -c
477 if (opts & 0x8) // -t
478 conf_ttl = xatou_range(sttl, 1, 0xffffffff);
479 if (opts & 0x10) // -p
480 port = xatou_range(sport, 1, 0xffff);
481 if (opts & 0x20) { // -d
482 bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
483 openlog(applet_name, LOG_PID, LOG_DAEMON);
484 logmode = LOGMODE_SYSLOG;
486 /* Clear all except "verbose" bit */
489 conf_data = parse_conf_file(fileconf);
491 lsa = xdotted2sockaddr(listen_interface, port);
492 udps = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0);
493 xbind(udps, &lsa->u.sa, lsa->len);
494 socket_want_pktinfo(udps); /* needed for recv_from_to to work */
495 lsa_size = LSA_LEN_SIZE + lsa->len;
496 from = xzalloc(lsa_size);
497 to = xzalloc(lsa_size);
500 char *p = xmalloc_sockaddr2dotted(&lsa->u.sa);
501 bb_info_msg("Accepting UDP packets on %s", p);
507 /* Try to get *DEST* address (to which of our addresses
508 * this query was directed), and reply from the same address.
509 * Or else we can exhibit usual UDP ugliness:
510 * [ip1.multihomed.ip2] <= query to ip1 <= peer
511 * [ip1.multihomed.ip2] => reply from ip2 => peer (confused) */
512 memcpy(to, lsa, lsa_size);
513 r = recv_from_to(udps, buf, MAX_PACK_LEN + 1, 0, &from->u.sa, &to->u.sa, lsa->len);
514 if (r < 12 || r > MAX_PACK_LEN) {
515 bb_error_msg("packet size %d, ignored", r);
519 bb_info_msg("Got UDP packet");
520 buf[r] = '\0'; /* paranoia */
521 r = process_packet(conf_data, conf_ttl, buf);
524 send_to_from(udps, buf, r, 0, &from->u.sa, &to->u.sa, lsa->len);