TFTP Server for mbed using the new EthernetInterface
Diff: TFTPServer.cpp
- 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); +}