TFTP Server for mbed using the new EthernetInterface

TFTPServer.cpp

Committer:
loopsva
Date:
2012-10-02
Revision:
0:5f175eb28fd6

File content as of revision 0:5f175eb28fd6:

//--------------------------------------------------------------------------------------------------------------------------------------//
// 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);
}