pxe: fix handling of lost packets in DNS resolution
authorH. Peter Anvin <hpa@zytor.com>
Mon, 21 Jun 2010 07:19:37 +0000 (00:19 -0700)
committerH. Peter Anvin <hpa@zytor.com>
Mon, 21 Jun 2010 07:19:37 +0000 (00:19 -0700)
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>
core/fs/pxe/dhcp_option.c
core/fs/pxe/dnsresolv.c

index ee1a378..d50b631 100644 (file)
@@ -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);
-}  
+}
index 134f24e..76a905a 100644 (file)
@@ -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)
 {