From 1a32bf41881b5dbe3119cb77a33572b4d462cabf Mon Sep 17 00:00:00 2001 From: Robin Getz Date: Mon, 20 Jul 2009 14:53:54 -0400 Subject: Add DNS support On 04 Oct 2008 Pieter posted a dns implementation for U-Boot. http://www.mail-archive.com/u-boot-users@lists.sourceforge.net/msg10216.html > > DNS can be enabled by setting CFG_CMD_DNS. After performing a query, > the serverip environment var is updated. > > Probably there are some cosmetic issues with the patch. Unfortunatly I > do not have the time to correct these. So if anybody else likes DNS > support in U-Boot and has the time, feel free to patch it in the main tree. Here it is again - slightly modified & smaller: - update to 2009-06 (Pieter's patch was for U-Boot 1.2.0) - README.dns is added - syntax is changed (now takes a third option, the env var to store the result in) - add a random port() function in net.c - sort Makefile in ./net/Makefile - dns just returns unless a env var is given - run through checkpatch, and clean up style issues - remove packet from stack - cleaned up some comments - failure returns much faster (if server responds, don't wait for timeout) - use built in functions (memcpy) rather than byte copy. Signed-off-by: Robin Getz Signed-off-by: Pieter Voorthuijsen Signed-off-by: Ben Warren --- net/Makefile | 7 +- net/dns.c | 211 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ net/dns.h | 39 +++++++++++ net/net.c | 29 ++++++++ 4 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 net/dns.c create mode 100644 net/dns.h (limited to 'net') diff --git a/net/Makefile b/net/Makefile index d34187425..835a04af4 100644 --- a/net/Makefile +++ b/net/Makefile @@ -27,13 +27,14 @@ include $(TOPDIR)/config.mk LIB = $(obj)libnet.a -COBJS-y += net.o -COBJS-y += tftp.o COBJS-y += bootp.o -COBJS-y += rarp.o +COBJS-$(CONFIG_CMD_DNS) += dns.o COBJS-y += eth.o +COBJS-y += net.o COBJS-y += nfs.o +COBJS-y += rarp.o COBJS-$(CONFIG_CMD_SNTP) += sntp.o +COBJS-y += tftp.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/net/dns.c b/net/dns.c new file mode 100644 index 000000000..f25c3f8c2 --- /dev/null +++ b/net/dns.c @@ -0,0 +1,211 @@ +/* + * DNS support driver + * + * Copyright (c) 2008 Pieter Voorthuijsen + * Copyright (c) 2009 Robin Getz + * + * This is a simple DNS implementation for U-Boot. It will use the first IP + * in the DNS response as NetServerIP. This can then be used for any other + * network related activities. + * + * The packet handling is partly based on TADNS, original copyrights + * follow below. + * + */ + +/* + * Copyright (c) 2004-2005 Sergey Lyubka + * + * "THE BEER-WARE LICENSE" (Revision 42): + * Sergey Lyubka wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. + */ + +#include +#include +#include + +#include "dns.h" + +char *NetDNSResolve; /* The host to resolve */ +char *NetDNSenvvar; /* The envvar to store the answer in */ + +static int DnsOurPort; + +static void +DnsSend(void) +{ + struct header *header; + int n, name_len; + uchar *p, *pkt; + const char *s; + const char *name; + enum dns_query_type qtype = DNS_A_RECORD; + + name = NetDNSResolve; + pkt = p = (uchar *)(NetTxPacket + NetEthHdrSize() + IP_HDR_SIZE); + + /* Prepare DNS packet header */ + header = (struct header *) pkt; + header->tid = 1; + header->flags = htons(0x100); /* standard query */ + header->nqueries = htons(1); /* Just one query */ + header->nanswers = 0; + header->nauth = 0; + header->nother = 0; + + /* Encode DNS name */ + name_len = strlen(name); + p = (uchar *) &header->data; /* For encoding host name into packet */ + + do { + s = strchr(name, '.'); + if (!s) + s = name + name_len; + + n = s - name; /* Chunk length */ + *p++ = n; /* Copy length */ + memcpy(p, name, n); /* Copy chunk */ + p += n; + + if (*s == '.') + n++; + + name += n; + name_len -= n; + } while (*s != '\0'); + + *p++ = 0; /* Mark end of host name */ + *p++ = 0; /* Some servers require double null */ + *p++ = (unsigned char) qtype; /* Query Type */ + + *p++ = 0; + *p++ = 1; /* Class: inet, 0x0001 */ + + n = p - pkt; /* Total packet length */ + debug("Packet size %d\n", n); + + DnsOurPort = random_port(); + + NetSendUDPPacket(NetServerEther, NetOurDNSIP, DNS_SERVICE_PORT, + DnsOurPort, n); + debug("DNS packet sent\n"); +} + +static void +DnsTimeout(void) +{ + puts("Timeout\n"); + NetState = NETLOOP_FAIL; +} + +static void +DnsHandler(uchar *pkt, unsigned dest, unsigned src, unsigned len) +{ + struct header *header; + const unsigned char *p, *e, *s; + u16 type, i; + int found, stop, dlen; + char IPStr[22]; + IPaddr_t IPAddress; + short tmp; + + + debug("%s\n", __func__); + if (dest != DnsOurPort) + return; + + for (i = 0; i < len; i += 4) + debug("0x%p - 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n", + pkt+i, pkt[i], pkt[i+1], pkt[i+2], pkt[i+3]); + + /* We sent 1 query. We want to see more that 1 answer. */ + header = (struct header *) pkt; + if (ntohs(header->nqueries) != 1) + return; + + /* Received 0 answers */ + if (header->nanswers == 0) { + puts("DNS server returned no answers\n"); + NetState = NETLOOP_SUCCESS; + return; + } + + /* Skip host name */ + s = &header->data[0]; + e = pkt + len; + for (p = s; p < e && *p != '\0'; p++) + continue; + + /* We sent query class 1, query type 1 */ + tmp = p[1] | (p[2] << 8); + if (&p[5] > e || ntohs(tmp) != DNS_A_RECORD) { + puts("DNS response was not A record\n"); + NetState = NETLOOP_SUCCESS; + return; + } + + /* Go to the first answer section */ + p += 5; + + /* Loop through the answers, we want A type answer */ + for (found = stop = 0; !stop && &p[12] < e; ) { + + /* Skip possible name in CNAME answer */ + if (*p != 0xc0) { + while (*p && &p[12] < e) + p++; + p--; + } + debug("Name (Offset in header): %d\n", p[1]); + + tmp = p[2] | (p[3] << 8); + type = ntohs(tmp); + debug("type = %d\n", type); + if (type == DNS_CNAME_RECORD) { + /* CNAME answer. shift to the next section */ + debug("Found canonical name\n"); + tmp = p[10] | (p[11] << 8); + dlen = ntohs(tmp); + debug("dlen = %d\n", dlen); + p += 12 + dlen; + } else if (type == DNS_A_RECORD) { + debug("Found A-record\n"); + found = stop = 1; + } else { + debug("Unknown type\n"); + stop = 1; + } + } + + if (found && &p[12] < e) { + + tmp = p[10] | (p[11] << 8); + dlen = ntohs(tmp); + p += 12; + memcpy(&IPAddress, p, 4); + + if (p + dlen <= e) { + ip_to_string(IPAddress, IPStr); + printf("%s\n", IPStr); + if (NetDNSenvvar) + setenv(NetDNSenvvar, IPStr); + } else + puts("server responded with invalid IP number\n"); + } + + NetState = NETLOOP_SUCCESS; +} + +void +DnsStart(void) +{ + debug("%s\n", __func__); + + NetSetTimeout(DNS_TIMEOUT, DnsTimeout); + NetSetHandler(DnsHandler); + + DnsSend(); +} + diff --git a/net/dns.h b/net/dns.h new file mode 100644 index 000000000..277c093ed --- /dev/null +++ b/net/dns.h @@ -0,0 +1,39 @@ +/* + * (C) Masami Komiya 2005 + * Copyright 2009, Robin Getz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + */ + +#ifndef __DNS_H__ +#define __DNS_H__ + +#define DNS_SERVICE_PORT 53 +#define DNS_TIMEOUT 10000UL + +/* http://en.wikipedia.org/wiki/List_of_DNS_record_types */ +enum dns_query_type { + DNS_A_RECORD = 0x01, + DNS_CNAME_RECORD = 0x05, + DNS_MX_RECORD = 0x0f, +}; + +/* + * DNS network packet + */ +struct header { + uint16_t tid; /* Transaction ID */ + uint16_t flags; /* Flags */ + uint16_t nqueries; /* Questions */ + uint16_t nanswers; /* Answers */ + uint16_t nauth; /* Authority PRs */ + uint16_t nother; /* Other PRs */ + unsigned char data[1]; /* Data, variable length */ +}; + +extern void DnsStart(void); /* Begin DNS */ + +#endif diff --git a/net/net.c b/net/net.c index e215fd8d9..4bbe5313c 100644 --- a/net/net.c +++ b/net/net.c @@ -92,6 +92,9 @@ #if defined(CONFIG_CDP_VERSION) #include #endif +#if defined(CONFIG_CMD_DNS) +#include "dns.h" +#endif #if defined(CONFIG_CMD_NET) @@ -291,6 +294,9 @@ NetInitLoop(proto_t protocol) NetServerIP = getenv_IPaddr ("serverip"); NetOurNativeVLAN = getenv_VLAN("nvlan"); NetOurVLAN = getenv_VLAN("vlan"); +#if defined(CONFIG_CMD_DNS) + NetOurDNSIP = getenv_IPaddr("dnsip"); +#endif env_changed_id = env_id; } @@ -425,6 +431,11 @@ restart: case SNTP: SntpStart(); break; +#endif +#if defined(CONFIG_CMD_DNS) + case DNS: + DnsStart(); + break; #endif default: break; @@ -1518,6 +1529,14 @@ static int net_check_prereq (proto_t protocol) } goto common; #endif +#if defined(CONFIG_CMD_DNS) + case DNS: + if (NetOurDNSIP == 0) { + puts("*** ERROR: DNS server address not given\n"); + return 1; + } + goto common; +#endif #if defined(CONFIG_CMD_NFS) case NFS: #endif @@ -1681,6 +1700,16 @@ void copy_filename (char *dst, char *src, int size) #endif +#if defined(CONFIG_CMD_NFS) || defined(CONFIG_CMD_SNTP) || defined(CONFIG_CMD_DNS) +/* + * make port a little random, but use something trivial to compute + */ +unsigned int random_port(void) +{ + return 1024 + (get_timer(0) % 0x8000);; +} +#endif + void ip_to_string (IPaddr_t x, char *s) { x = ntohl (x); -- cgit v1.2.3