11a32bf41SRobin Getz /*
21a32bf41SRobin Getz * DNS support driver
31a32bf41SRobin Getz *
41a32bf41SRobin Getz * Copyright (c) 2008 Pieter Voorthuijsen <pieter.voorthuijsen@prodrive.nl>
51a32bf41SRobin Getz * Copyright (c) 2009 Robin Getz <rgetz@blackfin.uclinux.org>
61a32bf41SRobin Getz *
71a32bf41SRobin Getz * This is a simple DNS implementation for U-Boot. It will use the first IP
8049a95a7SJoe Hershberger * in the DNS response as net_server_ip. This can then be used for any other
91a32bf41SRobin Getz * network related activities.
101a32bf41SRobin Getz *
111a32bf41SRobin Getz * The packet handling is partly based on TADNS, original copyrights
121a32bf41SRobin Getz * follow below.
131a32bf41SRobin Getz *
141a32bf41SRobin Getz */
151a32bf41SRobin Getz
161a32bf41SRobin Getz /*
171a32bf41SRobin Getz * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
181a32bf41SRobin Getz *
191a32bf41SRobin Getz * "THE BEER-WARE LICENSE" (Revision 42):
201a32bf41SRobin Getz * Sergey Lyubka wrote this file. As long as you retain this notice you
211a32bf41SRobin Getz * can do whatever you want with this stuff. If we meet some day, and you think
221a32bf41SRobin Getz * this stuff is worth it, you can buy me a beer in return.
231a32bf41SRobin Getz */
241a32bf41SRobin Getz
251a32bf41SRobin Getz #include <common.h>
261a32bf41SRobin Getz #include <command.h>
271a32bf41SRobin Getz #include <net.h>
286dc809f4SBernhard Kaindl #include <asm/unaligned.h>
291a32bf41SRobin Getz
301a32bf41SRobin Getz #include "dns.h"
311a32bf41SRobin Getz
32786eac5fSJoe Hershberger char *net_dns_resolve; /* The host to resolve */
33786eac5fSJoe Hershberger char *net_dns_env_var; /* The envvar to store the answer in */
341a32bf41SRobin Getz
35786eac5fSJoe Hershberger static int dns_our_port;
361a32bf41SRobin Getz
dns_send(void)37786eac5fSJoe Hershberger static void dns_send(void)
381a32bf41SRobin Getz {
391a32bf41SRobin Getz struct header *header;
401a32bf41SRobin Getz int n, name_len;
411a32bf41SRobin Getz uchar *p, *pkt;
421a32bf41SRobin Getz const char *s;
431a32bf41SRobin Getz const char *name;
441a32bf41SRobin Getz enum dns_query_type qtype = DNS_A_RECORD;
451a32bf41SRobin Getz
46786eac5fSJoe Hershberger name = net_dns_resolve;
471203fcceSJoe Hershberger pkt = (uchar *)(net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE);
481203fcceSJoe Hershberger p = pkt;
491a32bf41SRobin Getz
501a32bf41SRobin Getz /* Prepare DNS packet header */
511a32bf41SRobin Getz header = (struct header *)pkt;
521a32bf41SRobin Getz header->tid = 1;
531a32bf41SRobin Getz header->flags = htons(0x100); /* standard query */
541a32bf41SRobin Getz header->nqueries = htons(1); /* Just one query */
551a32bf41SRobin Getz header->nanswers = 0;
561a32bf41SRobin Getz header->nauth = 0;
571a32bf41SRobin Getz header->nother = 0;
581a32bf41SRobin Getz
591a32bf41SRobin Getz /* Encode DNS name */
601a32bf41SRobin Getz name_len = strlen(name);
611a32bf41SRobin Getz p = (uchar *)&header->data; /* For encoding host name into packet */
621a32bf41SRobin Getz
631a32bf41SRobin Getz do {
641a32bf41SRobin Getz s = strchr(name, '.');
651a32bf41SRobin Getz if (!s)
661a32bf41SRobin Getz s = name + name_len;
671a32bf41SRobin Getz
681a32bf41SRobin Getz n = s - name; /* Chunk length */
691a32bf41SRobin Getz *p++ = n; /* Copy length */
701a32bf41SRobin Getz memcpy(p, name, n); /* Copy chunk */
711a32bf41SRobin Getz p += n;
721a32bf41SRobin Getz
731a32bf41SRobin Getz if (*s == '.')
741a32bf41SRobin Getz n++;
751a32bf41SRobin Getz
761a32bf41SRobin Getz name += n;
771a32bf41SRobin Getz name_len -= n;
781a32bf41SRobin Getz } while (*s != '\0');
791a32bf41SRobin Getz
801a32bf41SRobin Getz *p++ = 0; /* Mark end of host name */
811a32bf41SRobin Getz *p++ = 0; /* Some servers require double null */
821a32bf41SRobin Getz *p++ = (unsigned char) qtype; /* Query Type */
831a32bf41SRobin Getz
841a32bf41SRobin Getz *p++ = 0;
851a32bf41SRobin Getz *p++ = 1; /* Class: inet, 0x0001 */
861a32bf41SRobin Getz
871a32bf41SRobin Getz n = p - pkt; /* Total packet length */
881a32bf41SRobin Getz debug("Packet size %d\n", n);
891a32bf41SRobin Getz
90786eac5fSJoe Hershberger dns_our_port = random_port();
911a32bf41SRobin Getz
921203fcceSJoe Hershberger net_send_udp_packet(net_server_ethaddr, net_dns_server,
93786eac5fSJoe Hershberger DNS_SERVICE_PORT, dns_our_port, n);
941a32bf41SRobin Getz debug("DNS packet sent\n");
951a32bf41SRobin Getz }
961a32bf41SRobin Getz
dns_timeout_handler(void)97786eac5fSJoe Hershberger static void dns_timeout_handler(void)
981a32bf41SRobin Getz {
991a32bf41SRobin Getz puts("Timeout\n");
10022f6e99dSJoe Hershberger net_set_state(NETLOOP_FAIL);
1011a32bf41SRobin Getz }
1021a32bf41SRobin Getz
dns_handler(uchar * pkt,unsigned dest,struct in_addr sip,unsigned src,unsigned len)103049a95a7SJoe Hershberger static void dns_handler(uchar *pkt, unsigned dest, struct in_addr sip,
104049a95a7SJoe Hershberger unsigned src, unsigned len)
1051a32bf41SRobin Getz {
1061a32bf41SRobin Getz struct header *header;
1071a32bf41SRobin Getz const unsigned char *p, *e, *s;
1081a32bf41SRobin Getz u16 type, i;
1091a32bf41SRobin Getz int found, stop, dlen;
110786eac5fSJoe Hershberger char ip_str[22];
111049a95a7SJoe Hershberger struct in_addr ip_addr;
1121a32bf41SRobin Getz
1131a32bf41SRobin Getz
1141a32bf41SRobin Getz debug("%s\n", __func__);
115786eac5fSJoe Hershberger if (dest != dns_our_port)
1161a32bf41SRobin Getz return;
1171a32bf41SRobin Getz
1181a32bf41SRobin Getz for (i = 0; i < len; i += 4)
1191a32bf41SRobin Getz debug("0x%p - 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n",
1201a32bf41SRobin Getz pkt+i, pkt[i], pkt[i+1], pkt[i+2], pkt[i+3]);
1211a32bf41SRobin Getz
1226dc809f4SBernhard Kaindl /* We sent one query. We want to have a single answer: */
1231a32bf41SRobin Getz header = (struct header *)pkt;
1241a32bf41SRobin Getz if (ntohs(header->nqueries) != 1)
1251a32bf41SRobin Getz return;
1261a32bf41SRobin Getz
1271a32bf41SRobin Getz /* Received 0 answers */
1281a32bf41SRobin Getz if (header->nanswers == 0) {
1296dc809f4SBernhard Kaindl puts("DNS: host not found\n");
13022f6e99dSJoe Hershberger net_set_state(NETLOOP_SUCCESS);
1311a32bf41SRobin Getz return;
1321a32bf41SRobin Getz }
1331a32bf41SRobin Getz
1341a32bf41SRobin Getz /* Skip host name */
1351a32bf41SRobin Getz s = &header->data[0];
1361a32bf41SRobin Getz e = pkt + len;
1371a32bf41SRobin Getz for (p = s; p < e && *p != '\0'; p++)
1381a32bf41SRobin Getz continue;
1391a32bf41SRobin Getz
1401a32bf41SRobin Getz /* We sent query class 1, query type 1 */
1416dc809f4SBernhard Kaindl if (&p[5] > e || get_unaligned_be16(p+1) != DNS_A_RECORD) {
1426dc809f4SBernhard Kaindl puts("DNS: response was not an A record\n");
14322f6e99dSJoe Hershberger net_set_state(NETLOOP_SUCCESS);
1441a32bf41SRobin Getz return;
1451a32bf41SRobin Getz }
1461a32bf41SRobin Getz
1471a32bf41SRobin Getz /* Go to the first answer section */
1481a32bf41SRobin Getz p += 5;
1491a32bf41SRobin Getz
1501a32bf41SRobin Getz /* Loop through the answers, we want A type answer */
1511a32bf41SRobin Getz for (found = stop = 0; !stop && &p[12] < e; ) {
1521a32bf41SRobin Getz /* Skip possible name in CNAME answer */
1531a32bf41SRobin Getz if (*p != 0xc0) {
1541a32bf41SRobin Getz while (*p && &p[12] < e)
1551a32bf41SRobin Getz p++;
1561a32bf41SRobin Getz p--;
1571a32bf41SRobin Getz }
1581a32bf41SRobin Getz debug("Name (Offset in header): %d\n", p[1]);
1591a32bf41SRobin Getz
1606dc809f4SBernhard Kaindl type = get_unaligned_be16(p+2);
1611a32bf41SRobin Getz debug("type = %d\n", type);
1621a32bf41SRobin Getz if (type == DNS_CNAME_RECORD) {
1631a32bf41SRobin Getz /* CNAME answer. shift to the next section */
1641a32bf41SRobin Getz debug("Found canonical name\n");
1656dc809f4SBernhard Kaindl dlen = get_unaligned_be16(p+10);
1661a32bf41SRobin Getz debug("dlen = %d\n", dlen);
1671a32bf41SRobin Getz p += 12 + dlen;
1681a32bf41SRobin Getz } else if (type == DNS_A_RECORD) {
1691a32bf41SRobin Getz debug("Found A-record\n");
170786eac5fSJoe Hershberger found = 1;
171786eac5fSJoe Hershberger stop = 1;
1721a32bf41SRobin Getz } else {
1731a32bf41SRobin Getz debug("Unknown type\n");
1741a32bf41SRobin Getz stop = 1;
1751a32bf41SRobin Getz }
1761a32bf41SRobin Getz }
1771a32bf41SRobin Getz
1781a32bf41SRobin Getz if (found && &p[12] < e) {
1796dc809f4SBernhard Kaindl dlen = get_unaligned_be16(p+10);
1801a32bf41SRobin Getz p += 12;
181049a95a7SJoe Hershberger memcpy(&ip_addr, p, 4);
1821a32bf41SRobin Getz
1831a32bf41SRobin Getz if (p + dlen <= e) {
184786eac5fSJoe Hershberger ip_to_string(ip_addr, ip_str);
185786eac5fSJoe Hershberger printf("%s\n", ip_str);
186786eac5fSJoe Hershberger if (net_dns_env_var)
187*382bee57SSimon Glass env_set(net_dns_env_var, ip_str);
188786eac5fSJoe Hershberger } else {
1891a32bf41SRobin Getz puts("server responded with invalid IP number\n");
1901a32bf41SRobin Getz }
191786eac5fSJoe Hershberger }
1921a32bf41SRobin Getz
19322f6e99dSJoe Hershberger net_set_state(NETLOOP_SUCCESS);
1941a32bf41SRobin Getz }
1951a32bf41SRobin Getz
dns_start(void)196786eac5fSJoe Hershberger void dns_start(void)
1971a32bf41SRobin Getz {
1981a32bf41SRobin Getz debug("%s\n", __func__);
1991a32bf41SRobin Getz
200bc0571fcSJoe Hershberger net_set_timeout_handler(DNS_TIMEOUT, dns_timeout_handler);
201049a95a7SJoe Hershberger net_set_udp_handler(dns_handler);
2021a32bf41SRobin Getz
203f395e75eSGerhard Sittig /* Clear a previous MAC address, the server IP might have changed. */
2040adb5b76SJoe Hershberger memset(net_server_ethaddr, 0, sizeof(net_server_ethaddr));
205f395e75eSGerhard Sittig
206786eac5fSJoe Hershberger dns_send();
2071a32bf41SRobin Getz }
208