uboot/net/dns.c
<<
>>
Prefs
   1/*
   2 * DNS support driver
   3 *
   4 * Copyright (c) 2008 Pieter Voorthuijsen <pieter.voorthuijsen@prodrive.nl>
   5 * Copyright (c) 2009 Robin Getz <rgetz@blackfin.uclinux.org>
   6 *
   7 * This is a simple DNS implementation for U-Boot. It will use the first IP
   8 * in the DNS response as NetServerIP. This can then be used for any other
   9 * network related activities.
  10 *
  11 * The packet handling is partly based on TADNS, original copyrights
  12 * follow below.
  13 *
  14 */
  15
  16/*
  17 * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
  18 *
  19 * "THE BEER-WARE LICENSE" (Revision 42):
  20 * Sergey Lyubka wrote this file.  As long as you retain this notice you
  21 * can do whatever you want with this stuff. If we meet some day, and you think
  22 * this stuff is worth it, you can buy me a beer in return.
  23 */
  24
  25#include <common.h>
  26#include <command.h>
  27#include <net.h>
  28#include <asm/unaligned.h>
  29
  30#include "dns.h"
  31
  32char *NetDNSResolve;    /* The host to resolve  */
  33char *NetDNSenvvar;     /* The envvar to store the answer in */
  34
  35static int DnsOurPort;
  36
  37static void
  38DnsSend(void)
  39{
  40        struct header *header;
  41        int n, name_len;
  42        uchar *p, *pkt;
  43        const char *s;
  44        const char *name;
  45        enum dns_query_type qtype = DNS_A_RECORD;
  46
  47        name = NetDNSResolve;
  48        pkt = p = (uchar *)(NetTxPacket + NetEthHdrSize() + IP_HDR_SIZE);
  49
  50        /* Prepare DNS packet header */
  51        header           = (struct header *) pkt;
  52        header->tid      = 1;
  53        header->flags    = htons(0x100);        /* standard query */
  54        header->nqueries = htons(1);            /* Just one query */
  55        header->nanswers = 0;
  56        header->nauth    = 0;
  57        header->nother   = 0;
  58
  59        /* Encode DNS name */
  60        name_len = strlen(name);
  61        p = (uchar *) &header->data;    /* For encoding host name into packet */
  62
  63        do {
  64                s = strchr(name, '.');
  65                if (!s)
  66                        s = name + name_len;
  67
  68                n = s - name;                   /* Chunk length */
  69                *p++ = n;                       /* Copy length  */
  70                memcpy(p, name, n);             /* Copy chunk   */
  71                p += n;
  72
  73                if (*s == '.')
  74                        n++;
  75
  76                name += n;
  77                name_len -= n;
  78        } while (*s != '\0');
  79
  80        *p++ = 0;                       /* Mark end of host name */
  81        *p++ = 0;                       /* Some servers require double null */
  82        *p++ = (unsigned char) qtype;   /* Query Type */
  83
  84        *p++ = 0;
  85        *p++ = 1;                               /* Class: inet, 0x0001 */
  86
  87        n = p - pkt;                            /* Total packet length */
  88        debug("Packet size %d\n", n);
  89
  90        DnsOurPort = random_port();
  91
  92        NetSendUDPPacket(NetServerEther, NetOurDNSIP, DNS_SERVICE_PORT,
  93                DnsOurPort, n);
  94        debug("DNS packet sent\n");
  95}
  96
  97static void
  98DnsTimeout(void)
  99{
 100        puts("Timeout\n");
 101        NetState = NETLOOP_FAIL;
 102}
 103
 104static void
 105DnsHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src, unsigned len)
 106{
 107        struct header *header;
 108        const unsigned char *p, *e, *s;
 109        u16 type, i;
 110        int found, stop, dlen;
 111        char IPStr[22];
 112        IPaddr_t IPAddress;
 113
 114
 115        debug("%s\n", __func__);
 116        if (dest != DnsOurPort)
 117                return;
 118
 119        for (i = 0; i < len; i += 4)
 120                debug("0x%p - 0x%.2x  0x%.2x  0x%.2x  0x%.2x\n",
 121                        pkt+i, pkt[i], pkt[i+1], pkt[i+2], pkt[i+3]);
 122
 123        /* We sent one query. We want to have a single answer: */
 124        header = (struct header *) pkt;
 125        if (ntohs(header->nqueries) != 1)
 126                return;
 127
 128        /* Received 0 answers */
 129        if (header->nanswers == 0) {
 130                puts("DNS: host not found\n");
 131                NetState = NETLOOP_SUCCESS;
 132                return;
 133        }
 134
 135        /* Skip host name */
 136        s = &header->data[0];
 137        e = pkt + len;
 138        for (p = s; p < e && *p != '\0'; p++)
 139                continue;
 140
 141        /* We sent query class 1, query type 1 */
 142        if (&p[5] > e || get_unaligned_be16(p+1) != DNS_A_RECORD) {
 143                puts("DNS: response was not an A record\n");
 144                NetState = NETLOOP_SUCCESS;
 145                return;
 146        }
 147
 148        /* Go to the first answer section */
 149        p += 5;
 150
 151        /* Loop through the answers, we want A type answer */
 152        for (found = stop = 0; !stop && &p[12] < e; ) {
 153
 154                /* Skip possible name in CNAME answer */
 155                if (*p != 0xc0) {
 156                        while (*p && &p[12] < e)
 157                                p++;
 158                        p--;
 159                }
 160                debug("Name (Offset in header): %d\n", p[1]);
 161
 162                type = get_unaligned_be16(p+2);
 163                debug("type = %d\n", type);
 164                if (type == DNS_CNAME_RECORD) {
 165                        /* CNAME answer. shift to the next section */
 166                        debug("Found canonical name\n");
 167                        dlen = get_unaligned_be16(p+10);
 168                        debug("dlen = %d\n", dlen);
 169                        p += 12 + dlen;
 170                } else if (type == DNS_A_RECORD) {
 171                        debug("Found A-record\n");
 172                        found = stop = 1;
 173                } else {
 174                        debug("Unknown type\n");
 175                        stop = 1;
 176                }
 177        }
 178
 179        if (found && &p[12] < e) {
 180
 181                dlen = get_unaligned_be16(p+10);
 182                p += 12;
 183                memcpy(&IPAddress, p, 4);
 184
 185                if (p + dlen <= e) {
 186                        ip_to_string(IPAddress, IPStr);
 187                        printf("%s\n", IPStr);
 188                        if (NetDNSenvvar)
 189                                setenv(NetDNSenvvar, IPStr);
 190                } else
 191                        puts("server responded with invalid IP number\n");
 192        }
 193
 194        NetState = NETLOOP_SUCCESS;
 195}
 196
 197void
 198DnsStart(void)
 199{
 200        debug("%s\n", __func__);
 201
 202        NetSetTimeout(DNS_TIMEOUT, DnsTimeout);
 203        NetSetHandler(DnsHandler);
 204
 205        DnsSend();
 206}
 207