changeset 2067:5841ca1d8824

TFTP ACK/NAK fixes, from Andrew Dyer
author gthomas
date Fri, 09 Sep 2005 13:26:01 +0000
parents 5d5572c64422
children c051df3f1083
files packages/redboot/current/ChangeLog packages/redboot/current/include/net/tftp_support.h packages/redboot/current/src/load.c packages/redboot/current/src/net/tftp_client.c
diffstat 4 files changed, 67 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/packages/redboot/current/ChangeLog
+++ b/packages/redboot/current/ChangeLog
@@ -1,3 +1,16 @@
+2005-09-09  Andrew Dyer  <adyer@righthandtech.com>
+ 
+ 	* src/load.c: add calls to redboot_getc_terminate before exiting
+ 	load_elf_image() in various error scenarios, change the final call
+ 	to redboot_getc_terminate to have the error flag set.  This will
+ 	cause a tftp nak and close down the connection since for ELF files
+ 	we don't read the whole content but end the connection when the
+ 	runnable parts are in.
+ 
+ 	* src/net/tftp_client.c: add tftp_error() to send an error back to
+ 	the server. define tftp_stream_terminate() and pass it into the
+ 	redboot interface.
+ 
 2005-09-08  Gary Thomas  <gary@mlbassoc.com>
 
 	* src/io.c: 
--- a/packages/redboot/current/include/net/tftp_support.h
+++ b/packages/redboot/current/include/net/tftp_support.h
@@ -87,6 +87,7 @@
 extern int   tftp_stream_open(connection_info_t *info, int *err);
 extern int   tftp_stream_read(char *buf, int len, int *err);
 extern void  tftp_stream_close(int *err);
+extern void  tftp_stream_terminate(bool abort, int (*getc)(void));
 extern char *tftp_error(int err);
 
 #define TFTP_TIMEOUT_PERIOD 5
--- a/packages/redboot/current/src/load.c
+++ b/packages/redboot/current/src/load.c
@@ -307,6 +307,7 @@ load_elf_image(getc_t getc, unsigned lon
     // Read the header
     if (_read(getc, (unsigned char *)&ehdr, sizeof(ehdr)) != sizeof(ehdr)) {
         diag_printf("Can't read ELF header\n");
+        redboot_getc_terminate(true);
         return 0;
     }
     offset += sizeof(ehdr);    
@@ -318,15 +319,18 @@ load_elf_image(getc_t getc, unsigned lon
 #endif
     if (ehdr.e_type != ET_EXEC) {
         diag_printf("Only absolute ELF images supported\n");
+        redboot_getc_terminate(true);
         return 0;
     }
     if (ehdr.e_phnum > MAX_PHDR) {
         diag_printf("Too many program headers\n");
+        redboot_getc_terminate(true);
         return 0;
     }
     while (offset < ehdr.e_phoff) {
         if ((*getc)() < 0) {
             diag_printf(SHORT_DATA);
+            redboot_getc_terminate(true);
             return 0;
         }
         offset++;
@@ -334,6 +338,7 @@ load_elf_image(getc_t getc, unsigned lon
     for (phx = 0;  phx < ehdr.e_phnum;  phx++) {
         if (_read(getc, (unsigned char *)&phdr[phx], sizeof(phdr[0])) != sizeof(phdr[0])) {
             diag_printf("Can't read ELF program header\n");
+            redboot_getc_terminate(true);
             return 0;
         }
 #if 0 // DEBUG
@@ -376,6 +381,7 @@ load_elf_image(getc_t getc, unsigned lon
             if (offset > phdr[phx].p_offset) {
                 if ((phdr[phx].p_offset + len) < offset) {
                     diag_printf("Can't load ELF file - program headers out of order\n");
+                    redboot_getc_terminate(true);
                     return 0;
                 }
                 addr += offset - phdr[phx].p_offset;
@@ -383,6 +389,7 @@ load_elf_image(getc_t getc, unsigned lon
                 while (offset < phdr[phx].p_offset) {
                     if ((*getc)() < 0) {
                         diag_printf(SHORT_DATA);
+                        redboot_getc_terminate(true);
                         return 0;
                     }
                     offset++;
@@ -400,6 +407,7 @@ load_elf_image(getc_t getc, unsigned lon
 #endif
                 if ((ch = (*getc)()) < 0) {
                     diag_printf(SHORT_DATA);
+                    redboot_getc_terminate(true);
                     return 0;
                 }
                 *addr++ = ch;
@@ -422,7 +430,10 @@ load_elf_image(getc_t getc, unsigned lon
         entry_address = ehdr.e_entry;
     }
 
-    redboot_getc_terminate(false);
+    // nak everything to stop the transfer, since redboot
+    // usually doesn't read all the way to the end of the
+    // elf files.
+    redboot_getc_terminate(true);
     if (addr_offset) diag_printf("Address offset = %p\n", (void *)addr_offset);
     diag_printf("Entry point: %p, address range: %p-%p\n", 
                 (void*)entry_address, (void *)load_address, (void *)load_address_end);
--- a/packages/redboot/current/src/net/tftp_client.c
+++ b/packages/redboot/current/src/net/tftp_client.c
@@ -158,10 +158,49 @@ tftp_ack(int *err)
     return 0;
 }
 
+static int
+tftp_error_ack(int *err, short code, char *msg)
+{
+    struct tftphdr *hdr = (struct tftphdr *)tftp_stream.data;
+
+    if (strlen(msg) > (SEGSIZE-1)) {
+      *(msg + SEGSIZE) = NULL;
+    }
+
+    if (tftp_stream.packets_received > 0) {
+        hdr->th_opcode = htons(ERROR);
+        hdr->th_code = code;
+        strcpy(&hdr->th_data, msg);
+        if (__udp_sendto(tftp_stream.data, (5 + strlen(msg)), 
+                         &tftp_stream.from_addr, &tftp_stream.local_addr) < 0) {
+            // Problem sending ACK
+            *err = TFTP_NETERR;
+            return -1;
+        }
+    }
+    return 0;
+}
+
 void
 tftp_stream_close(int *err)
 {
-    tftp_ack(err);
+    if (tftp_stream.open == true) {
+        tftp_ack(err);
+        tftp_stream.open = false;
+    }
+}
+
+void
+tftp_stream_terminate(bool abort,
+                      int (*getc)(void))
+{
+    int err;
+
+    if (abort)
+        tftp_error_ack(&err, EUNDEF, "redboot tftp_stream_terminate");
+    else
+        tftp_ack(&err);
+
     tftp_stream.open = false;
 }
 
@@ -274,6 +313,6 @@ tftp_error(int err)
 // RedBoot interface
 //
 GETC_IO_FUNCS(tftp_io, tftp_stream_open, tftp_stream_close,
-              0, tftp_stream_read, tftp_error);
+              tftp_stream_terminate, tftp_stream_read, tftp_error);
 RedBoot_load(tftp, tftp_io, true, true, 0);