Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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
