/* * 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