summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2010-06-21 00:19:37 -0700
committerH. Peter Anvin <hpa@zytor.com>2010-06-21 00:19:37 -0700
commitee367a7d8215eea0b59a4f672b8fcfb5544c5f5a (patch)
tree2c08a5b3249add90d852e9ddb85cb392c92e7b25
parent2a3c031ec81742c977a8c7116a97bfa1a766443c (diff)
downloadsyslinux-ee367a7d8215eea0b59a4f672b8fcfb5544c5f5a.tar.gz
pxe: fix handling of lost packets in DNS resolution
When we have lost packets in DNS resolution, or otherwise no response, both rotate through the known servers and advance through the timeout table. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--core/fs/pxe/dhcp_option.c73
-rw-r--r--core/fs/pxe/dnsresolv.c80
2 files changed, 75 insertions, 78 deletions
diff --git a/core/fs/pxe/dhcp_option.c b/core/fs/pxe/dhcp_option.c
index ee1a3785..d50b6314 100644
--- a/core/fs/pxe/dhcp_option.c
+++ b/core/fs/pxe/dhcp_option.c
@@ -28,27 +28,22 @@ static void router(void *data, int opt_len)
static void dns_servers(void *data, int opt_len)
{
- int num = opt_len >> 2;
- int i;
+ const uint32_t *dp = data;
+ int num = 0;
- if (num > DNS_MAX_SERVERS)
- num = DNS_MAX_SERVERS;
+ while (num < DNS_MAX_SERVERS) {
+ uint32_t ip;
- for (i = 0; i < num; i++) {
- dns_server[i] = *(uint32_t *)data;
- data += 4;
- }
+ if (opt_len < 4)
+ break;
-#if 0
- /*
- * if you find you got no corret DNS server, you can add
- * it here manually. BUT be carefull the DNS_MAX_SERVERS
- */
- if (i < DNS_MAX_SERVERS ) {
- dns_server[i++] = your_master_dns_server;
- dns_server[i++] = your_second_dns_server;
+ opt_len -= 4;
+ ip = *dp++;
+ if (ip_ok(ip))
+ dns_server[num++] = ip;
}
-#endif
+ while (num < DNS_MAX_SERVERS)
+ dns_server[num++] = 0;
}
static void local_domain(void *data, int opt_len)
@@ -56,7 +51,7 @@ static void local_domain(void *data, int opt_len)
char *p = (char *)data + opt_len;
char *ld = LocalDomain;
char end = *p;
-
+
*p = '\0'; /* Zero-terminate option */
dns_mangle(&ld, data);
*p = end; /* Restore ending byte */
@@ -82,10 +77,10 @@ static void server(void *data, int opt_len)
if (opt_len != 4)
return;
-
+
if (IPInfo.serverip)
return;
-
+
ip = *(uint32_t *)data;
if (ip_ok(ip))
IPInfo.serverip = ip;
@@ -94,7 +89,7 @@ static void server(void *data, int opt_len)
static void client_identifier(void *data, int opt_len)
{
if (opt_len > MAC_MAX || opt_len < 2 ||
- MAC_len != (opt_len >> 8) ||
+ MAC_len != (opt_len >> 8) ||
*(uint8_t *)data != MAC_type)
return;
@@ -109,7 +104,7 @@ static void bootfile_name(void *data, int opt_len)
strncpy(boot_file, data, opt_len);
boot_file[opt_len] = 0;
}
-
+
static void uuid_client_identifier(void *data, int opt_len)
{
int type = *(uint8_t *)data;
@@ -140,7 +135,7 @@ static void pxelinux_reboottime(void *data, int opt_len)
{
if ((opt_len && 0xff) != 4)
return ;
-
+
RebootTime = ntohl(*(uint32_t *)data);
DHCPMagic |= 8; /* Got reboot time */
}
@@ -151,7 +146,7 @@ struct dhcp_options {
void (*fun) (void *, int);
};
-static struct dhcp_options dhcp_opts[] = {
+static struct dhcp_options dhcp_opts[] = {
{1, subnet_mask},
{3, router},
{6, dns_servers},
@@ -168,13 +163,13 @@ static struct dhcp_options dhcp_opts[] = {
};
/*
- * Parse a sequence of DHCP options, pointed to by _option_;
+ * Parse a sequence of DHCP options, pointed to by _option_;
* -- some DHCP servers leave option fields unterminated
* in violation of the spec.
*
* filter contains the minimum value for the option to recognize
* -- this is used to restrict parsing to PXELINUX-specific options only.
- */
+ */
static void parse_dhcp_options(void *option, int size, uint8_t opt_filter)
{
uint8_t opt_num;
@@ -183,7 +178,7 @@ static void parse_dhcp_options(void *option, int size, uint8_t opt_filter)
int i = 0;
char *p = option;
struct dhcp_options *opt;
-
+
while (size--) {
opt_num = *p++;
@@ -193,7 +188,7 @@ static void parse_dhcp_options(void *option, int size, uint8_t opt_filter)
continue;
if (opt_num == 0xff)
break;
-
+
/* Anything else will have a lenght filed */
opt_len = *p++; /* c <- option lenght */
size = size - opt_len - 1;
@@ -209,15 +204,15 @@ static void parse_dhcp_options(void *option, int size, uint8_t opt_filter)
if (opt_num == opt->opt_num) {
opt->fun(p, opt_len);
break;
- }
+ }
opt ++;
}
-
+
/* parse next */
p += opt_len;
}
}
-
+
/*
* parse_dhcp
*
@@ -249,19 +244,19 @@ void parse_dhcp(int pkt_len)
over_load = 0;
if (ip_ok(dhcp->yip))
IPInfo.myip = dhcp->yip;
-
+
if (ip_ok(dhcp->sip))
IPInfo.serverip = dhcp->sip;
-
+
opt_len = (char *)dhcp + pkt_len - (char *)&dhcp->options;
- if (opt_len && (dhcp->option_magic == BOOTP_OPTION_MAGIC))
+ if (opt_len && (dhcp->option_magic == BOOTP_OPTION_MAGIC))
parse_dhcp_options(&dhcp->options, opt_len, 0);
- if (over_load & 1)
+ if (over_load & 1)
parse_dhcp_options(&dhcp->bootfile, 128, 0);
- else if (dhcp->bootfile[0])
+ else if (dhcp->bootfile[0])
strcpy(boot_file, dhcp->bootfile);
-
- if (over_load & 2)
+
+ if (over_load & 2)
parse_dhcp_options(dhcp->sname, 64, 0);
-}
+}
diff --git a/core/fs/pxe/dnsresolv.c b/core/fs/pxe/dnsresolv.c
index 134f24e3..76a905a2 100644
--- a/core/fs/pxe/dnsresolv.c
+++ b/core/fs/pxe/dnsresolv.c
@@ -35,7 +35,7 @@ struct dnsquery {
} __attribute__ ((packed));
/*
- * The DNS Resource recodes structure
+ * The DNS Resource recodes structure
*/
struct dnsrr {
uint16_t type;
@@ -50,7 +50,7 @@ uint32_t dns_server[DNS_MAX_SERVERS] = {0, };
/*
- * Turn a string in _src_ into a DNS "label set" in _dst_; returns the
+ * Turn a string in _src_ into a DNS "label set" in _dst_; returns the
* number of dots encountered. On return, *dst is updated.
*/
int dns_mangle(char **dst, const char *p)
@@ -58,14 +58,14 @@ int dns_mangle(char **dst, const char *p)
char *q = *dst;
char *count_ptr;
char c;
- int dots = 0;
+ int dots = 0;
count_ptr = q;
*q++ = 0;
while (1) {
c = *p++;
- if (c == 0 || c == ':')
+ if (c == 0 || c == ':' || c == '/')
break;
if (c == '.') {
dots++;
@@ -73,7 +73,7 @@ int dns_mangle(char **dst, const char *p)
*q++ = 0;
continue;
}
-
+
*count_ptr += 1;
*q++ = c;
}
@@ -85,7 +85,7 @@ int dns_mangle(char **dst, const char *p)
*dst = q;
return dots;
}
-
+
/*
* Compare two sets of DNS labels, in _s1_ and _s2_; the one in _s2_
@@ -126,7 +126,7 @@ static void *dns_copylabel(void *dst, const void *src, const void *buf)
uint8_t *q = dst;
const uint8_t *p = src;
unsigned int c0, c1;
-
+
while (1) {
c0 = p[0];
if (c0 >= 0xc0) {
@@ -151,7 +151,7 @@ static void *dns_copylabel(void *dst, const void *src, const void *buf)
static char *dns_skiplabel(char *label)
{
uint8_t c;
-
+
while (1) {
c = *label++;
if (c >= 0xc0)
@@ -184,16 +184,20 @@ uint32_t dns_resolv(const char *name)
const uint8_t *timeout_ptr = TimeoutTable;
uint32_t oldtime;
uint32_t srv;
- uint32_t *srv_ptr = dns_server;
+ uint32_t *srv_ptr;
struct dnshdr *hd1 = (struct dnshdr *)DNSSendBuf;
- struct dnshdr *hd2 = (struct dnshdr *)DNSRecvBuf;
+ struct dnshdr *hd2 = (struct dnshdr *)DNSRecvBuf;
struct dnsquery *query;
struct dnsrr *rr;
static __lowmem struct s_PXENV_UDP_WRITE udp_write;
- static __lowmem struct s_PXENV_UDP_READ udp_read;
+ static __lowmem struct s_PXENV_UDP_READ udp_read;
uint16_t local_port;
uint32_t result = 0;
+ /* Make sure we have at least one valid DNS server */
+ if (!dns_server[0])
+ return 0;
+
/* Get a local port number */
local_port = get_port();
@@ -204,29 +208,33 @@ uint32_t dns_resolv(const char *name)
hd1->ancount = 0; /* No answers */
hd1->nscount = 0; /* No NS */
hd1->arcount = 0; /* No AR */
-
+
p = DNSSendBuf + sizeof(struct dnshdr);
dots = dns_mangle(&p, name); /* store the CNAME */
-
+
if (!dots) {
p--; /* Remove final null */
/* Uncompressed DNS label set so it ends in null */
- strcpy(p, LocalDomain);
+ strcpy(p, LocalDomain);
}
-
+
/* Fill the DNS query packet */
query = (struct dnsquery *)p;
query->qtype = htons(TYPE_A);
query->qclass = htons(CLASS_IN);
p += sizeof(struct dnsquery);
-
+
/* Now send it to name server */
timeout_ptr = TimeoutTable;
timeout = *timeout_ptr++;
- while (srv_ptr < dns_server + DNS_MAX_SERVERS) {
- srv = *srv_ptr++;
- if (!srv)
- continue; /* just move on before runing the time out */
+ srv_ptr = dns_server;
+ while (timeout) {
+ srv = *srv_ptr++;
+ if (!srv) {
+ srv_ptr = dns_server;
+ srv = *srv_ptr++;
+ }
+
udp_write.status = 0;
udp_write.ip = srv;
udp_write.gw = gateway(srv);
@@ -235,9 +243,9 @@ uint32_t dns_resolv(const char *name)
udp_write.buffer_size = p - DNSSendBuf;
udp_write.buffer = FAR_PTR(DNSSendBuf);
err = pxe_call(PXENV_UDP_WRITE, &udp_write);
- if (err || udp_write.status != 0)
+ if (err || udp_write.status)
continue;
-
+
oldtime = jiffies();
while (1) {
udp_read.status = 0;
@@ -245,27 +253,21 @@ uint32_t dns_resolv(const char *name)
udp_read.dest_ip = IPInfo.myip;
udp_read.s_port = DNS_PORT;
udp_read.d_port = local_port;
- udp_read.buffer_size = DNS_MAX_PACKET;
+ udp_read.buffer_size = PKTBUF_SIZE;
udp_read.buffer = FAR_PTR(DNSRecvBuf);
err = pxe_call(PXENV_UDP_READ, &udp_read);
if (err || udp_read.status)
continue;
-
+
/* Got a packet, deal with it... */
if (hd2->id == hd1->id)
break;
- if (jiffies()-oldtime >= timeout) {
- /* time out */
- timeout = *timeout_ptr++;
- if (!timeout)
- goto done; /* All time ticks run out */
- else
- goto again;
- }
+ if (jiffies() - oldtime >= timeout)
+ goto again;
}
if ((hd2->flags ^ 0x80) & htons(0xf80f))
- goto badness;
+ goto badness;
ques = htons(hd2->qdcount); /* Questions */
reps = htons(hd2->ancount); /* Replies */
@@ -302,7 +304,7 @@ uint32_t dns_resolv(const char *name)
default:
break;
}
- }
+ }
/* not the one we want, try next */
p += sizeof(struct dnsrr) + rd_len;
@@ -319,13 +321,13 @@ uint32_t dns_resolv(const char *name)
; domain doesn't exist. If this turns out to be a
; problem, we may want to add code to go through all
; the servers before giving up.
-
+
; If the DNS server wasn't capable of recursion, and
; isn't capable of giving us an authoritative reply
; (i.e. neither AA or RA set), then at least try a
; different setver...
*/
- if (hd2->flags == htons(0x480))
+ if (hd2->flags == htons(0x480))
continue;
break; /* failed */
@@ -339,10 +341,10 @@ done:
return result;
}
-
-
+
+
/*
- * the one should be called from ASM file
+ * the one should be called from ASM file
*/
void pxe_dns_resolv(com32sys_t *regs)
{