diff packages/net/common/current/src/tftp_client.c @ 957:db3e0a2fda2f

2003-04-12 Andrew Lunn <andrew.lunn@ascom.ch> * src/network_support.c (init_loopback_interface): Close the socket when things go wrong otherwise we leak sockets. * src/tftp_server.c (tftpd_server): Added support for IPv6 as well as IPv4. Extended the multithreading support so that it works correctly when there are multiple servers on multiple ports. * doc/tcpip.sgml: Documentation for the changes made to the tftp server. 2003-04-10 Andrew Lunn <andrew.lunn@ascom.ch> * src/network_support.c (init_all_network_interfaces): Wait upto 4 seconds for a router solicit message to be received. Once we have received the message we know we have a valid IPv6 address. * src/tftp_client.c: Added support for IPv6. This requires two new functions, tftp_client_{get|put} which are protocol version independent. * tests/tftp_client_test.c (tftp_test): Added tests which use IPv6 addresses. 2003-04-07 Andrew Lunn <andrew.lunn@ascom.ch> * src/getaddrinfo.c (getaddrinfo): Correctly deal with node when its not NULL. Its OK for the address to not parse for an address family when AF_UNSPEC is passed in hints. Also get the socktype correct when wildcarding for services that use UDP. * tests/addr_test.c: Added more test cases which test IP addresses in number format as node. 2003-04-05 Andrew Lunn <andrew.lunn@ascom.ch> * cdl/net.cdl: Added addr_tests to the HW tests. * tests/addr_test.c (net_test): void function not int. * tests/ping_test.c (net_test): Added IPv6 ping test. This pings the router which answers our router solicit message. * src/ipv6_routing_thread.c (cyg_rs): Print out the router advertisement message. Cleaned up the debug messages so they can be disabled. * src/ipv6_routing_thread.c (cyg_net_get_ipv6_advrouter): New function to return the address of the router. * include/network.h: Added prototype for above and ipv6_start_routing_thread which did not have a prototype. * src/ipv6_routing_thread.c (cyg_rs): Only wait 2 seconds before sending the first solicit request rather than 10. * src/dhcp_prot.c (do_dhcp_down_net): clear the if_laddrreq before using it. If the request fails finish the IPv4 code rather than returning,. 2003-04-02 Andrew Lunn <andrew.lunn@ascom.ch> * tests/ping_lo_test.c: Added IPv6 ping test. * src/getproto.c: Added the protocol ipv6-icmp.
author asl
date Wed, 23 Apr 2003 08:52:08 +0000
parents a440f07b6d29
children 16d247bb9c70
line wrap: on
line diff
--- a/packages/net/common/current/src/tftp_client.c
+++ b/packages/net/common/current/src/tftp_client.c
@@ -9,6 +9,7 @@
 // -------------------------------------------
 // This file is part of eCos, the Embedded Configurable Operating System.
 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+// Copyright (C) 2003 Andrew.Lunn@ascom.ch
 //
 // eCos 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
@@ -41,7 +42,7 @@
 //#####DESCRIPTIONBEGIN####
 //
 // Author(s):    gthomas
-// Contributors: gthomas
+// Contributors: gthomas, andrew.lunn@ascom.ch
 // Date:         2000-04-06
 // Purpose:      
 // Description:  
@@ -63,25 +64,31 @@
 // Read a file from a host into a local buffer.  Returns the
 // number of bytes actually read, or (-1) if an error occurs.
 // On error, *err will hold the reason.
-//
-int
-tftp_get(char *filename,
-         struct sockaddr_in *server,
-         char *buf,
-         int len,
-         int mode,
-         int *err)
-{
-    int res = 0;
-    int s, actual_len, data_len, recv_len, from_len;
+// This version uses the server name. This can be a name for DNS lookup
+// or a dotty or colony number format for IPv4 or IPv6.
+int tftp_client_get(char *filename,
+		    char *server,
+		    int port,
+		    char *buf,
+		    int len,
+		    int mode,
+		    int *err) {
+		    
+    int result = 0;
+    int s=-1;
+    int actual_len, data_len, recv_len, from_len;
     static int get_port = 7700;
-    struct sockaddr_in local_addr, server_addr, from_addr;
+    struct addrinfo * addrinfo;
+    struct addrinfo * res;
+    struct addrinfo hints;
+    int error;
+
+    struct sockaddr local_addr, from_addr;
     char data[SEGSIZE+sizeof(struct tftphdr)];
     struct tftphdr *hdr = (struct tftphdr *)data;
     char *cp, *fp;
     struct timeval timeout;
     unsigned short last_good_block = 0;
-    struct servent *server_info;
     fd_set fds;
     int total_timeouts = 0;
 
@@ -103,130 +110,188 @@ tftp_get(char *filename,
     }
     while (*fp) *cp++ = *fp++;
     *cp++ = '\0';
-    server_info = getservbyname("tftp", "udp");
-    if (server_info == (struct servent *)0) {
-        *err = TFTP_NETERR;
-        return -1;
-    }
 
-    s = socket(AF_INET, SOCK_DGRAM, 0);
-    if (s < 0) {
-        // Couldn't open a communications channel
-        *err = TFTP_NETERR;
-        return -1;
+    memset(&hints,0,sizeof(hints));
+    hints.ai_family = PF_UNSPEC;
+    error = getaddrinfo(server, "tftp", &hints, &res);
+    if (error) {
+      *err = TFTP_NETERR;
+      return -1;
     }
-    memset((char *)&local_addr, 0, sizeof(local_addr));
-    local_addr.sin_family = AF_INET;
-    local_addr.sin_len = sizeof(local_addr);
-    local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
-    local_addr.sin_port = htons(get_port++);
-    if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
-        // Problem setting up my end
-        *err = TFTP_NETERR;
-        close(s);
-        return -1;
-    }
-    memset((char *)&server_addr, 0, sizeof(server_addr));
-    server_addr.sin_family = AF_INET;
-    server_addr.sin_len = sizeof(server_addr);
-    server_addr.sin_addr = server->sin_addr;
-    if (server->sin_port == 0) {
-        server_addr.sin_port = server_info->s_port; // Network order already
-    } else {
-        server_addr.sin_port = server->sin_port;
-    }
+    
+    addrinfo = res;
+    while (addrinfo) {
+      s = socket(addrinfo->ai_family, addrinfo->ai_socktype, 
+		 addrinfo->ai_protocol);
+      if (s >= 0) {
+	memcpy(&local_addr,addrinfo->ai_addr,addrinfo->ai_addrlen);
+	switch(addrinfo->ai_addr->sa_family) {
+	case AF_INET: {
+	  struct sockaddr_in * saddr = 
+	    (struct sockaddr_in *) addrinfo->ai_addr;
+	  struct sockaddr_in * laddr = 
+	    (struct sockaddr_in *) &local_addr;
+	  if (port) {
+	    saddr->sin_port = htons(port);
+	  }
+	  laddr->sin_port = htons(get_port++);
+	  laddr->sin_addr.s_addr = INADDR_ANY;
+	  break;
+	}
+#ifdef CYGPKG_NET_INET6
+	case AF_INET6: {
+	  struct sockaddr_in6 * saddr = 
+	    (struct sockaddr_in6 *) addrinfo->ai_addr;
+	  struct sockaddr_in6 * laddr = 
+	    (struct sockaddr_in6 *) &local_addr;
+	  if (port) {
+	    saddr->sin6_port = htons(port);
+	  }
+	  laddr->sin6_port = htons(get_port++);
+	  laddr->sin6_addr = in6addr_any;
+	  break;
+	}
+#endif
+	default:
+	  *err = TFTP_NETERR;
+	  goto out;
+	}
 
-    // Send request
-    if (sendto(s, data, (int)(cp - data), 0, 
-               (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
-        // Problem sending request
-        *err = TFTP_NETERR;
-        close(s);
-        return -1;
-    }
+	if (bind(s,&local_addr,addrinfo->ai_addrlen) < 0) {
+	  *err = TFTP_NETERR;
+	  goto out;
+	}
+	
+	// Send request
+	if (sendto(s, data, (int)(cp-data), 0, 
+		   addrinfo->ai_addr, 
+		   addrinfo->ai_addrlen) < 0) {
+	  // Problem sending request
+	  *err = TFTP_NETERR;
+	  goto nextaddr;
+	}
 
-    // Read data
-    fp = buf;
-    while (true) {
-        timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
-        timeout.tv_usec = 0;
-        FD_ZERO(&fds);
-        FD_SET(s, &fds);
-        if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
-            if ((++total_timeouts > TFTP_TIMEOUT_MAX) || (last_good_block == 0)) {
-                // Timeout - no data received
-                *err = TFTP_TIMEOUT;
-                close(s);
-                return -1;
-            }
-            // Try resending last ACK
-            hdr->th_opcode = htons(ACK);
-            hdr->th_block = htons(last_good_block);
-            if (sendto(s, data, 4 /* FIXME */, 0, 
-                       (struct sockaddr *)&from_addr, from_len) < 0) {
-                // Problem sending request
-                *err = TFTP_NETERR;
-                close(s);
-                return -1;
-            }
-        } else {
-            recv_len = sizeof(data);
-            from_len = sizeof(from_addr);
-            if ((data_len = recvfrom(s, &data, recv_len, 0, 
-                                     (struct sockaddr *)&from_addr, &from_len)) < 0) {
-                // What happened?
-                *err = TFTP_NETERR;
-                close(s);
-                return -1;
-            }
-            if (ntohs(hdr->th_opcode) == DATA) {
-                actual_len = 0;
-                if (ntohs(hdr->th_block) == (last_good_block+1)) {
-                    // Consume this data
-                    cp = hdr->th_data;
-                    data_len -= 4;  /* Sizeof TFTP header */
-                    actual_len = data_len;
-                    res += actual_len;
-                    while (data_len-- > 0) {
-                        if (len-- > 0) {
-                            *fp++ = *cp++;
-                        } else {
-                            // Buffer overflow
-                            *err = TFTP_TOOLARGE;
-                            close(s);
-                            return -1;
-                        }
-                    }
-                    last_good_block++;
-                }
-                // Send out the ACK
-                hdr->th_opcode = htons(ACK);
-                hdr->th_block = htons(last_good_block);
-                if (sendto(s, data, 4 /* FIXME */, 0, 
-                           (struct sockaddr *)&from_addr, from_len) < 0) {
-                    // Problem sending request
-                    *err = TFTP_NETERR;
-                    close(s);
-                    return -1;
-                }
-                if ((actual_len >= 0) && (actual_len < SEGSIZE)) {
-                    // End of data
-                    close(s);
-                    return res;
-                }
-            } else 
-            if (ntohs(hdr->th_opcode) == ERROR) {
-                *err = ntohs(hdr->th_code);
-                close(s);
-                return -1;
-            } else {
-                // What kind of packet is this?
+	// Read data
+	fp = buf;
+	while (true) {
+	  timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
+	  timeout.tv_usec = 0;
+	  FD_ZERO(&fds);
+	  FD_SET(s, &fds);
+	  if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
+	    if ((++total_timeouts > TFTP_TIMEOUT_MAX) 
+		|| (last_good_block == 0)) {
+	      // Timeout - no data received
+	      *err = TFTP_TIMEOUT;
+	      goto out;
+	    }
+	    // Try resending last ACK
+	    hdr->th_opcode = htons(ACK);
+	    hdr->th_block = htons(last_good_block);
+	    if (sendto(s, data, 4 /* FIXME */, 0, 
+		       &from_addr, from_len) < 0) {
+	      // Problem sending request
+	      *err = TFTP_NETERR;
+	      goto out;
+	    }
+	  } else {
+	    recv_len = sizeof(data);
+	    from_len = sizeof(from_addr);
+	    if ((data_len = recvfrom(s, &data, recv_len, 0, 
+				     &from_addr, &from_len)) < 0) {
+	      // What happened?
+	      *err = TFTP_NETERR;
+	      goto out;
+	    }
+	    if (ntohs(hdr->th_opcode) == DATA) {
+	      actual_len = 0;
+	      if (ntohs(hdr->th_block) == (last_good_block+1)) {
+		// Consume this data
+		cp = hdr->th_data;
+		data_len -= 4;  /* Sizeof TFTP header */
+		actual_len = data_len;
+		result += actual_len;
+		while (data_len-- > 0) {
+		  if (len-- > 0) {
+		    *fp++ = *cp++;
+		  } else {
+		    // Buffer overflow
+		    *err = TFTP_TOOLARGE;
+		    goto out;
+		  }
+		}
+		last_good_block++;
+	      }
+	      // Send out the ACK
+	      hdr->th_opcode = htons(ACK);
+	      hdr->th_block = htons(last_good_block);
+	      if (sendto(s, data, 4 /* FIXME */, 0, 
+			 &from_addr, from_len) < 0) {
+		// Problem sending request
+		*err = TFTP_NETERR;
+		goto out;
+	      }
+	      if ((actual_len >= 0) && (actual_len < SEGSIZE)) {
+		// End of data
+		close(s);
+		freeaddrinfo(res);
+		return result;
+	      }
+	    } else 
+	      if (ntohs(hdr->th_opcode) == ERROR) {
+		*err = ntohs(hdr->th_code);
+		goto out;
+	      } else {
+		// What kind of packet is this?
                 *err = TFTP_PROTOCOL;
-                close(s);
-                return -1;
-            }
-        }
+		goto out;
+	      }
+	  }
+	}
+      }
+      // If we got here, it means there was a problem connecting to the 
+      // server. Try the next address returned by getaddrinfo
+    nextaddr:
+      if (-1 != s) {
+	close(s);
+      }
+      addrinfo=addrinfo->ai_next;
+    }
+    // We ran into problems. Cleanup
+ out:
+    if (-1 != s) {
+      close (s);
     }
+    freeaddrinfo(res);
+    return -1;
+}
+//
+// Read a file from a host into a local buffer.  Returns the
+// number of bytes actually read, or (-1) if an error occurs.
+// On error, *err will hold the reason.
+//
+// Depreciated. Use tftp_client_get instead.
+int
+tftp_get(char *filename,
+         struct sockaddr_in *server,
+         char *buf,
+         int len,
+         int mode,
+         int *err)
+{
+  char server_name[20];
+  char *ret;
+  int port;
+
+  ret = inet_ntop(AF_INET, (void *)&server->sin_addr, 
+		  server_name, sizeof(server_name));
+  if (NULL == ret) {
+      *err = TFTP_NETERR;
+      return -1;
+  }
+  port = server->sin_port;
+
+  return tftp_client_get(filename, server_name, port, buf, len, mode, err);
 }
 
 //
@@ -240,186 +305,247 @@ tftp_put(char *filename,
          int mode,
          int *err)
 {
-    int res = 0;
+  char server_name[20];
+  char *ret;
+  int port;
+
+  ret = inet_ntop(AF_INET, (void *)&server->sin_addr, 
+		  server_name, sizeof(server_name));
+  if (NULL == ret) {
+      *err = TFTP_NETERR;
+      return -1;
+  }
+  port = server->sin_port;
+
+  return tftp_client_put(filename, server_name, port, buf, len, mode, err);
+}
+
+//
+// Put a file to a host from a local buffer.  Returns the
+// number of bytes actually writen, or (-1) if an error occurs.
+// On error, *err will hold the reason.
+// This version uses the server name. This can be a name for DNS lookup
+// or a dotty or colony number format for IPv4 or IPv6.
+int tftp_client_put(char *filename,
+		    char *server,
+		    int port,
+		    char *buf,
+		    int len,
+		    int mode,
+		    int *err) {
+
+    int result = 0;
     int s, actual_len, data_len, recv_len, from_len;
     static int put_port = 7800;
-    struct sockaddr_in local_addr, server_addr, from_addr;
+    struct sockaddr local_addr, from_addr;
     char data[SEGSIZE+sizeof(struct tftphdr)];
     struct tftphdr *hdr = (struct tftphdr *)data;
     char *cp, *fp, *sfp;
     struct timeval timeout;
     unsigned short last_good_block = 0;
-    struct servent *server_info;
     fd_set fds;
     int total_timeouts = 0;
+    struct addrinfo hints;
+    struct addrinfo * addrinfo;
+    struct addrinfo * res;
+    int error;
 
     *err = 0;  // Just in case
 
-    server_info = getservbyname("tftp", "udp");
-    if (server_info == (struct servent *)0) {
-        *err = TFTP_NETERR;
-        return -1;
-    }
-
-    s = socket(AF_INET, SOCK_DGRAM, 0);
-    if (s < 0) {
-        // Couldn't open a communications channel
-        *err = TFTP_NETERR;
-        return -1;
-    }
-    memset((char *)&local_addr, 0, sizeof(local_addr));
-    local_addr.sin_family = AF_INET;
-    local_addr.sin_len = sizeof(local_addr);
-    local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
-    local_addr.sin_port = htons(put_port++);
-    if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
-        // Problem setting up my end
-        *err = TFTP_NETERR;
-        close(s);
-        return -1;
+    memset(&hints,0,sizeof(hints));
+    hints.ai_family = PF_UNSPEC;
+    error = getaddrinfo(server, "tftp", &hints, &res);
+    if (error) {
+      *err = TFTP_NETERR;
+      return -1;
     }
-    memset((char *)&server_addr, 0, sizeof(server_addr));
-    server_addr.sin_family = AF_INET;
-    server_addr.sin_len = sizeof(server_addr);
-    server_addr.sin_addr = server->sin_addr;
-    if (server->sin_port == 0) {
-        server_addr.sin_port = server_info->s_port; // Network order already
-    } else {
-        server_addr.sin_port = server->sin_port;
-    }
+    
+    addrinfo = res;
+    while (addrinfo) {
+      s = socket(addrinfo->ai_family, addrinfo->ai_socktype,
+		 addrinfo->ai_protocol);
+      if (s >= 0) {
+	memcpy(&local_addr,addrinfo->ai_addr,addrinfo->ai_addrlen);
+	switch(addrinfo->ai_addr->sa_family) {
+	case AF_INET: {
+	  struct sockaddr_in * saddr = 
+	    (struct sockaddr_in *) addrinfo->ai_addr;
+	  struct sockaddr_in * laddr = 
+	    (struct sockaddr_in *) &local_addr;
+	  if (port) {
+	    saddr->sin_port = htons(port);
+	  }
+	  laddr->sin_port = htons(put_port++);
+	  laddr->sin_addr.s_addr = INADDR_ANY;
+	  break;
+	}
+#ifdef CYGPKG_NET_INET6
+	case AF_INET6: {
+	  struct sockaddr_in6 * saddr = 
+	    (struct sockaddr_in6 *) addrinfo->ai_addr;
+	  struct sockaddr_in6 * laddr = 
+	    (struct sockaddr_in6 *) &local_addr;
+	  if (port) {
+	    saddr->sin6_port = htons(port);
+	  }
+	  laddr->sin6_port = htons(put_port++);
+	  laddr->sin6_addr = in6addr_any;
+	  break;
+	}
+#endif
+	default:
+	  *err = TFTP_NETERR;
+	  goto out;
+	}
+	if (bind(s, 
+		 (struct sockaddr *)&local_addr, 
+		 addrinfo->ai_addrlen) < 0) {
+	  // Problem setting up my end
+	  *err = TFTP_NETERR;
+	  goto out;
+	}
 
-    while (1) {
-        // Create initial request
-        hdr->th_opcode = htons(WRQ);  // Create/write file
-        cp = (char *)&hdr->th_stuff;
-        fp = filename;
-        while (*fp) *cp++ = *fp++;
-        *cp++ = '\0';
-        if (mode == TFTP_NETASCII) {
+	while (1) {
+	  // Create initial request
+	  hdr->th_opcode = htons(WRQ);  // Create/write file
+	  cp = (char *)&hdr->th_stuff;
+	  fp = filename;
+	  while (*fp) *cp++ = *fp++;
+	  *cp++ = '\0';
+	  if (mode == TFTP_NETASCII) {
             fp = "NETASCII";
-        } else if (mode == TFTP_OCTET) {
+	  } else if (mode == TFTP_OCTET) {
             fp = "OCTET";
-        } else {
+	  } else {
             *err = TFTP_INVALID;
-            return -1;
-        }
-        while (*fp) *cp++ = *fp++;
-        *cp++ = '\0';
-        // Send request
-        if (sendto(s, data, (int)(cp-data), 0, 
-                   (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
+	    goto out;
+	  }
+	  while (*fp) *cp++ = *fp++;
+	  *cp++ = '\0';
+	  // Send request
+	  if (sendto(s, data, (int)(cp-data), 0, 
+		     addrinfo->ai_addr,
+		     addrinfo->ai_addrlen) < 0) {
             // Problem sending request
             *err = TFTP_NETERR;
-            close(s);
-            return -1;
-        }
-        // Wait for ACK
-        timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
-        timeout.tv_usec = 0;
-        FD_ZERO(&fds);
-        FD_SET(s, &fds);
-        if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
+	    goto nextaddr;
+	  }
+	  // Wait for ACK
+	  timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
+	  timeout.tv_usec = 0;
+	  FD_ZERO(&fds);
+	  FD_SET(s, &fds);
+	  if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
             if (++total_timeouts > TFTP_TIMEOUT_MAX) {
-                // Timeout - no ACK received
-                *err = TFTP_TIMEOUT;
-                close(s);
-                return -1;
+	      // Timeout - no ACK received
+	      *err = TFTP_TIMEOUT;
+	      goto nextaddr;
             }
-        } else {
+	  } else {
             recv_len = sizeof(data);
             from_len = sizeof(from_addr);
             if ((data_len = recvfrom(s, &data, recv_len, 0, 
-                                     (struct sockaddr *)&from_addr, &from_len)) < 0) {
-                // What happened?
-                *err = TFTP_NETERR;
-                close(s);
-                return -1;
+                                     &from_addr, &from_len)) < 0) {
+	      // What happened?
+	      *err = TFTP_NETERR;
+	      goto out;
             }
             if (ntohs(hdr->th_opcode) == ACK) {
-                // Write request accepted - start sending data
-                break;
+	      // Write request accepted - start sending data
+	      break;
             } else 
-            if (ntohs(hdr->th_opcode) == ERROR) {
+	      if (ntohs(hdr->th_opcode) == ERROR) {
                 *err = ntohs(hdr->th_code);
-                close(s);
-                return -1;
-            } else {
+                goto out;
+	      } else {
                 // What kind of packet is this?
-                *err = TFTP_PROTOCOL;
-                close(s);
-                return -1;
-            }
-        }
-    }
-
-    // Send data
-    sfp = buf;
-    last_good_block = 1;
-    while (res < len) {
-        // Build packet of data to send
-        data_len = min(SEGSIZE, len-res);
-        hdr->th_opcode = htons(DATA);
-        hdr->th_block = htons(last_good_block);
-        cp = hdr->th_data;
-        fp = sfp;
-        actual_len = data_len + 4;
-        // FIXME - what about "netascii" data?
-        while (data_len-- > 0) *cp++ = *fp++;
-        // Send data packet
-        if (sendto(s, data, actual_len, 0, 
-                   (struct sockaddr *)&from_addr, from_len) < 0) {
+		goto out;
+	      }
+	  }
+	}
+	
+	// Send data
+	sfp = buf;
+	last_good_block = 1;
+	while (result < len) {
+	  // Build packet of data to send
+	  data_len = min(SEGSIZE, len-result);
+	  hdr->th_opcode = htons(DATA);
+	  hdr->th_block = htons(last_good_block);
+	  cp = hdr->th_data;
+	  fp = sfp;
+	  actual_len = data_len + 4;
+	  // FIXME - what about "netascii" data?
+	  while (data_len-- > 0) *cp++ = *fp++;
+	  // Send data packet
+	  if (sendto(s, data, actual_len, 0, 
+		     &from_addr, from_len) < 0) {
             // Problem sending request
             *err = TFTP_NETERR;
-            close(s);
-            return -1;
-        }
-        // Wait for ACK
-        timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
-        timeout.tv_usec = 0;
-        FD_ZERO(&fds);
-        FD_SET(s, &fds);
-        if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
+	    goto out;
+	  }
+	  // Wait for ACK
+	  timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
+	  timeout.tv_usec = 0;
+	  FD_ZERO(&fds);
+	  FD_SET(s, &fds);
+	  if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
             if (++total_timeouts > TFTP_TIMEOUT_MAX) {
-                // Timeout - no data received
-                *err = TFTP_TIMEOUT;
-                close(s);
-                return -1;
+	      // Timeout - no data received
+	      *err = TFTP_TIMEOUT;
+	      goto out;
             }
-        } else {
+	  } else {
             recv_len = sizeof(data);
             from_len = sizeof(from_addr);
             if ((data_len = recvfrom(s, &data, recv_len, 0, 
-                                     (struct sockaddr *)&from_addr, &from_len)) < 0) {
-                // What happened?
-                *err = TFTP_NETERR;
-                close(s);
-                return -1;
+                                     &from_addr, &from_len)) < 0) {
+	      // What happened?
+	      *err = TFTP_NETERR;
+	      goto out;
             }
             if (ntohs(hdr->th_opcode) == ACK) {
-                if (ntohs(hdr->th_block) == last_good_block) {
-                    // Advance pointers, etc
-                    sfp = fp;
-                    res += (actual_len - 4);
-                    last_good_block++;
-                } else {
-                    diag_printf("Send block #%d, got ACK for #%d\n", 
-                                last_good_block, ntohs(hdr->th_block));
-                }
+	      if (ntohs(hdr->th_block) == last_good_block) {
+		// Advance pointers, etc
+		sfp = fp;
+		result += (actual_len - 4);
+		last_good_block++;
+	      } else {
+		diag_printf("Send block #%d, got ACK for #%d\n", 
+			    last_good_block, ntohs(hdr->th_block));
+	      }
             } else 
-            if (ntohs(hdr->th_opcode) == ERROR) {
+	      if (ntohs(hdr->th_opcode) == ERROR) {
                 *err = ntohs(hdr->th_code);
-                close(s);
-                return -1;
-            } else {
+                goto out;
+	      } else {
                 // What kind of packet is this?
                 *err = TFTP_PROTOCOL;
-                close(s);
-                return -1;
-            }
-        }
+		goto out;
+	      }
+	  }
+	}
+	close (s);
+	return result;
+      }
+
+      // If we got here, it means there was a problem connecting to the 
+      // server. Try the next address returned by getaddrinfo
+    nextaddr:
+      if (-1 != s) {
+	close(s);
+      }
+      addrinfo=addrinfo->ai_next;
     }
-    close(s);
-    return res;
+    // We ran into problems. Cleanup
+ out:
+    if (-1 != s) {
+      close (s);
+    }
+    freeaddrinfo(res);
+    return -1;
 }
 
 // EOF tftp_client.c
+
+