diff packages/net/common/current/src/tftp_server.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 7c34839e7e97
children 16d247bb9c70
line wrap: on
line diff
--- a/packages/net/common/current/src/tftp_server.c
+++ b/packages/net/common/current/src/tftp_server.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
 //
 // 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
@@ -85,8 +86,18 @@ static struct info tftp_server_instrumen
 
 #endif // CYGOPT_NET_TFTP_SERVER_INSTRUMENT
 
+#ifdef CYGSEM_NET_TFTPD_MULTITHREADED
+struct tftpd_sem {
+  int port;
+  cyg_sem_t sem;
+};
+
+static struct tftpd_sem tftpd_sems[CYGNUM_NET_TFTPD_MULTITHREADED_PORTS];
+#endif //CYGSEM_NET_TFTPD_MULTITHREADED
+
 #define STACK_SIZE (((CYGNUM_HAL_STACK_SIZE_TYPICAL+(3*(SEGSIZE+sizeof(struct tftphdr)))) + CYGARC_ALIGNMENT-1) & ~(CYGARC_ALIGNMENT-1))
 static char *TFTP_tag = "TFTPD";
+#define CYGNUM_NET_MAX_INET_PROTOS 2
 struct tftp_server {
     char                 *tag;
     char                  stack[STACK_SIZE];
@@ -94,6 +105,8 @@ struct tftp_server {
     cyg_handle_t          thread_handle;
     int                   port;
     struct tftpd_fileops *ops;
+    int s[CYGNUM_NET_MAX_INET_PROTOS], num_s;
+    struct addrinfo      *res;
 };
 
 static char * errmsg[] = {
@@ -107,10 +120,87 @@ static char * errmsg[] = {
   "No such user",                       // 7 TFTP_ENOUSER   
 };
 
+// Little helper function to set the port number in an address 
+static void set_port(struct sockaddr * address, int port) {
+
+  switch (address->sa_family) {
+  case AF_INET:{
+    struct sockaddr_in *addr = (struct sockaddr_in *)address;
+    addr->sin_port = ntohs(port);
+    break;
+  }
+#ifdef CYGPKG_NET_INET6
+  case AF_INET6:
+    {
+      struct sockaddr_in6 *addr = (struct sockaddr_in6 *)address;
+      addr->sin6_port = ntohs(port);
+      break;
+    }
+#endif
+  default:
+    break;
+  }
+}
+
+#ifdef CYGSEM_NET_TFTPD_MULTITHREADED
+
+// Allocate a semaphore for a given port number if one is not already
+// allocated.
+static void sem_alloc(int port) {
+  int i;
+
+  CYG_ASSERT(port != 0, "Invalid port number");
+
+  cyg_scheduler_lock(); // Avoid race with other tftpd's
+  for (i=0; i < CYGNUM_NET_TFTPD_MULTITHREADED_PORTS; i++) {
+    if (tftpd_sems[i].port == port) {
+      cyg_scheduler_unlock();
+      return;
+    }
+    if (tftpd_sems[i].port == 0) {
+      tftpd_sems[i].port = port;
+      cyg_semaphore_init(&tftpd_sems[i].sem,1);
+      cyg_scheduler_unlock();
+      return ;
+    }
+  }
+  cyg_scheduler_unlock();
+  diag_printf("TFTPD: Unable to allocate a semaphore for port %d\n",port);
+}
+  
+// Wait on the semaphore for a given port number.
+static void sem_wait(int port) {
+  int i;
+  CYG_ASSERT(port != 0, "Invalid port number");
+  
+  for (i=0; i < CYGNUM_NET_TFTPD_MULTITHREADED_PORTS; i++) {
+    if (tftpd_sems[i].port == port) {
+      cyg_semaphore_wait(&tftpd_sems[i].sem);
+      return;
+    }
+  }
+  diag_printf("TFTPD: No semaphore for port %d\n",port);
+}
+
+// Release the semaphore for a given port number.
+static void sem_post(int port) {
+  int i;
+  CYG_ASSERT(port != 0, "Invalid port number");
+  
+  for (i=0; i < CYGNUM_NET_TFTPD_MULTITHREADED_PORTS; i++) {
+    if (tftpd_sems[i].port == port) {
+      cyg_semaphore_post(&tftpd_sems[i].sem);
+      return;
+    }
+  }
+  diag_printf("TFTPD: No semaphore for port %d\n",port);
+}
+#endif
+
 /* Send an error packet to the client */
 static void 
 tftpd_send_error(int s, struct tftphdr * reply, int err,
-		 struct sockaddr_in *from_addr, int from_len)
+		 struct sockaddr *from_addr, int from_len)
 {
     CYG_ASSERT( 0 <= err, "err underflow" );
     CYG_ASSERT( sizeof(errmsg)/sizeof(errmsg[0]) > err, "err overflow" );
@@ -121,7 +211,7 @@ tftpd_send_error(int s, struct tftphdr *
         err = 0; // Do not copy a random string from hyperspace
     strcpy(reply->th_msg, errmsg[err]);
     sendto(s, reply, 4+strlen(reply->th_msg)+1, 0, 
-	   (struct sockaddr *)from_addr, from_len);
+	   from_addr, from_len);
 }
 
 //
@@ -130,7 +220,7 @@ tftpd_send_error(int s, struct tftphdr *
 static void
 tftpd_write_file(struct tftp_server *server,
                  struct tftphdr *hdr, 
-                 struct sockaddr_in *from_addr, int from_len)
+                 struct sockaddr *from_addr, int from_len)
 {
     char data_out[SEGSIZE+sizeof(struct tftphdr)];
     char data_in[SEGSIZE+sizeof(struct tftphdr)];
@@ -141,28 +231,43 @@ tftpd_write_file(struct tftp_server *ser
     struct timeval timeout;
     fd_set fds;
     int total_timeouts = 0;
-    struct sockaddr_in client_addr, local_addr;
+    struct sockaddr client_addr;
+    struct addrinfo hints;
+    struct addrinfo *res;
     int client_len;
+    int error;
+    
+    memset(&hints,0,sizeof(hints));
+    hints.ai_family = from_addr->sa_family;
+    hints.ai_flags = AI_PASSIVE;
 
-    s = socket(AF_INET, SOCK_DGRAM, 0);
+    error = getaddrinfo(NULL,"tftp",&hints, &res);
+    if (0 != error) {
+      diag_printf("TFTPD: can't get a suitable local address: %s\n",
+		  gai_strerror(error));
+      return;
+    }
+    s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
     if (s < 0) {
         diag_printf("TFTPD: can't open socket for 'write_file'\n");
+	freeaddrinfo(res);
         return;
     }
-    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(INADDR_ANY);
-    if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
+
+    // We want the stack to pick a free local port number
+    set_port(res->ai_addr,0);
+
+    if (bind(s, res->ai_addr, res->ai_addrlen) < 0) {
         // Problem setting up my end
         diag_printf("TFTPD: can't bind to reply port for 'write_file'\n");
         close(s);
+	freeaddrinfo(res);
         return;
     }
     if ((fd = (server->ops->open)(hdr->th_stuff, O_WRONLY)) < 0) {
         tftpd_send_error(s,reply,TFTP_ENOTFOUND,from_addr, from_len);
         close(s);
+	freeaddrinfo(res);
         return;
     }
     ok = true;
@@ -176,7 +281,7 @@ tftpd_write_file(struct tftp_server *ser
 #ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT
             tftp_server_instrument.ack.send++;
 #endif
-            sendto(s, reply, 4, 0, (struct sockaddr *)from_addr, from_len);
+            sendto(s, reply, 4, 0, from_addr, from_len);
         repeat_select:
             timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
             timeout.tv_usec = 0;
@@ -200,7 +305,7 @@ tftpd_write_file(struct tftp_server *ser
             data_len = sizeof(data_in);
             client_len = sizeof(client_addr);
             if ((data_len = recvfrom(s, data_in, data_len, 0, 
-                      (struct sockaddr *)&client_addr, &client_len)) < 0) {
+                      &client_addr, &client_len)) < 0) {
                 // What happened?  No data here!
 #ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT
                 tftp_server_instrument.ack.resend++;
@@ -248,7 +353,7 @@ tftpd_write_file(struct tftp_server *ser
 #endif
                     reply->th_opcode = htons(ACK);
                     reply->th_block = htons(block++); // postincrement
-                    sendto(s, reply, 4, 0, (struct sockaddr *)from_addr, from_len);
+                    sendto(s, reply, 4, 0, from_addr, from_len);
                     break; // out of the retries loop
                 }
                 // Happy!  Break out of the retries loop.
@@ -271,6 +376,7 @@ tftpd_write_file(struct tftp_server *ser
         }
     }
     close(s);
+    freeaddrinfo(res);
     if (!closed) {
       (server->ops->close)(fd);
     }
@@ -282,7 +388,7 @@ tftpd_write_file(struct tftp_server *ser
 static void
 tftpd_read_file(struct tftp_server *server,
                 struct tftphdr *hdr, 
-                struct sockaddr_in *from_addr, int from_len)
+                struct sockaddr *from_addr, int from_len)
 {
     char data_out[SEGSIZE+sizeof(struct tftphdr)];
     char data_in[SEGSIZE+sizeof(struct tftphdr)];
@@ -293,28 +399,44 @@ tftpd_read_file(struct tftp_server *serv
     struct timeval timeout;
     fd_set fds;
     int total_timeouts = 0;
-    struct sockaddr_in client_addr, local_addr;
+    struct sockaddr client_addr;
+    struct addrinfo hints;
+    struct addrinfo *res;
     int client_len;
+    int error;
+
+    memset(&hints,0,sizeof(hints));
+    hints.ai_family = from_addr->sa_family;
+    hints.ai_flags = AI_PASSIVE;
 
-    s = socket(AF_INET, SOCK_DGRAM, 0);
+    error = getaddrinfo(NULL,"tftp",&hints, &res);
+    if (0 != error) {
+      diag_printf("TFTPD: can't get a suitable local address: %s\n",
+		  gai_strerror(error));
+      return;
+    }
+    s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
     if (s < 0) {
-        diag_printf("TFTPD: can't open socket for 'read_file'\n");
+        diag_printf("TFTPD: can't open socket for 'write_file'\n");
+	freeaddrinfo(res);
         return;
     }
-    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(INADDR_ANY);
-    if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
+
+    // We want the stack to pick a free local port number
+    set_port(res->ai_addr,0);
+
+    if (bind(s, res->ai_addr, res->ai_addrlen) < 0) {
         // Problem setting up my end
-        diag_printf("TFTPD: can't bind to reply port for 'read_file'\n");
+        diag_printf("TFTPD: can't bind to reply port for 'write_file'\n");
         close(s);
+	freeaddrinfo(res);
         return;
     }
-    if ((fd = (server->ops->open)(hdr->th_stuff, O_RDONLY)) < 0) {
+
+    if ((fd = (server->ops->open)(hdr->th_stuff, O_WRONLY)) < 0) {
         tftpd_send_error(s,reply,TFTP_ENOTFOUND,from_addr, from_len);
         close(s);
+	freeaddrinfo(res);
         return;
     }
     block = 0;
@@ -329,7 +451,7 @@ tftpd_read_file(struct tftp_server *serv
             tftp_server_instrument.data.send++;
 #endif
             if (sendto(s, reply, 4+len, 0,
-                       (struct sockaddr *)from_addr, from_len) < 0) {
+                       from_addr, from_len) < 0) {
                 // Something went wrong with the network!
                 ok = false;
                 break;
@@ -356,7 +478,7 @@ tftpd_read_file(struct tftp_server *serv
             data_len = sizeof(data_in);
             client_len = sizeof(client_addr);
             if ((data_len = recvfrom(s, data_in, data_len, 0, 
-                                     (struct sockaddr *)&client_addr,
+                                     &client_addr,
                                      &client_len)) < 0) {
                 // What happened?  Maybe someone lied to us...
 #ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT
@@ -403,149 +525,222 @@ tftpd_read_file(struct tftp_server *serv
         }
     }
     close(s);
+    freeaddrinfo(res);
     (server->ops->close)(fd);
 }
 
 //
 // Actual TFTP server
 //
-#define CYGSEM_TFTP_SERVER_MULTITHREADED
-#ifdef CYGSEM_TFTP_SERVER_MULTITHREADED
-static cyg_sem_t tftp_server_sem;
-#endif
 
 static void
 tftpd_server(cyg_addrword_t p)
 {
     struct tftp_server *server = (struct tftp_server *)p;
-    int s;
+    int max_s = 0;
     int data_len, recv_len, from_len;
-    struct sockaddr_in local_addr, from_addr;
-    struct servent *server_info;
+    struct sockaddr from_addr;
     char data[SEGSIZE+sizeof(struct tftphdr)];
     struct tftphdr *hdr = (struct tftphdr *)data;
-
+    struct addrinfo hints;
+    struct addrinfo *ai;
+    fd_set readfds;
+    char name[64];
+    int error;
+    int i;
+#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT
+    struct info o = tftp_server_instrument;
+#endif
+    
 #ifndef CYGPKG_NET_TESTS_USE_RT_TEST_HARNESS
     // Otherwise routine printfs fail the test - interrupts disabled too long.
     diag_printf("TFTPD [%x]: port %d\n", p, server->port);
 #endif
 
-    // Set up port
+    memset(&hints,0,sizeof(hints));
+    hints.ai_family = PF_UNSPEC;
+    hints.ai_flags = AI_PASSIVE;
+
+    error = getaddrinfo(NULL,"tftp",&hints, &server->res);
+    if (0 != error) {
+      diag_printf("TFTPD [%p] : can't get a local server address to bind to: %s\n",
+		  p, gai_strerror(error));
+      return;
+    }
+ 
+    // If the port is 0, we need to use the default TFTP port. Extrace
+    // the port number from one of the addresses returned by
+    // getaddrinfo.
     if (server->port == 0) {
-        server_info = getservbyname("tftp", "udp");
-        if (server_info == (struct servent *)0) {
-            diag_printf("TFTPD: can't get TFTP service information\n");
-            return;
-        }
-        // Network order in info; host order in server:
-        server->port = ntohs( server_info->s_port );
+      switch (server->res->ai_family) {
+      case AF_INET: 
+	{
+	  struct sockaddr_in *addr = (struct sockaddr_in *)server->res->ai_addr;
+	  server->port = ntohs(addr->sin_port);
+	  break;
+	}
+#ifdef CYGPKG_NET_INET6
+      case AF_INET6:
+	{
+	  struct sockaddr_in6 *addr = (struct sockaddr_in6 *)server->res->ai_addr;
+	  server->port = ntohs(addr->sin6_port);
+	  break;
+	}
+#endif
+      default:
+	break;
+      }
     }
 
+#ifdef CYGSEM_NET_TFTPD_MULTITHREADED   
+    sem_alloc(server->port);
     while (true) {
-#ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT
-        struct info o = tftp_server_instrument;
-#endif
-        // Create socket
-        s = socket(AF_INET, SOCK_DGRAM, 0);
-        if (s < 0) {
-            diag_printf("TFTPD [%x]: can't open socket\n", p);
-            return;
-        }
-        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(server->port);
-        if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
-            // Problem setting up my end
-            close(s);
-#ifdef CYGSEM_TFTP_SERVER_MULTITHREADED
-#ifndef CYGPKG_NET_TESTS_USE_RT_TEST_HARNESS
-            diag_printf("TFTPD [%x]: waiting to bind to service port\n", p);
 #endif
-            // Wait until the socket is free...
-            cyg_semaphore_wait( &tftp_server_sem );
-            continue; // try re-opening and rebinding the socket.
-#else
-            diag_printf("TFTPD [%x]: can't bind to service port\n", p);
-            return;
+      // Iterate over the addresses and create a local port to listen for requests 
+      ai = server->res;
+      memset(server->s,0,sizeof(server->s));
+      server->num_s = 0;
+      while (ai && (server->num_s < CYGNUM_NET_MAX_INET_PROTOS)) {
+	server->s[server->num_s] = socket(ai->ai_family, 
+					  ai->ai_socktype, 
+					  ai->ai_protocol);
+	if (server->s[server->num_s] < 0 ) {
+	  diag_printf("TFTPD [%x]: can't open socket\n", p);
+	  freeaddrinfo(server->res);
+	  server->res = NULL;
+	  return;
+	}
+	
+	set_port(ai->ai_addr, server->port);
+	
+#ifdef CYGSEM_NET_TFTPD_MULTITHREADED   
+	sem_wait(server->port);
 #endif
-        }
-
-        recv_len = sizeof(data);
-        from_len = sizeof(from_addr);
-        data_len = recvfrom(s, hdr, recv_len, 0,
-                            (struct sockaddr *)&from_addr, &from_len);
-        close(s); // so that other servers can bind to the TFTP socket
-#ifdef CYGSEM_TFTP_SERVER_MULTITHREADED
-        // The socket is free...
-        cyg_semaphore_post( &tftp_server_sem );
-#endif
-
-        if ( data_len < 0) {
-            diag_printf("TFTPD [%x]: can't read request\n", p);
-        } else {
-#ifndef CYGPKG_NET_TESTS_USE_RT_TEST_HARNESS
-            diag_printf("TFTPD [%x]: received %x from %s:%d\n", p,
-                        ntohs(hdr->th_opcode), inet_ntoa(from_addr.sin_addr),
-                        from_addr.sin_port);
+	if (bind(server->s[server->num_s],ai->ai_addr, ai->ai_addrlen) < 0) {
+	  // Problem setting up my end
+	  diag_printf("TFTPD [%x]: can't bind to server port\n",p);
+	  close(server->s[server->num_s]);
+	  server->s[server->num_s] = 0;
+	  ai = ai->ai_next;
+	  continue;
+	}
+	// We need to know the highest socket number for select.
+	if (server->s[server->num_s] > max_s)
+	  max_s = server->s[server->num_s];
+	server->num_s++;
+	ai = ai->ai_next;
+      }
+      
+#ifndef CYGSEM_NET_TFTPD_MULTITHREADED   
+      while (true) {
 #endif
-            switch (ntohs(hdr->th_opcode)) {
-            case WRQ:
-                tftpd_write_file(server, hdr, &from_addr, from_len);
-                break;
-            case RRQ:
-                tftpd_read_file(server, hdr, &from_addr, from_len);
-                break;
-            case ACK:
-            case DATA:
-            case ERROR:
-                // Ignore
-                break;
-            default:
-                diag_printf("TFTPD [%x]: bogus request %x from %s:%d\n", p,
-                            ntohs(hdr->th_opcode),
-                            inet_ntoa(from_addr.sin_addr),
-                            from_addr.sin_port );
-                tftpd_send_error(s,hdr,TFTP_EBADOP,&from_addr,from_len);
-            }
-        }
+	FD_ZERO(&readfds);
+	for (i=0; i < CYGNUM_NET_MAX_INET_PROTOS; i++) {
+	  if (server->s[i]) {
+	    FD_SET(server->s[i],&readfds);
+	  }
+	}
+	error = select(max_s+1,&readfds,NULL,NULL,NULL);
+	if ( -1 == error) {
+	  diag_printf("TFTPD [%x]: error in select\n",p);
+	}
+	for (i=0; i < CYGNUM_NET_MAX_INET_PROTOS; i++) {
+	  if (server->s[i] && FD_ISSET(server->s[i],&readfds)) {
+	    recv_len = sizeof(data);
+	    from_len = sizeof(from_addr);
+	    data_len = recvfrom(server->s[i], hdr, recv_len, 0,
+				&from_addr, &from_len);
+	    if ( data_len < 0) {
+	      diag_printf("TFTPD [%x]: can't read request\n", p);
+	    } else {
+#ifdef CYGSEM_NET_TFTPD_MULTITHREADED   
+	      // Close the socket and post on the semaphore some
+	      // another thread can start listening for requests. This
+	      // is not quite right. select could of returned with more than
+	      // one socket with data to read. Here we only deal with one of them 
+	      for (i=0; i < CYGNUM_NET_MAX_INET_PROTOS; i++) {
+		if (server->s[i]) {
+		  close (server->s[i]);
+		  server->s[i] = 0;
+		}
+	      }
+	      sem_post(server->port);
+#endif
+#ifndef CYGPKG_NET_TESTS_USE_RT_TEST_HARNESS
+	      getnameinfo(&from_addr,sizeof(from_addr), name, sizeof(name),0,0,0);
+	      diag_printf("TFTPD [%x]: received %x from %s\n", p,
+			  ntohs(hdr->th_opcode), name);
+#endif
+	      switch (ntohs(hdr->th_opcode)) {
+	      case WRQ:
+		tftpd_write_file(server, hdr, &from_addr, from_len);
+		break;
+	      case RRQ:
+		tftpd_read_file(server, hdr, &from_addr, from_len);
+		break;
+	      case ACK:
+	      case DATA:
+	      case ERROR:
+		// Ignore
+		break;
+	      default:
+		getnameinfo(&from_addr,sizeof(from_addr), name, sizeof(name),0,0,0);
+		diag_printf("TFTPD [%x]: bogus request %x from %s\n", p,
+			    ntohs(hdr->th_opcode),
+			    name);
+		tftpd_send_error(server->s[i],hdr,TFTP_EBADOP,&from_addr,from_len);
+	      }
+	      
 #ifdef CYGOPT_NET_TFTP_SERVER_INSTRUMENT
-        tftp_server_instrument.total_transactions++;
-
-        o.data.rx        -= tftp_server_instrument.data.rx       ;
-        o.data.rx_repeat -= tftp_server_instrument.data.rx_repeat;
-        o.data.rx_skip   -= tftp_server_instrument.data.rx_skip  ;
-        o.data.send      -= tftp_server_instrument.data.send     ;
-        o.data.resend    -= tftp_server_instrument.data.resend   ;
-
-        o.ack.rx         -= tftp_server_instrument.ack.rx        ;
-        o.ack.rx_repeat  -= tftp_server_instrument.ack.rx_repeat ;
-        o.ack.rx_skip    -= tftp_server_instrument.ack.rx_skip   ;
-        o.ack.send       -= tftp_server_instrument.ack.send      ;
-        o.ack.resend     -= tftp_server_instrument.ack.resend    ;
-
-        o.err_send       -= tftp_server_instrument.err_send      ;
-
+	      tftp_server_instrument.total_transactions++;
+	      
+	      o.data.rx        -= tftp_server_instrument.data.rx       ;
+	      o.data.rx_repeat -= tftp_server_instrument.data.rx_repeat;
+	      o.data.rx_skip   -= tftp_server_instrument.data.rx_skip  ;
+	      o.data.send      -= tftp_server_instrument.data.send     ;
+	      o.data.resend    -= tftp_server_instrument.data.resend   ;
+	      
+	      o.ack.rx         -= tftp_server_instrument.ack.rx        ;
+	      o.ack.rx_repeat  -= tftp_server_instrument.ack.rx_repeat ;
+	      o.ack.rx_skip    -= tftp_server_instrument.ack.rx_skip   ;
+	      o.ack.send       -= tftp_server_instrument.ack.send      ;
+	      o.ack.resend     -= tftp_server_instrument.ack.resend    ;
+	      
+	      o.err_send       -= tftp_server_instrument.err_send      ;
+	      
 #ifndef CYGPKG_NET_TESTS_USE_RT_TEST_HARNESS
-        if ( o.data.rx        ) diag_printf( "data rx       %4d\n", -o.data.rx        );
-        if ( o.data.rx_repeat ) diag_printf( "data rx_repeat%4d\n", -o.data.rx_repeat );
-        if ( o.data.rx_skip   ) diag_printf( "data rx_skip  %4d\n", -o.data.rx_skip   );
-        if ( o.data.send      ) diag_printf( "data send     %4d\n", -o.data.send      );
-        if ( o.data.resend    ) diag_printf( "data resend   %4d\n", -o.data.resend    );
-
-        if ( o.ack.rx        ) diag_printf( " ack rx       %4d\n", -o.ack.rx        );
-        if ( o.ack.rx_repeat ) diag_printf( " ack rx_repeat%4d\n", -o.ack.rx_repeat );
-        if ( o.ack.rx_skip   ) diag_printf( " ack rx_skip  %4d\n", -o.ack.rx_skip   );
-        if ( o.ack.send      ) diag_printf( " ack send     %4d\n", -o.ack.send      );
-        if ( o.ack.resend    ) diag_printf( " ack resend   %4d\n", -o.ack.resend    );
-
-        if ( o.err_send      ) diag_printf( "*error sends  %4d\n", -o.err_send      );
+	      if ( o.data.rx        ) diag_printf( "data rx       %4d\n", -o.data.rx        );
+	      if ( o.data.rx_repeat ) diag_printf( "data rx_repeat%4d\n", -o.data.rx_repeat );
+	      if ( o.data.rx_skip   ) diag_printf( "data rx_skip  %4d\n", -o.data.rx_skip   );
+	      if ( o.data.send      ) diag_printf( "data send     %4d\n", -o.data.send      );
+	      if ( o.data.resend    ) diag_printf( "data resend   %4d\n", -o.data.resend    );
+	      
+	      if ( o.ack.rx         ) diag_printf( " ack rx       %4d\n", -o.ack.rx         );
+	      if ( o.ack.rx_repeat )  diag_printf( " ack rx_repeat%4d\n", -o.ack.rx_repeat  );
+	      if ( o.ack.rx_skip   )  diag_printf( " ack rx_skip  %4d\n", -o.ack.rx_skip    );
+	      if ( o.ack.send      )  diag_printf( " ack send     %4d\n", -o.ack.send       );
+	      if ( o.ack.resend    )  diag_printf( " ack resend   %4d\n", -o.ack.resend     );
+	      
+	      if ( o.err_send      )  diag_printf( "*error sends  %4d\n", -o.err_send      );
 #endif // CYGPKG_NET_TESTS_USE_RT_TEST_HARNESS
 #endif // CYGOPT_NET_TFTP_SERVER_INSTRUMENT
+#ifdef CYGSEM_NET_TFTPD_MULTITHREADED
+	      break;
+#endif
+	    }
+	  }
+	}
+	// The following looks a little strange, but it keeps emacs's
+	// auto indention happy.
+#ifndef CYGSEM_NET_TFTPD_MULTITHREADED   
+      }
+#endif
+#ifdef CYGSEM_NET_TFTPD_MULTITHREADED   
     }
+#endif
 }
 
+
 //
 // This function is used to create a new server [thread] which supports
 // the TFTP protocol on the given port.  A server 'id' will be returned
@@ -599,7 +794,14 @@ tftpd_stop(int p)
         cyg_thread_set_priority(server->thread_handle, 0);
         cyg_thread_delay(1);  // Make sure it gets to die...
         if (cyg_thread_delete(server->thread_handle)) {
-            // Success shutting down the thread
+            // Success shutting down the thread. Close all its sockets.
+	    int i;
+	    for (i = 0 ; i < CYGNUM_NET_MAX_INET_PROTOS; i++) {
+	      if (server->s[i]) {
+		close (server->s[i]);
+	      }
+	    }
+	    freeaddrinfo(server->res);
             free(server);  // Give up memory
             return 1;
         }