This is a simple but complete TFTP server capable to run on MBED-OS5. It can receive and send files and listens on port 69. Please notice that it supports only octet (raw 8 bit bytes) mode transfers. It is part of the LaOS (Laser Open Source) project: http://www.laoslaser.org/
Fork of TFTPServer by
TFTPServer.cpp
00001 /* 00002 * TFTPServer.cpp 00003 * Simple TFTP server 00004 * 00005 * Copyright (c) 2011 Jaap Vermaas 00006 * Modified by Zoltan Hudak 2018 for MBED-OS5 00007 * 00008 * This file is part of the LaOS project (see: http://wiki.laoslaser.org) 00009 * 00010 * LaOS is free software: you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation, either version 3 of the License, or 00013 * (at your option) any later version. 00014 * 00015 * LaOS is distributed in the hope that it will be useful, 00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 * GNU General Public License for more details. 00019 * 00020 * You should have received a copy of the GNU General Public License 00021 * along with LaOS. If not, see <http://www.gnu.org/licenses/>. 00022 * 00023 * Minimal TFTP Server 00024 * * Receive and send files via TFTP 00025 * * Server handles only one transfer at a time 00026 * * Supports only octet (raw 8 bit bytes) mode transfers 00027 * * fixed block size: 512 bytes 00028 * 00029 */ 00030 #include "TFTPServer.h" 00031 #include "EthernetInterface.h" 00032 00033 //#define TFTP_DEBUG 00034 00035 /** 00036 * @brief Creates a new TFTP server listening on myPort. 00037 * @note 00038 * @param net A pointer to EthernetInterface object. 00039 * @param port A port to listen on (defaults to 69). 00040 * @retval 00041 */ 00042 TFTPServer::TFTPServer(EthernetInterface* net, uint16_t myPort /* = 69 */ ) 00043 { 00044 port = myPort; 00045 #ifdef TFTP_DEBUG 00046 printf("TFTPServer(): port=%d\r\n", myPort); 00047 #endif 00048 socket = new UDPSocket(net); 00049 state = LISTENING; 00050 if (socket->bind(port)) 00051 { 00052 socketAddr = SocketAddress(0, port); 00053 state = ERROR; 00054 } 00055 00056 #ifdef TFTP_DEBUG 00057 printf("FTP server state = %d\r\n", getState()); 00058 #endif 00059 socket->set_blocking(false); 00060 fileCounter = 0; 00061 } 00062 00063 /** 00064 * @brief Destroys this instance of the TFTP server. 00065 * @note 00066 * @param 00067 * @retval 00068 */ 00069 TFTPServer::~TFTPServer() 00070 { 00071 socket->close(); 00072 delete(socket); 00073 strcpy(remoteIP, ""); 00074 state = DELETED; 00075 } 00076 00077 /** 00078 * @brief Resets the TFTP server. 00079 * @note 00080 * @param 00081 * @retval 00082 */ 00083 void TFTPServer::reset() 00084 { 00085 socket->close(); 00086 delete(socket); 00087 strcpy(remoteIP, ""); 00088 socket = new UDPSocket(); 00089 state = LISTENING; 00090 if (socket->bind(port)) 00091 { 00092 socketAddr = SocketAddress(0, port); 00093 state = ERROR; 00094 } 00095 00096 socket->set_blocking(false); 00097 strcpy(fileName, ""); 00098 fileCounter = 0; 00099 } 00100 00101 /** 00102 * @brief Gets current TFTP status. 00103 * @note 00104 * @param 00105 * @retval 00106 */ 00107 TFTPServer::State TFTPServer::getState() 00108 { 00109 return state; 00110 } 00111 00112 /** 00113 * @brief Temporarily disables incoming TFTP connections. 00114 * @note 00115 * @param 00116 * @retval 00117 */ 00118 void TFTPServer::suspend() 00119 { 00120 state = SUSPENDED; 00121 } 00122 00123 /** 00124 * @brief Resumes incoming TFTP connection after suspension. 00125 * @note 00126 * @param 00127 * @retval 00128 */ 00129 void TFTPServer::resume() 00130 { 00131 if (state == SUSPENDED) 00132 state = LISTENING; 00133 } 00134 00135 /** 00136 * @brief Polls for data or new connection. 00137 * @note 00138 * @param 00139 * @retval 00140 */ 00141 void TFTPServer::poll() 00142 { 00143 if ((state == SUSPENDED) || (state == DELETED) || (state == ERROR)) 00144 return; 00145 00146 socket->set_blocking(false); 00147 00148 char buff[516]; 00149 int len = socket->recvfrom(&socketAddr, buff, sizeof(buff)); 00150 00151 if (len <= 0) 00152 return; 00153 00154 #ifdef TFTP_DEBUG 00155 printf("Got block with size %d.\n\r", len); 00156 #endif 00157 switch (state) { 00158 case LISTENING: 00159 { 00160 switch (buff[1]) { 00161 case 0x01: // RRQ 00162 connectRead(buff); 00163 break; 00164 00165 case 0x02: // WRQ 00166 connectWrite(buff); 00167 break; 00168 00169 case 0x03: // DATA before connection established 00170 sendError("No data expected.\r\n"); 00171 break; 00172 00173 case 0x04: // ACK before connection established 00174 sendError("No ack expected.\r\n"); 00175 break; 00176 00177 case 0x05: // ERROR packet received 00178 #ifdef TFTP_DEBUG 00179 printf("TFTP Eror received.\r\n"); 00180 #endif 00181 break; 00182 00183 default: // unknown TFTP packet type 00184 sendError("Unknown TFTP packet type.\r\n"); 00185 break; 00186 } // switch buff[1] 00187 break; // case listening 00188 } 00189 00190 case READING: 00191 { 00192 if (cmpHost()) 00193 { 00194 switch (buff[1]) { 00195 case 0x01: 00196 // if this is the receiving host, send first packet again 00197 if (blockCounter == 1) 00198 { 00199 ack(0); 00200 dupCounter++; 00201 } 00202 00203 if (dupCounter > 10) 00204 { // too many dups, stop sending 00205 sendError("Too many dups"); 00206 fclose(file); 00207 state = LISTENING; 00208 strcpy(remoteIP, ""); 00209 } 00210 break; 00211 00212 case 0x02: 00213 // this should never happen, ignore 00214 sendError("WRQ received on open read socket"); 00215 fclose(file); 00216 state = LISTENING; 00217 strcpy(remoteIP, ""); 00218 break; 00219 00220 case 0x03: 00221 // we are the sending side, ignore 00222 sendError("Received data package on sending socket"); 00223 fclose(file); 00224 state = LISTENING; 00225 strcpy(remoteIP, ""); 00226 break; 00227 00228 case 0x04: 00229 // last packet received, send next if there is one 00230 dupCounter = 0; 00231 if (blockSize == 516) 00232 { 00233 getBlock(); 00234 sendBlock(); 00235 } 00236 else 00237 { //EOF 00238 fclose(file); 00239 state = LISTENING; 00240 strcpy(remoteIP, ""); 00241 } 00242 break; 00243 00244 default: // this includes 0x05 errors 00245 sendError("Received 0x05 error message"); 00246 fclose(file); 00247 state = LISTENING; 00248 strcpy(remoteIP, ""); 00249 break; 00250 } // switch (buff[1]) 00251 } 00252 00253 #ifdef TFTP_DEBUG 00254 else 00255 printf("Ignoring package from other remote client during RRQ.\r\n"); 00256 #endif 00257 break; // reading 00258 } 00259 00260 case WRITING: 00261 { 00262 if (cmpHost()) 00263 { 00264 switch (buff[1]) { 00265 case 0x02: 00266 { 00267 // if this is a returning host, send ack again 00268 ack(0); 00269 #ifdef TFTP_DEBUG 00270 TFTP_DEBUG("Resending Ack on WRQ.\r\n"); 00271 #endif 00272 break; // case 0x02 00273 } 00274 00275 case 0x03: 00276 { 00277 int block = (buff[2] << 8) + buff[3]; 00278 if ((blockCounter + 1) == block) 00279 { 00280 ack(block); 00281 // new packet 00282 char* data = &buff[4]; 00283 fwrite(data, 1, len - 4, file); 00284 blockCounter++; 00285 dupCounter = 0; 00286 } 00287 else 00288 { // mismatch in block nr 00289 if ((blockCounter + 1) < block) 00290 { // too high 00291 sendError("Packet count mismatch"); 00292 fclose(file); 00293 state = LISTENING; 00294 remove(fileName); 00295 strcpy(remoteIP, ""); 00296 } 00297 else 00298 { // duplicate packet, send ACK again 00299 if (dupCounter > 10) 00300 { 00301 sendError("Too many dups"); 00302 fclose(file); 00303 remove(fileName); 00304 state = LISTENING; 00305 } 00306 else 00307 { 00308 ack(blockCounter); 00309 dupCounter++; 00310 } 00311 } 00312 } 00313 00314 if (len < 516) 00315 { 00316 ack(blockCounter); 00317 fclose(file); 00318 state = LISTENING; 00319 strcpy(remoteIP, ""); 00320 fileCounter++; 00321 #ifdef TFTP_DEBUG 00322 printf("File receive finished.\r\n"); 00323 #endif 00324 } 00325 break; // case 0x03 00326 } 00327 00328 default: 00329 { 00330 sendError("No idea why you're sending me this!"); 00331 break; // default 00332 } 00333 } // switch (buff[1]) 00334 } 00335 00336 #ifdef TFTP_DEBUG 00337 else 00338 printf("Ignoring packege from other remote client during WRQ.\r\n"); 00339 #endif 00340 break; // writing 00341 } 00342 00343 case ERROR: 00344 case SUSPENDED: 00345 case DELETED: 00346 default: 00347 { } 00348 } // state 00349 } 00350 00351 /** 00352 * @brief Gets the file name during read and write. 00353 * @note 00354 * @param name A pointer to C-style string to be filled with file name. 00355 * @retval 00356 */ 00357 void TFTPServer::getFileName(char* name) 00358 { 00359 sprintf(name, "%s", fileName); 00360 } 00361 00362 /** 00363 * @brief Returns number of received files. 00364 * @note 00365 * @param 00366 * @retval 00367 */ 00368 int TFTPServer::fileCount() 00369 { 00370 return fileCounter; 00371 } 00372 00373 /** 00374 * @brief Creates a new connection reading a file from server. 00375 * @note Sends the file to the remote client. 00376 * Sends en error message to the remote client in case of failure. 00377 * @param buff A char array to pass data. 00378 * @retval 00379 */ 00380 void TFTPServer::connectRead(char* buff) 00381 { 00382 remoteIP = const_cast<char*>(socketAddr.get_ip_address()); 00383 remotePort = socketAddr.get_port(); 00384 blockCounter = 0; 00385 dupCounter = 0; 00386 00387 sprintf(fileName, "%s", &buff[2]); 00388 00389 if (modeOctet(buff)) 00390 file = fopen(fileName, "rb"); 00391 else 00392 file = fopen(fileName, "r"); 00393 00394 if (!file) 00395 { 00396 state = LISTENING; 00397 00398 char msg[123] = { "Could not read file: " }; 00399 00400 strcat(msg, fileName); 00401 strcat(msg, "\r\n"); 00402 sendError(msg); 00403 } 00404 else 00405 { 00406 // file ready for reading 00407 state = READING; 00408 #ifdef TFTP_DEBUG 00409 printf 00410 ( 00411 "Listening: Requested file %s from TFTP connection %d.%d.%d.%d port %d\r\n", 00412 fileName, 00413 clientIp[0], 00414 clientIp[1], 00415 clientIp[2], 00416 clientIp[3], 00417 clientPort 00418 ); 00419 #endif 00420 getBlock(); 00421 sendBlock(); 00422 } 00423 } 00424 00425 /** 00426 * @brief Creates a new connection for writing a file to the server. 00427 * @note Sends the file to the TFTP server. 00428 * Sends error message to the remote client in case of failure. 00429 * @param buff A char array to pass data. 00430 * @retval 00431 */ 00432 void TFTPServer::connectWrite(char* buff) 00433 { 00434 remoteIP = const_cast<char*>(socketAddr.get_ip_address()); 00435 remotePort = socketAddr.get_port(); 00436 ack(0); 00437 blockCounter = 0; 00438 dupCounter = 0; 00439 sprintf(fileName, "%s", &buff[2]); 00440 00441 if (modeOctet(buff)) 00442 file = fopen(fileName, "wb"); 00443 else 00444 file = fopen(fileName, "w"); 00445 00446 if (file == NULL) 00447 { 00448 sendError("Could not open file to write.\r\n"); 00449 state = LISTENING; 00450 strcpy(remoteIP, ""); 00451 } 00452 else 00453 { 00454 // file ready for writing 00455 blockCounter = 0; 00456 state = WRITING; 00457 #ifdef TFTP_DEBUG 00458 printf 00459 ( 00460 "Listening: Incoming file %s on TFTP connection from %d.%d.%d.%d clientPort %d\r\n", 00461 fileName, 00462 clientIp[0], 00463 clientIp[1], 00464 clientIp[2], 00465 clientIp[3], 00466 clientPort 00467 ); 00468 #endif 00469 } 00470 } 00471 00472 /** 00473 * @brief Gets DATA block from file on disk into memory. 00474 * @note 00475 * @param 00476 * @retval 00477 */ 00478 void TFTPServer::getBlock() 00479 { 00480 blockCounter++; 00481 00482 blockBuff[0] = 0x00; 00483 blockBuff[1] = 0x03; 00484 blockBuff[2] = blockCounter >> 8; 00485 blockBuff[3] = blockCounter & 255; 00486 blockSize = 4 + fread((void*) &blockBuff[4], 1, 512, file); 00487 } 00488 00489 /** 00490 * @brief Sends DATA block to remote client. 00491 * @note 00492 * @param 00493 * @retval 00494 */ 00495 void TFTPServer::sendBlock() 00496 { 00497 socket->sendto(socketAddr, blockBuff, blockSize); 00498 } 00499 00500 /** 00501 * @brief Compares host's IP and Port with connected remote machine. 00502 * @note 00503 * @param 00504 * @retval 00505 */ 00506 int TFTPServer::cmpHost() 00507 { 00508 char ip[17]; 00509 strcpy(ip, socketAddr.get_ip_address()); 00510 00511 int port = socketAddr.get_port(); 00512 return((strcmp(ip, remoteIP) == 0) && (port == remotePort)); 00513 } 00514 00515 /** 00516 * @brief Sends ACK to remote client. 00517 * @note 00518 * @param 00519 * @retval 00520 */ 00521 void TFTPServer::ack(int val) 00522 { 00523 char ack[4]; 00524 ack[0] = 0x00; 00525 ack[1] = 0x04; 00526 if ((val > 603135) || (val < 0)) 00527 val = 0; 00528 ack[2] = val >> 8; 00529 ack[3] = val & 255; 00530 socket->sendto(socketAddr, ack, 4); 00531 } 00532 00533 /** 00534 * @brief Sends ERROR message to remote client. 00535 * @note msg A C-style string with error message to be sent. 00536 * @param 00537 * @retval 00538 */ 00539 void TFTPServer::sendError(const char* msg) 00540 { 00541 errorBuff[0] = 0x00; 00542 errorBuff[1] = 0x05; 00543 errorBuff[2] = 0x00; 00544 errorBuff[3] = 0x00; 00545 errorBuff[4] = '\0'; // termination char 00546 strcat(&errorBuff[4], msg); 00547 00548 int len = 4 + strlen(&errorBuff[4]) + 1; 00549 socket->sendto(socketAddr, errorBuff, len); 00550 #ifdef TFTP_DEBUG 00551 printf("Error: %s\r\n", msg); 00552 #endif 00553 } 00554 00555 /** 00556 * @brief Checks if connection mode of client is octet/binary. 00557 * @note buff A char array. 00558 * @param 00559 * @retval 00560 */ 00561 int TFTPServer::modeOctet(char* buff) 00562 { 00563 int x = 2; 00564 00565 while (buff[x++] != 0); 00566 // get beginning of mode field 00567 int y = x; 00568 while (buff[y] != 0) 00569 { 00570 buff[y] = tolower(buff[y]); 00571 y++; 00572 } // make mode field lowercase 00573 00574 return(strcmp(&buff[x++], "octet") == 0); 00575 }
Generated on Thu Jul 21 2022 22:34:50 by 1.7.2