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