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