From cd81aa6e3b9181ffdbd8d91770407fa5f9e47aee Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 8 Jun 2010 13:11:40 +0200 Subject: net: add dns support Signed-off-by: Sascha Hauer --- net/Kconfig | 4 + net/Makefile | 1 + net/dns.c | 264 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 net/dns.c (limited to 'net') diff --git a/net/Kconfig b/net/Kconfig index faf2c286c6..ff6e45523d 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -25,4 +25,8 @@ config NET_NETCONSOLE help This option adds support for a simple udp based network console. +config NET_RESOLV + bool + prompt "dns support" + endif diff --git a/net/Makefile b/net/Makefile index e42a484f2e..66dc564dac 100644 --- a/net/Makefile +++ b/net/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_NET) += net.o obj-$(CONFIG_NET_NFS) += nfs.o obj-$(CONFIG_NET_TFTP) += tftp.o obj-$(CONFIG_NET_PING) += ping.o +obj-$(CONFIG_NET_RESOLV)+= dns.o obj-$(CONFIG_NET_NETCONSOLE) += netconsole.o diff --git a/net/dns.c b/net/dns.c new file mode 100644 index 0000000000..1ee270b678 --- /dev/null +++ b/net/dns.c @@ -0,0 +1,264 @@ +/* + * 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. + */ +//#define DEBUG +#include +#include +#include +#include +#include +#include + +#define DNS_PORT 53 + +/* 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 */ +}; + +#define STATE_INIT 0 +#define STATE_DONE 1 + +static struct net_connection *dns_con; +static uint64_t dns_timer_start; +static int dns_state; +static IPaddr_t dns_ip; + +static int dns_send(char *name) +{ + int ret; + struct header *header; + enum dns_query_type qtype = DNS_A_RECORD; + unsigned char *packet = net_udp_get_payload(dns_con); + unsigned char *p, *s, *fullname, *dotptr; + const unsigned char *domain; + + /* Prepare DNS packet header */ + header = (struct header *)packet; + 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; + + domain = getenv("domainname"); + + if (!strchr(name, '.') && domain && *domain) + fullname = asprintf(".%s.%s.", name, domain); + else + fullname = asprintf(".%s.", name); + + /* replace dots in fullname with chunk len */ + dotptr = fullname; + do { + int len; + + s = strchr(dotptr + 1, '.'); + + len = s - dotptr - 1; + + *dotptr = len; + dotptr = s; + } while (*(dotptr + 1)); + *dotptr = 0; +//memory_display(fullname, 0, strlen(fullname), 1); + strcpy(header->data, fullname); + + p = header->data + strlen(fullname); + + *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 */ + + ret = net_udp_send(dns_con, p - packet); + + free(fullname); + + return ret; +} + +static void dns_handler(char *packet, unsigned len) +{ + struct header *header; + unsigned char *p, *e, *s; + u16 type; + int found, stop, dlen; + short tmp; + + debug("%s\n", __func__); + + /* We sent 1 query. We want to see more that 1 answer. */ + header = (struct header *)net_eth_to_udp_payload(packet);; + if (ntohs(header->nqueries) != 1) + return; + + /* Received 0 answers */ + if (header->nanswers == 0) { + dns_state = STATE_DONE; + debug("DNS server returned no answers\n"); + return; + } + + /* Skip host name */ + s = &header->data[0]; + e = packet + 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) { + debug("DNS response was not A record\n"); + 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; + dns_ip = net_read_ip(p); + dns_state = STATE_DONE; + } +} + +IPaddr_t resolv(char *host) +{ + IPaddr_t ip; + + if (!string_to_ip(host, &ip)) + return ip; + + dns_ip = 0; + + dns_state = STATE_INIT; + + ip = getenv_ip("nameserver"); + if (!ip) + return 0; + + debug("resolving host %s via nameserver %s\n", host, getenv("nameserver")); + + dns_con = net_udp_new(ip, DNS_PORT, dns_handler); + if (IS_ERR(dns_con)) + return PTR_ERR(dns_con); + dns_timer_start = get_time_ns(); + dns_send(host); + + while (dns_state != STATE_DONE) { + if (ctrlc()) { + break; + } + net_poll(); + if (is_timeout(dns_timer_start, SECOND)) { + dns_timer_start = get_time_ns(); + printf("T "); + dns_send(host); + } + } + + net_unregister(dns_con); + + return dns_ip; +} + +static int do_host(struct command *cmdtp, int argc, char *argv[]) +{ + IPaddr_t ip; + + if (argc != 2) + return COMMAND_ERROR_USAGE; + + ip = resolv(argv[1]); + if (!ip) + printf("unknown host %s\n", argv[1]); + else { + printf("%s is at ", argv[1]); + print_IPaddr(ip); + printf("\n"); + } + + return 0; +} + +static const __maybe_unused char cmd_host_help[] = +"Usage: host \n"; + +BAREBOX_CMD_START(host) + .cmd = do_host, + .usage = "resolve a hostname", + BAREBOX_CMD_HELP(cmd_host_help) +BAREBOX_CMD_END + -- cgit v1.2.3