summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2010-06-08 13:11:40 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2010-06-17 08:28:29 +0200
commitcd81aa6e3b9181ffdbd8d91770407fa5f9e47aee (patch)
tree78e1ce4660dd8c43a05034e0af621bcd5abe53af /net
parent232b46996c039e3a01bb8774fb9148616d94d8e9 (diff)
downloadbarebox-cd81aa6e3b9181ffdbd8d91770407fa5f9e47aee.tar.gz
barebox-cd81aa6e3b9181ffdbd8d91770407fa5f9e47aee.tar.xz
net: add dns support
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'net')
-rw-r--r--net/Kconfig4
-rw-r--r--net/Makefile1
-rw-r--r--net/dns.c264
3 files changed, 269 insertions, 0 deletions
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 <pieter.voorthuijsen@prodrive.nl>
+ * Copyright (c) 2009 Robin Getz <rgetz@blackfin.uclinux.org>
+ *
+ * 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 <valenok@gmail.com>
+ *
+ * "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 <common.h>
+#include <command.h>
+#include <net.h>
+#include <clock.h>
+#include <environment.h>
+#include <linux/err.h>
+
+#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 <hostname>\n";
+
+BAREBOX_CMD_START(host)
+ .cmd = do_host,
+ .usage = "resolve a hostname",
+ BAREBOX_CMD_HELP(cmd_host_help)
+BAREBOX_CMD_END
+