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/

Dependents:   TFTPServerTest

Fork of TFTPServer by Jaap Vermaas

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers TFTPServer.cpp Source File

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 }