TFTP Server for mbed using the new EthernetInterface

Revision:
0:5f175eb28fd6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TFTPServer.cpp	Tue Oct 02 20:03:54 2012 +0000
@@ -0,0 +1,471 @@
+//--------------------------------------------------------------------------------------------------------------------------------------//
+// the following to be used in TFTPServer.cpp
+#include "FATFileSystem.h" 
+#include "SDFileSystem.h"
+#include "EthernetInterface.h"
+#include "TFTPServer.h"
+#include "Watchdog.h"
+
+#define DEFAULTFILENAME "mbed-1001001sos"
+#define TFTP_PORT 69                    //Trivial File Transfer Protocol (TFTP)
+
+LocalFileSystem local(const char* name);//for access of files on mbed itself
+SDFileSystem sd(PinName MOSI, PinName MISO, PinName SCLK, PinName CS, const char* name);  //mosi, miso, sclk, cs
+UDPSocket server;     
+Endpoint client; 
+extern Serial pc;                       //Serial pc(USBTX, USBRX); 
+extern DigitalOut led1;
+extern DigitalOut led2;
+extern Watchdog wdt;
+extern int gDebug;                      //amount of verboseness on terminal wanted
+extern char mac[6];                     //mbed's Ethernet MAC address
+
+char *filename = DEFAULTFILENAME;
+const int MAXFILENAMESIZE = 12;         //old DOS filename 8.3 >>  abcdefgh.ijk
+const int MAXBLOCKSIZE_LCL = 300;       //200 blocks * 512 = 102.4k bytes, 300 blocks = 153.6k
+const int MAXBLOCKSIZE_SD = 20000;      //10000 blocks * 512 = 5.12M bytes, 50000 blocks = 25.6M
+bool filenameOK = false;
+bool isSD = false;
+char DUPfilename[MAXFILENAMESIZE + 33]; //dulpicate of filename which somehow gets destroyed during file transfer
+
+char buffer[592];                       //should be divisible by 16. normally 516 (528) bytes, however sometimes extra data shows up
+const int BUFFLINESMAX = sizeof(buffer) / 16;   //maximum buffer print size (in lines)
+int filesize = 1;                       //filesize in bytes
+int n = 0;
+int tempN = 0;
+
+//--------------------------------------------------------------------------------------------------------------------------------------//
+// Constructor and destructor
+
+TFTPServer::TFTPServer(const char* name) {
+}
+    
+TFTPServer::~TFTPServer() {
+    TFTPServer::close();
+}
+
+//--------------------------------------------------------------------------------------------------------------------------------------//
+// Initializes and closes the TFTP server.
+
+// open port 69
+void TFTPServer::open() {
+    server.bind(TFTP_PORT); 
+    server.set_blocking(false, 1);
+}
+
+// close port 69
+void TFTPServer::close() {
+    server.close();
+}
+
+//--------------------------------------------------------------------------------------------------------------------------------------//
+// A generic text array for 2 purposes. 
+// 1. An adder to become part of a TFTP file for FatFileSystem.  Final file name made by CreateFileName()  
+//      Example, if requested file was happy.cpp, FatFileSystem would see /local/happy.cpp
+// 2. To return text errors back to tftp host. Error text is made by SendErrMsg(3, 2)
+//      Examples, mbed>> Big-File  -or-  mbed>> NotExist
+
+const char LocalText[] = {
+    '/', 'l', 'o', 'c', 'a', 'l', '/',   0,   0,   // /local/   << part of final filename
+    '/', 's', 'd', '/',   0,   0,   0,   0,   0,   // /sd/      <<  ""    ""    ""
+    'L', 'o', 'n', 'g', 'N', 'a', 'm', 'e', ' ',   // LongName  << TFTP Error return text to host
+    'B', 'i', 'g', '-', 'F', 'i', 'l', 'e', ' ',   // Big-File  <<  ""    ""    ""
+    'N', 'o', 't', 'E', 'x', 'i', 's', 't', ' ',   // NotExist  <<  ""    ""    ""
+    'I', 'l', 'l', 'e', 'g', 'a', 'l', ' ', ' ',   // Illegal   <<  ""    ""    ""
+    'T', 'i', 'm', 'e', 'O', 'u', 't', ' ', ' ',   // TimeOut   <<  ""    ""    ""
+    'm', 'b', 'e', 'd', '-', 's', 'n', '>', ' ',   // mbed-sn>  << header for FTP Error return to host
+    0};                                            //           << example:  mbed>> Big-File
+
+//--------------------------------------------------------------------------------------------------------------------------------------//
+// Used for doing a hex and ascii dump of the buffer.  BufferLines tells PrintBuffer how
+// many 16 byte lines to print.
+
+
+void TFTPServer::PrintBuffer(int BufferLines) {
+    if(BufferLines <= 0) {
+        BufferLines = 1;
+    } else if(BufferLines > BUFFLINESMAX) {
+        BufferLines = BUFFLINESMAX;
+    }
+    pc.printf("\r\nbuffer contents:\r\n");
+    for(int i = 0; i < BufferLines; i++) {
+        pc.printf("%04x  ", i * 0x10);
+        for(int s = 0; s < 8; s++) {
+            pc.printf("%02x ", buffer[s + i * 0x10]);
+        }
+        pc.printf("  ");
+        for(int s = 0; s < 8; s++) {
+            pc.printf("%02x ", buffer[s + i * 0x10 + 8]);
+        }
+        pc.printf("  ");
+        for(int s = 0; s < 8; s++) {
+            if((buffer[s + i * 0x10] < 0x20) || (buffer[s + i * 0x10] > 0x7f)) { 
+                pc.printf(".");
+            } else {
+                pc.printf("%c", buffer[s + i * 0x10]);
+            }
+        }
+        pc.printf(" ");
+        for(int s = 0; s < 8; s++) {
+            if((buffer[s + i * 0x10 + 8] < 0x20) || (buffer[s + i * 0x10 + 8] > 0x7f)) { 
+                pc.printf(".");
+            } else {
+                pc.printf("%c", buffer[s + i * 0x10 + 8]);
+            }
+        }
+        pc.printf("\r\n");
+    }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------------------------------//
+// Creates a text error message and op code in the buffer to send back to the host.
+
+void TFTPServer::SendErrMsg(int msg, int code) {
+    buffer[0] = 0;
+    buffer[1] = 5;
+    buffer[2] = 0;
+    buffer[3] = code;
+    //add text   mbed sn:
+    for(int i = 0; i <= 8; i++) {
+        buffer[i + 4] = LocalText[i + 7 * 9];
+    }
+    //add s/n
+    for(int i = 0; i <= 2; i++) {
+        unsigned char m = mac[i + 3];
+        m = m >> 4;
+        if(m <= 9) {
+            m = m + '0';
+        } else {
+            m = m - 10 + 'A';
+        }
+        buffer[i * 2 + 13] = m;
+        m = mac[i + 3];
+        m = m & 15;
+        if(m <= 9) {
+            m = m + '0';
+        } else {
+            m = m - 10 + 'A';
+        }
+        buffer[i * 2 + 14] = m;
+    }
+    buffer[19] = ' ';
+    //add error message text
+    for(int i = 0; i <= 8; i++) {
+        buffer[i + 20] = LocalText[i + msg * 9];
+    }
+    buffer[29] = NULL;
+    if(gDebug > 3) PrintBuffer(4); 
+    tempN = n;
+    n = 30;
+    //send off the error message
+    server.sendTo(client, buffer, n); 
+    n = tempN;
+}
+
+//--------------------------------------------------------------------------------------------------------------------------------------//
+// Used for making a proper file name for FatFileSystem.  Routine takes file request out of buffer and  
+// adds /local/ to the front end.  Example, if the requested file from the host is hoopla.fun, the
+// routine would make filename = /local/hoopla.fun
+// NEW:  if mbed source / destination filename starts with /sd/ then files are directed to / from SD flash
+
+void TFTPServer::CreateFileName() {
+    int i = 0;
+    int t = 0;
+    isSD = false;
+    filenameOK = false;
+    if((buffer[2] == '/')  && (buffer[3] == 's')  && (buffer[4] == 'd')  && (buffer[5] == '/')) {
+        isSD = true;
+        do {
+            buffer[i] = buffer[i +2];
+            i++;
+        } while(buffer[i] != 0);
+        if(i <= (MAXFILENAMESIZE + 6)) {
+            filenameOK = true;
+        }
+    
+    } else {
+        do {
+            buffer[i + MAXFILENAMESIZE + 32] = LocalText[i];
+            i++;
+        } while(LocalText[i] != 0);
+        buffer[i + MAXFILENAMESIZE + 32] = LocalText[i];
+    
+        if(gDebug > 3) PrintBuffer(6); 
+        t = 2;
+        do {
+            buffer[i + MAXFILENAMESIZE + 32] = buffer[t];
+            i++;
+            t++;
+        } while(buffer[t] != 0);
+        buffer[i + MAXFILENAMESIZE + 32] = buffer[t];
+        if(t <= (MAXFILENAMESIZE + 2)) {
+            filenameOK = true;
+        }
+        buffer[i] = 0;
+        i = 0;
+        if(gDebug > 3) PrintBuffer(6); 
+        do {
+            buffer[i] = buffer[i + MAXFILENAMESIZE + 32];
+            i++;
+        } while(buffer[i + MAXFILENAMESIZE + 32] != 0);
+        buffer[i] = buffer[i + MAXFILENAMESIZE + 32];
+    }
+    buffer[i] = 0;
+    buffer[i + 1] = 0;
+    for(int i = 0; i <= MAXFILENAMESIZE + 32; i++) {
+        DUPfilename[i] = buffer[i];
+        if(buffer[i] == NULL) {
+            break;
+        }
+    }
+    filename = buffer;
+    if(gDebug > 3) {
+        PrintBuffer(6);
+        pc.printf("mbed file:  %s\r\n", filename);
+        pc.printf(" dup file:  %s\r\n", DUPfilename);
+    }
+}
+
+//--------------------------------------------------------------------------------------------------------------------------------------//
+// The main TFTP routine
+            
+int TFTPServer::poll() {
+    server.set_blocking(false, 1);
+    n = server.receiveFrom(client, buffer, sizeof(buffer)); 
+    if(n == 0) {
+        return(0);
+    }
+    pc.printf("Opening socket from: %s\r\n", client.get_address());
+    bool opened = false;     
+    filenameOK = false;
+    int block = 0; 
+    int getBlock = (buffer[2] << 8) + buffer[3]; 
+    int command = (buffer[0] << 8) + buffer[1];    
+    int timeout = 0;
+    int totalTO = 0;
+    if(command == 5) {
+        if(gDebug > 0) PrintBuffer(4);
+        pc.printf("*** Remote Host Error!!   n: %d   cmd: %d   blk: %d\r\n", n, command, getBlock); 
+        return(7);
+    }
+    if((n >= (MAXFILENAMESIZE + 30)) || (command >= 3)) {
+        if(gDebug > 3) PrintBuffer(4);
+        pc.printf("*** Illegal TFTP operation!!   n: %d   cmd: %d   blk: %d\r\n", n, command, getBlock); 
+        SendErrMsg(5, 5);                //from RFC:  5  Unknown transfer ID.
+        return(5);
+    }
+    CreateFileName();
+    if(filenameOK == false) {
+        pc.printf("*** Bad file name size!!  %s\r\n", filename);
+            SendErrMsg(2, 2);                //from RFC:  2  Access violation.
+            return(2);
+    }
+    opened = true; 
+    if(command == 2) {
+        pc.printf("Receiving mbed file %s ...       ", filename);
+        FILE *fp = fopen(filename, "w"); 
+        buffer[0] = 0;
+        buffer[1] = 4;
+        buffer[2] = block >> 8;
+        buffer[3] = block & 255;
+        tempN = n;
+        n = 4;
+        server.sendTo(client, buffer, n); 
+        n = tempN;
+        block++; 
+        filesize = 0;
+        while (opened == true) {   
+            if(gDebug > 2)  pc.printf("\r\nWait for packet...\r\n");    
+            timeout = 0;
+            do{ 
+                n = server.receiveFrom(client, buffer, sizeof(buffer));  //<<<<I think it gets stuck in here!!!!
+                timeout++;
+                totalTO++;
+                led2 = !led2;
+                led1 = !led2;
+                wdt.kick();
+                if(gDebug > 0) {
+                    if(timeout >= 3) {
+                        pc.printf("  TO: %4d %c%c%c%c%c%c%c%c%c%c%c", timeout, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8);
+                    }
+                }
+            } while((n == 0) && (timeout <= 5000));
+            if ((n == 0) || (timeout >= 4999)) {
+                pc.printf("*** RX packet timeout!!\r\n");
+                fclose(fp);
+                SendErrMsg(6, 4);                //from RFC: 4 Illegal TFTP operation.
+                return(6);
+            }
+            if(buffer[1] == 3) {
+                if(gDebug > 2)  pc.printf("Got data from: %s   Bytes:  %d\r\n", client.get_address(), n); 
+                for(int i = 0; i < 512; i++) {
+                    fputc( buffer[i + 4], fp);
+                    filesize++;
+                }
+                if((block > MAXBLOCKSIZE_LCL) && (isSD == false)) {
+                    fclose(fp);
+                    pc.printf("\r\n*** Local File too big!!\r\n");
+                    SendErrMsg(3, 3);                //from RFC: 3 Disk full or allocation exceeded.
+ 
+//                    pc.printf("Same filename? %s ...       \r\n", DUPfilename);                    
+
+//filemane gets destroyed somewhere between original creation and here.  filename now has the first 4 bytes (and size) of buffer
+//this make removing the file useless since you cannot remove an empty filename.  This was fixed in Rev 108a by adding a duplicate
+//file name 'DUPfilename' at CreateFileName().  DUPfilename is copied into filename just before this code.
+
+                    filename = DUPfilename;
+                    pc.printf("removing:  %s\r\n", filename);
+                    if(gDebug >= 2) {
+                        for(int i = 0; i <= sizeof(filename) - 1; i++) {
+                            pc.printf(" %02x", filename[i]);
+                        }
+                        pc.printf("    %d\r\n", n);
+                        PrintBuffer(4);
+                    }
+                    remove(filename); 
+                    return(3);
+                
+                } else if((block > MAXBLOCKSIZE_SD) && (isSD == true)) {
+                    fclose(fp);
+                    pc.printf("\r\n*** SD File too big!!\r\n");
+                    SendErrMsg(3, 3);                //from RFC: 3 Disk full or allocation exceeded.
+                    filename = DUPfilename;
+                    pc.printf("removing:  %s\r\n", filename);
+                    if(gDebug >= 2) {
+                        for(int i = 0; i <= sizeof(filename) - 1; i++) {
+                            pc.printf(" %02x", filename[i]);
+                        }
+                        pc.printf("    %d\r\n", n);
+                        PrintBuffer(4);
+                    }
+                    remove(filename); 
+                    return(3);
+                
+                } else {
+                    buffer[0] = 0;
+                    buffer[1] = 4;
+                    buffer[2] = block >> 8;
+                    buffer[3] = block & 255;
+                    tempN = n;
+                    n = 4;
+                    server.sendTo(client, buffer, n); 
+                    n = tempN;
+                   if(gDebug > 0) pc.printf("%c%c%c%c%c%c%c%6d ", 8, 8, 8, 8, 8, 8, 8, block);
+                    block++; 
+                    if(n <= 515) {
+                        fclose(fp);
+                        if(gDebug > 0) pc.printf("Blocks.  Filesize: %d bytes\r\n", filesize);
+                        totalTO = totalTO - block;
+                        if(gDebug > 0) pc.printf("Time Waiting on Host: %d mSec\r\n", totalTO);
+                        opened = false;
+                    }
+                }
+            } else {
+                pc.printf("huh data??\r\n");
+                fclose(fp);
+                SendErrMsg(5, 5);                //from RFC: 5 Unknown transfer ID.
+                return(5);
+            }
+        }
+    }
+    if(command == 1) {
+        bool lastblock = false;
+        FILE *fp = fopen(filename, "r");  
+        if (fp == NULL) {
+            pc.printf("\r\n*** Bad filename!!\r\n");
+            SendErrMsg(4, 1);                //from RFC: 1 File not found.
+            return(4);
+        } else {
+            pc.printf("Sending mbed file %s ...         ", filename);
+            buffer[0] = 0;
+            buffer[1] = 3;
+            buffer[2] = block >> 8;
+            buffer[3] = block & 255;
+            tempN = n;
+            n = 4;
+            server.sendTo(client, buffer, n); 
+            n = tempN;
+            block++; 
+            filesize = 0;
+            while (opened == true) {  
+                timeout = 0;
+                do{ 
+                    n = server.receiveFrom(client, buffer, sizeof(buffer));  //<<<<I think it gets stuck in here!!!!
+                    timeout++;
+                    wdt.kick();
+                    totalTO++;
+                    led2 = !led2;
+                    led1 = !led2;
+                    if(gDebug > 0) {
+                        if(timeout >= 3) {
+                            pc.printf("  TO: %4d %c%c%c%c%c%c%c%c%c%c%c", timeout, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8);
+                        }
+                    }
+                } while((n == 0) && (timeout <= 5000));
+                if ((n == 0) || (timeout >= 4999)) {
+                    pc.printf("*** TX packet timeout!!\r\n");
+                    fclose(fp);
+                    SendErrMsg(6, 4);                //from RFC: 4 Illegal TFTP operation.
+                    return(6);
+                }
+                if(gDebug > 2)  pc.printf("Got request from: %s   Bytes:  %d\r\n", client.get_address(), n); 
+                tempN = 0;
+                for(int i = 0; i < 512; i++) {
+                    buffer[i + 4] = fgetc(fp);
+                    filesize++;
+                    tempN++;
+                    if(feof(fp)) {
+                        if(gDebug > 2) pc.printf("fini...\r\n");
+                        lastblock = true;
+                        break;
+                    }
+                }
+                buffer[0] = 0;
+                buffer[1] = 3;
+                buffer[2] = block >> 8;
+                buffer[3] = block & 255;
+                n = tempN + 4;
+                if(lastblock == true) {
+                    n--;
+                }
+                server.sendTo(client, buffer, n); 
+               if(gDebug > 0) pc.printf("%c%c%c%c%c%c%c%6d ", 8, 8, 8, 8, 8, 8, 8, block);
+                block++; 
+                if(lastblock == true) {
+                    fclose(fp);
+                    block--; 
+//                    block--; 
+                    totalTO = totalTO - block;
+                    opened = false;
+                    timeout = 0;
+                    if(gDebug > 0) { 
+                        pc.printf("%c%c%c%c%c%c%c%6d ", 8, 8, 8, 8, 8, 8, 8, block);
+                        pc.printf("Blocks.  Filesize: %d bytes\r\n", filesize - 1);
+                        if(gDebug > 0) pc.printf("Time Waiting on Host: %d mSec\r\n", totalTO);
+                    }
+                    do{ 
+                        n = server.receiveFrom(client, buffer, sizeof(buffer));  //<<<<I think it gets stuck in here!!!!
+                        timeout++;
+                        wdt.kick();
+                        totalTO++;
+                        led2 = !led2;
+                        led1 = !led2;
+                        if(gDebug > 2) {
+                            if(timeout >= 3) {
+                                pc.printf("  TO: %4d %c%c%c%c%c%c%c%c%c%c%c", timeout, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8);
+                            }
+                        }
+                    } while((n == 0) && (timeout <= 5000));
+                    if ((n == 0) || (timeout >= 4999)) {
+                        pc.printf("*** TX  -last- packet timeout!!\r\n");
+                        fclose(fp);
+                        SendErrMsg(6, 4);                //from RFC: 4 Illegal TFTP operation.
+                        return(6);
+                    }
+                }
+            }
+        }
+    }
+    return(0);
+}