[syslinux:master] pxe: fix handling of lost packets in DNS resolution

syslinux-bot for H. Peter Anvin hpa at zytor.com
Mon Jun 21 00:21:02 PDT 2010


Commit-ID:  ee367a7d8215eea0b59a4f672b8fcfb5544c5f5a
Gitweb:     http://syslinux.zytor.com/commit/ee367a7d8215eea0b59a4f672b8fcfb5544c5f5a
Author:     H. Peter Anvin <hpa at zytor.com>
AuthorDate: Mon, 21 Jun 2010 00:19:37 -0700
Committer:  H. Peter Anvin <hpa at zytor.com>
CommitDate: Mon, 21 Jun 2010 00:19:37 -0700

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 at zytor.com>


---
 core/fs/pxe/dhcp_option.c |   73 +++++++++++++++++++----------------------
 core/fs/pxe/dnsresolv.c   |   80 +++++++++++++++++++++++----------------------
 2 files changed, 75 insertions(+), 78 deletions(-)

diff --git a/core/fs/pxe/dhcp_option.c b/core/fs/pxe/dhcp_option.c
index ee1a378..d50b631 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 134f24e..76a905a 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)
 {



More information about the Syslinux-commits mailing list