busybox/networking/dnsd.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini DNS server implementation for busybox
   4 *
   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
   8 *
   9 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  10 *
  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/
  15 *
  16 * Some bugfix and minor changes was applied by Roberto A. Foglietta who made
  17 * the first porting of oao' scdns to busybox also.
  18 */
  19//config:config DNSD
  20//config:       bool "dnsd (9.8 kb)"
  21//config:       default y
  22//config:       help
  23//config:       Small and static DNS server daemon.
  24
  25//applet:IF_DNSD(APPLET(dnsd, BB_DIR_USR_SBIN, BB_SUID_DROP))
  26
  27//kbuild:lib-$(CONFIG_DNSD) += dnsd.o
  28
  29//usage:#define dnsd_trivial_usage
  30//usage:       "[-dvs] [-c CONFFILE] [-t TTL_SEC] [-p PORT] [-i ADDR]"
  31//usage:#define dnsd_full_usage "\n\n"
  32//usage:       "Small static DNS server daemon\n"
  33//usage:     "\n        -c FILE Config file"
  34//usage:     "\n        -t SEC  TTL"
  35//usage:     "\n        -p PORT Listen on PORT"
  36//usage:     "\n        -i ADDR Listen on ADDR"
  37//usage:     "\n        -d      Daemonize"
  38//usage:     "\n        -v      Verbose"
  39//usage:     "\n        -s      Send successful replies only. Use this if you want"
  40//usage:     "\n                to use /etc/resolv.conf with two nameserver lines:"
  41//usage:     "\n                        nameserver DNSD_SERVER"
  42//usage:     "\n                        nameserver NORMAL_DNS_SERVER"
  43
  44#include "libbb.h"
  45#include <syslog.h>
  46
  47//#define DEBUG 1
  48#define DEBUG 0
  49
  50enum {
  51        /* can tweak this */
  52        DEFAULT_TTL = 120,
  53
  54        /* cannot get bigger packets than 512 per RFC1035. */
  55        MAX_PACK_LEN = 512,
  56        IP_STRING_LEN = sizeof(".xxx.xxx.xxx.xxx"),
  57        MAX_NAME_LEN = IP_STRING_LEN - 1 + sizeof(".in-addr.arpa"),
  58        REQ_A = 1,
  59        REQ_PTR = 12,
  60};
  61
  62/* the message from client and first part of response msg */
  63struct dns_head {
  64        uint16_t id;
  65        uint16_t flags;
  66        uint16_t nquer;
  67        uint16_t nansw;
  68        uint16_t nauth;
  69        uint16_t nadd;
  70};
  71/* Structure used to access type and class fields.
  72 * They are totally unaligned, but gcc 4.3.4 thinks that pointer of type uint16_t*
  73 * is 16-bit aligned and replaces 16-bit memcpy (in move_from_unaligned16 macro)
  74 * with aligned halfword access on arm920t!
  75 * Oh well. Slapping PACKED everywhere seems to help: */
  76struct type_and_class {
  77        uint16_t type PACKED;
  78        uint16_t class PACKED;
  79} PACKED;
  80/* element of known name, ip address and reversed ip address */
  81struct dns_entry {
  82        struct dns_entry *next;
  83        uint32_t ip;
  84        char rip[IP_STRING_LEN]; /* length decimal reversed IP */
  85        char name[1];
  86};
  87
  88#define OPT_verbose (option_mask32 & 1)
  89#define OPT_silent  (option_mask32 & 2)
  90
  91
  92/*
  93 * Insert length of substrings instead of dots
  94 */
  95static void undot(char *rip)
  96{
  97        int i = 0;
  98        int s = 0;
  99
 100        while (rip[i])
 101                i++;
 102        for (--i; i >= 0; i--) {
 103                if (rip[i] == '.') {
 104                        rip[i] = s;
 105                        s = 0;
 106                } else {
 107                        s++;
 108                }
 109        }
 110}
 111
 112/*
 113 * Read hostname/IP records from file
 114 */
 115static struct dns_entry *parse_conf_file(const char *fileconf)
 116{
 117        char *token[2];
 118        parser_t *parser;
 119        struct dns_entry *m, *conf_data;
 120        struct dns_entry **nextp;
 121
 122        conf_data = NULL;
 123        nextp = &conf_data;
 124
 125        parser = config_open(fileconf);
 126        while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
 127                struct in_addr ip;
 128                uint32_t v32;
 129
 130                if (inet_aton(token[1], &ip) == 0) {
 131                        bb_error_msg("error at line %u, skipping", parser->lineno);
 132                        continue;
 133                }
 134
 135                if (OPT_verbose)
 136                        bb_info_msg("name:%s, ip:%s", token[0], token[1]);
 137
 138                /* sizeof(*m) includes 1 byte for m->name[0] */
 139                m = xzalloc(sizeof(*m) + strlen(token[0]) + 1);
 140                /*m->next = NULL;*/
 141                *nextp = m;
 142                nextp = &m->next;
 143
 144                m->name[0] = '.';
 145                strcpy(m->name + 1, token[0]);
 146                undot(m->name);
 147                m->ip = ip.s_addr; /* in network order */
 148                v32 = ntohl(m->ip);
 149                /* inverted order */
 150                sprintf(m->rip, ".%u.%u.%u.%u",
 151                        (uint8_t)(v32),
 152                        (uint8_t)(v32 >> 8),
 153                        (uint8_t)(v32 >> 16),
 154                        (v32 >> 24)
 155                );
 156                undot(m->rip);
 157        }
 158        config_close(parser);
 159        return conf_data;
 160}
 161
 162/*
 163 * Look query up in dns records and return answer if found.
 164 */
 165static char *table_lookup(struct dns_entry *d,
 166                uint16_t type,
 167                char* query_string)
 168{
 169        while (d) {
 170                unsigned len = d->name[0];
 171                /* d->name[len] is the last (non NUL) char */
 172#if DEBUG
 173                char *p, *q;
 174                q = query_string + 1;
 175                p = d->name + 1;
 176                fprintf(stderr, "%d/%d p:%s q:%s %d\n",
 177                        (int)strlen(p), len,
 178                        p, q, (int)strlen(q)
 179                );
 180#endif
 181                if (type == htons(REQ_A)) {
 182                        /* search by host name */
 183                        if (len != 1 || d->name[1] != '*') {
 184/* we are lax, hope no name component is ever >64 so that length
 185 * (which will be represented as 'A','B'...) matches a lowercase letter.
 186 * Actually, I think false matches are hard to construct.
 187 * Example.
 188 * [31] len is represented as '1', [65] as 'A', [65+32] as 'a'.
 189 * [65]   <65 same chars>[31]<31 same chars>NUL
 190 * [65+32]<65 same chars>1   <31 same chars>NUL
 191 * This example seems to be the minimal case when false match occurs.
 192 */
 193                                if (strcasecmp(d->name, query_string) != 0)
 194                                        goto next;
 195                        }
 196                        return (char *)&d->ip;
 197#if DEBUG
 198                        fprintf(stderr, "Found IP:%x\n", (int)d->ip);
 199#endif
 200                        return 0;
 201                }
 202                /* search by IP-address */
 203                if ((len != 1 || d->name[1] != '*')
 204                /* we assume (do not check) that query_string
 205                 * ends in ".in-addr.arpa" */
 206                 && is_prefixed_with(query_string, d->rip)
 207                ) {
 208#if DEBUG
 209                        fprintf(stderr, "Found name:%s\n", d->name);
 210#endif
 211                        return d->name;
 212                }
 213 next:
 214                d = d->next;
 215        }
 216
 217        return NULL;
 218}
 219
 220/*
 221 * Decode message and generate answer
 222 */
 223/* RFC 1035
 224...
 225Whenever an octet represents a numeric quantity, the left most bit
 226in the diagram is the high order or most significant bit.
 227That is, the bit labeled 0 is the most significant bit.
 228...
 229
 2304.1.1. Header section format
 231      0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
 232    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 233    |                      ID                       |
 234    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 235    |QR|   OPCODE  |AA|TC|RD|RA| 0  0  0|   RCODE   |
 236    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 237    |                    QDCOUNT                    |
 238    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 239    |                    ANCOUNT                    |
 240    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 241    |                    NSCOUNT                    |
 242    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 243    |                    ARCOUNT                    |
 244    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 245ID      16 bit random identifier assigned by querying peer.
 246        Used to match query/response.
 247QR      message is a query (0), or a response (1).
 248OPCODE  0   standard query (QUERY)
 249        1   inverse query (IQUERY)
 250        2   server status request (STATUS)
 251AA      Authoritative Answer - this bit is valid in responses.
 252        Responding name server is an authority for the domain name
 253        in question section. Answer section may have multiple owner names
 254        because of aliases.  The AA bit corresponds to the name which matches
 255        the query name, or the first owner name in the answer section.
 256TC      TrunCation - this message was truncated.
 257RD      Recursion Desired - this bit may be set in a query and
 258        is copied into the response.  If RD is set, it directs
 259        the name server to pursue the query recursively.
 260        Recursive query support is optional.
 261RA      Recursion Available - this be is set or cleared in a
 262        response, and denotes whether recursive query support is
 263        available in the name server.
 264RCODE   Response code.
 265        0   No error condition
 266        1   Format error
 267        2   Server failure - server was unable to process the query
 268            due to a problem with the name server.
 269        3   Name Error - meaningful only for responses from
 270            an authoritative name server. The referenced domain name
 271            does not exist.
 272        4   Not Implemented.
 273        5   Refused.
 274QDCOUNT number of entries in the question section.
 275ANCOUNT number of records in the answer section.
 276NSCOUNT number of records in the authority records section.
 277ARCOUNT number of records in the additional records section.
 278
 2794.1.2. Question section format
 280
 281The section contains QDCOUNT (usually 1) entries, each of this format:
 282      0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
 283    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 284    /                     QNAME                     /
 285    /                                               /
 286    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 287    |                     QTYPE                     |
 288    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 289    |                     QCLASS                    |
 290    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 291QNAME   a domain name represented as a sequence of labels, where
 292        each label consists of a length octet followed by that
 293        number of octets. The domain name terminates with the
 294        zero length octet for the null label of the root. Note
 295        that this field may be an odd number of octets; no
 296        padding is used.
 297QTYPE   a two octet type of the query.
 298          1 a host address [REQ_A const]
 299          2 an authoritative name server
 300          3 a mail destination (Obsolete - use MX)
 301          4 a mail forwarder (Obsolete - use MX)
 302          5 the canonical name for an alias
 303          6 marks the start of a zone of authority
 304          7 a mailbox domain name (EXPERIMENTAL)
 305          8 a mail group member (EXPERIMENTAL)
 306          9 a mail rename domain name (EXPERIMENTAL)
 307         10 a null RR (EXPERIMENTAL)
 308         11 a well known service description
 309         12 a domain name pointer [REQ_PTR const]
 310         13 host information
 311         14 mailbox or mail list information
 312         15 mail exchange
 313         16 text strings
 314       0x1c IPv6?
 315        252 a request for a transfer of an entire zone
 316        253 a request for mailbox-related records (MB, MG or MR)
 317        254 a request for mail agent RRs (Obsolete - see MX)
 318        255 a request for all records
 319QCLASS  a two octet code that specifies the class of the query.
 320          1 the Internet
 321        (others are historic only)
 322        255 any class
 323
 3244.1.3. Resource Record format
 325
 326The answer, authority, and additional sections all share the same format:
 327a variable number of resource records, where the number of records
 328is specified in the corresponding count field in the header.
 329Each resource record has this format:
 330      0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
 331    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 332    /                                               /
 333    /                      NAME                     /
 334    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 335    |                      TYPE                     |
 336    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 337    |                     CLASS                     |
 338    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 339    |                      TTL                      |
 340    |                                               |
 341    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 342    |                   RDLENGTH                    |
 343    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
 344    /                     RDATA                     /
 345    /                                               /
 346    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 347NAME    a domain name to which this resource record pertains.
 348TYPE    two octets containing one of the RR type codes.  This
 349        field specifies the meaning of the data in the RDATA field.
 350CLASS   two octets which specify the class of the data in the RDATA field.
 351TTL     a 32 bit unsigned integer that specifies the time interval
 352        (in seconds) that the record may be cached.
 353RDLENGTH a 16 bit integer, length in octets of the RDATA field.
 354RDATA   a variable length string of octets that describes the resource.
 355        The format of this information varies according to the TYPE
 356        and CLASS of the resource record.
 357        If the TYPE is A and the CLASS is IN, it's a 4 octet IP address.
 358
 3594.1.4. Message compression
 360
 361In order to reduce the size of messages, domain names coan be compressed.
 362An entire domain name or a list of labels at the end of a domain name
 363is replaced with a pointer to a prior occurrence of the same name.
 364
 365The pointer takes the form of a two octet sequence:
 366    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 367    | 1  1|                OFFSET                   |
 368    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 369The first two bits are ones.  This allows a pointer to be distinguished
 370from a label, since the label must begin with two zero bits because
 371labels are restricted to 63 octets or less.  The OFFSET field specifies
 372an offset from the start of the message (i.e., the first octet
 373of the ID field in the domain header).
 374A zero offset specifies the first byte of the ID field, etc.
 375Domain name in a message can be represented as either:
 376   - a sequence of labels ending in a zero octet
 377   - a pointer
 378   - a sequence of labels ending with a pointer
 379 */
 380static int process_packet(struct dns_entry *conf_data,
 381                uint32_t conf_ttl,
 382                uint8_t *buf)
 383{
 384        struct dns_head *head;
 385        struct type_and_class *unaligned_type_class;
 386        const char *err_msg;
 387        char *query_string;
 388        char *answstr;
 389        uint8_t *answb;
 390        uint16_t outr_rlen;
 391        uint16_t outr_flags;
 392        uint16_t type;
 393        uint16_t class;
 394        int query_len;
 395
 396        head = (struct dns_head *)buf;
 397        if (head->nquer == 0) {
 398                bb_error_msg("packet has 0 queries, ignored");
 399                return 0; /* don't reply */
 400        }
 401        if (head->flags & htons(0x8000)) { /* QR bit */
 402                bb_error_msg("response packet, ignored");
 403                return 0; /* don't reply */
 404        }
 405        /* QR = 1 "response", RCODE = 4 "Not Implemented" */
 406        outr_flags = htons(0x8000 | 4);
 407        err_msg = NULL;
 408
 409        /* start of query string */
 410        query_string = (void *)(head + 1);
 411        /* caller guarantees strlen is <= MAX_PACK_LEN */
 412        query_len = strlen(query_string) + 1;
 413        /* may be unaligned! */
 414        unaligned_type_class = (void *)(query_string + query_len);
 415        query_len += sizeof(*unaligned_type_class);
 416        /* where to append answer block */
 417        answb = (void *)(unaligned_type_class + 1);
 418
 419        /* OPCODE != 0 "standard query"? */
 420        if ((head->flags & htons(0x7800)) != 0) {
 421                err_msg = "opcode != 0";
 422                goto empty_packet;
 423        }
 424        move_from_unaligned16(class, &unaligned_type_class->class);
 425        if (class != htons(1)) { /* not class INET? */
 426                err_msg = "class != 1";
 427                goto empty_packet;
 428        }
 429        move_from_unaligned16(type, &unaligned_type_class->type);
 430        if (type != htons(REQ_A) && type != htons(REQ_PTR)) {
 431                /* we can't handle this query type */
 432//TODO: happens all the time with REQ_AAAA (0x1c) requests - implement those?
 433                err_msg = "type is !REQ_A and !REQ_PTR";
 434                goto empty_packet;
 435        }
 436
 437        /* look up the name */
 438        answstr = table_lookup(conf_data, type, query_string);
 439#if DEBUG
 440        /* Shows lengths instead of dots, unusable for !DEBUG */
 441        bb_info_msg("'%s'->'%s'", query_string, answstr);
 442#endif
 443        outr_rlen = 4;
 444        if (answstr && type == htons(REQ_PTR)) {
 445                /* returning a host name */
 446                outr_rlen = strlen(answstr) + 1;
 447        }
 448        if (!answstr
 449         || (unsigned)(answb - buf) + query_len + 4 + 2 + outr_rlen > MAX_PACK_LEN
 450        ) {
 451                /* QR = 1 "response"
 452                 * AA = 1 "Authoritative Answer"
 453                 * RCODE = 3 "Name Error" */
 454                err_msg = "name is not found";
 455                outr_flags = htons(0x8000 | 0x0400 | 3);
 456                goto empty_packet;
 457        }
 458
 459        /* Append answer Resource Record */
 460        memcpy(answb, query_string, query_len); /* name, type, class */
 461        answb += query_len;
 462        move_to_unaligned32((uint32_t *)answb, htonl(conf_ttl));
 463        answb += 4;
 464        move_to_unaligned16((uint16_t *)answb, htons(outr_rlen));
 465        answb += 2;
 466        memcpy(answb, answstr, outr_rlen);
 467        answb += outr_rlen;
 468
 469        /* QR = 1 "response",
 470         * AA = 1 "Authoritative Answer",
 471         * TODO: need to set RA bit 0x80? One user says nslookup complains
 472         * "Got recursion not available from SERVER, trying next server"
 473         * "** server can't find HOSTNAME"
 474         * RCODE = 0 "success"
 475         */
 476        if (OPT_verbose)
 477                bb_info_msg("returning positive reply");
 478        outr_flags = htons(0x8000 | 0x0400 | 0);
 479        /* we have one answer */
 480        head->nansw = htons(1);
 481
 482 empty_packet:
 483        if ((outr_flags & htons(0xf)) != 0) { /* not a positive response */
 484                if (OPT_verbose) {
 485                        bb_error_msg("%s, %s",
 486                                err_msg,
 487                                OPT_silent ? "dropping query" : "sending error reply"
 488                        );
 489                }
 490                if (OPT_silent)
 491                        return 0;
 492        }
 493        head->flags |= outr_flags;
 494        head->nauth = head->nadd = 0;
 495        head->nquer = htons(1); // why???
 496
 497        return answb - buf;
 498}
 499
 500int dnsd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 501int dnsd_main(int argc UNUSED_PARAM, char **argv)
 502{
 503        const char *listen_interface = "0.0.0.0";
 504        const char *fileconf = "/etc/dnsd.conf";
 505        struct dns_entry *conf_data;
 506        uint32_t conf_ttl = DEFAULT_TTL;
 507        char *sttl, *sport;
 508        len_and_sockaddr *lsa, *from, *to;
 509        unsigned lsa_size;
 510        int udps, opts;
 511        uint16_t port = 53;
 512        /* Ensure buf is 32bit aligned (we need 16bit, but 32bit can't hurt) */
 513        uint8_t buf[MAX_PACK_LEN + 1] ALIGN4;
 514
 515        opts = getopt32(argv, "vsi:c:t:p:d", &listen_interface, &fileconf, &sttl, &sport);
 516        //if (opts & (1 << 0)) // -v
 517        //if (opts & (1 << 1)) // -s
 518        //if (opts & (1 << 2)) // -i
 519        //if (opts & (1 << 3)) // -c
 520        if (opts & (1 << 4)) // -t
 521                conf_ttl = xatou_range(sttl, 1, 0xffffffff);
 522        if (opts & (1 << 5)) // -p
 523                port = xatou_range(sport, 1, 0xffff);
 524        if (opts & (1 << 6)) { // -d
 525                bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
 526                openlog(applet_name, LOG_PID, LOG_DAEMON);
 527                logmode = LOGMODE_SYSLOG;
 528        }
 529
 530        conf_data = parse_conf_file(fileconf);
 531
 532        lsa = xdotted2sockaddr(listen_interface, port);
 533        udps = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0);
 534        xbind(udps, &lsa->u.sa, lsa->len);
 535        socket_want_pktinfo(udps); /* needed for recv_from_to to work */
 536        lsa_size = LSA_LEN_SIZE + lsa->len;
 537        from = xzalloc(lsa_size);
 538        to = xzalloc(lsa_size);
 539
 540        {
 541                char *p = xmalloc_sockaddr2dotted(&lsa->u.sa);
 542                bb_info_msg("accepting UDP packets on %s", p);
 543                free(p);
 544        }
 545
 546        while (1) {
 547                int r;
 548                /* Try to get *DEST* address (to which of our addresses
 549                 * this query was directed), and reply from the same address.
 550                 * Or else we can exhibit usual UDP ugliness:
 551                 * [ip1.multihomed.ip2] <=  query to ip1  <= peer
 552                 * [ip1.multihomed.ip2] => reply from ip2 => peer (confused) */
 553                memcpy(to, lsa, lsa_size);
 554                r = recv_from_to(udps, buf, MAX_PACK_LEN + 1, 0, &from->u.sa, &to->u.sa, lsa->len);
 555                if (r < 12 || r > MAX_PACK_LEN) {
 556                        bb_error_msg("packet size %d, ignored", r);
 557                        continue;
 558                }
 559                if (OPT_verbose)
 560                        bb_info_msg("got UDP packet");
 561                buf[r] = '\0'; /* paranoia */
 562                r = process_packet(conf_data, conf_ttl, buf);
 563                if (r <= 0)
 564                        continue;
 565                send_to_from(udps, buf, r, 0, &from->u.sa, &to->u.sa, lsa->len);
 566        }
 567        return 0;
 568}
 569