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 <rgetz@blackfin.uclinux.org>
Signed-off-by: Pieter Voorthuijsen <pieter.voorthuijsen@prodrive.nl>
Signed-off-by: Ben Warren <biggerbadderben@gmail.com>
This commit is contained in:
Robin Getz 2009-07-20 14:53:54 -04:00 committed by Ben Warren
parent 88ad3fd91c
commit 1a32bf4188
7 changed files with 401 additions and 3 deletions

View file

@ -353,3 +353,52 @@ U_BOOT_CMD(
"[NTP server IP]\n"
);
#endif
#if defined(CONFIG_CMD_DNS)
int do_dns(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
if (argc == 1) {
cmd_usage(cmdtp);
return -1;
}
/*
* We should check for a valid hostname:
* - Each label must be between 1 and 63 characters long
* - the entire hostname has a maximum of 255 characters
* - only the ASCII letters 'a' through 'z' (case-insensitive),
* the digits '0' through '9', and the hyphen
* - cannot begin or end with a hyphen
* - no other symbols, punctuation characters, or blank spaces are
* permitted
* but hey - this is a minimalist implmentation, so only check length
* and let the name server deal with things.
*/
if (strlen(argv[1]) >= 255) {
printf("dns error: hostname too long\n");
return 1;
}
NetDNSResolve = argv[1];
if (argc == 3)
NetDNSenvvar = argv[2];
else
NetDNSenvvar = NULL;
if (NetLoop(DNS) < 0) {
printf("dns lookup of %s failed, check setup\n", argv[1]);
return 1;
}
return 0;
}
U_BOOT_CMD(
dns, 3, 1, do_dns,
"lookup the IP of a hostname",
"hostname [envvar]"
);
#endif /* CONFIG_CMD_DNS */

64
doc/README.dns Normal file
View file

@ -0,0 +1,64 @@
Domain Name System
-------------------------------------------
The Domain Name System (DNS) is a hierarchical naming system for computers,
services, or any resource participating in the Internet. It associates various
information with domain names assigned to each of the participants. Most
importantly, it translates domain names meaningful to humans into the numerical
(binary) identifiers associated with networking equipment for the purpose of
locating and addressing these devices world-wide. An often used analogy to
explain the Domain Name System is that it serves as the "phone book" for the
Internet by translating human-friendly computer hostnames into IP addresses.
For example, www.example.com translates to 208.77.188.166.
For more information on DNS - http://en.wikipedia.org/wiki/Domain_Name_System
U-Boot and DNS
------------------------------------------
CONFIG_CMD_DNS - controls if the 'dns' command is compiled in. If it is, it
will send name lookups to the dns server (env var 'dnsip')
Turning this option on will about abou 1k to U-Boot's size.
Example:
bfin> print dnsip
dnsip=192.168.0.1
bfin> dns www.google.com
66.102.1.104
By default, dns does nothing except print the IP number on
the default console - which by itself, would be pretty
useless. Adding a third argument to the dns command will
use that as the environment variable to be set.
Example:
bfin> print googleip
## Error: "googleip" not defined
bfin> dns www.google.com googleip
64.233.161.104
bfin> print googleip
googleip=64.233.161.104
bfin> ping ${googleip}
Using Blackfin EMAC device
host 64.233.161.104 is alive
In this way, you can lookup, and set many more meaningful
things.
bfin> sntp
ntpserverip not set
bfin> dns pool.ntp.org ntpserverip
72.18.205.156
bfin> sntp
Date: 2009-07-18 Time: 4:06:57
For some helpful things that can be related to DNS in U-Boot,
look at the top level README for these config options:
CONFIG_CMD_DHCP
CONFIG_BOOTP_DNS
CONFIG_BOOTP_DNS2

View file

@ -361,6 +361,11 @@ typedef enum { BOOTP, RARP, ARP, TFTP, DHCP, PING, DNS, NFS, CDP, NETCONS, SNTP
/* from net/net.c */
extern char BootFile[128]; /* Boot File name */
#if defined(CONFIG_CMD_DNS)
extern char *NetDNSResolve; /* The host to resolve */
extern char *NetDNSenvvar; /* the env var to put the ip into */
#endif
#if defined(CONFIG_CMD_PING)
extern IPaddr_t NetPingIP; /* the ip address to ping */
#endif

View file

@ -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)

211
net/dns.c Normal file
View file

@ -0,0 +1,211 @@
/*
* 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.
*/
#include <common.h>
#include <command.h>
#include <net.h>
#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();
}

39
net/dns.h Normal file
View file

@ -0,0 +1,39 @@
/*
* (C) Masami Komiya <mkomiya@sonare.it> 2005
* Copyright 2009, Robin Getz <rgetz@blackfin.uclinux.org>
*
* 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

View file

@ -92,6 +92,9 @@
#if defined(CONFIG_CDP_VERSION)
#include <timestamp.h>
#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);