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
Import programTFTPServerTest
This is an example code for using the TFTPServer library.
A simple TFTP server. NOTE: Supports only octet (raw 8 bit bytes) mode transfers.
TFTPServer.cpp
- Committer:
- hudakz
- Date:
- 2018-03-20
- Revision:
- 2:f7c0fbc8c5aa
- Parent:
- 1:9c973065a97e
File content as of revision 2:f7c0fbc8c5aa:
/* * TFTPServer.cpp * Simple TFTP server * * Copyright (c) 2011 Jaap Vermaas * Modified by Zoltan Hudak 2018 for MBED-OS5 * * This file is part of the LaOS project (see: http://wiki.laoslaser.org) * * LaOS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * LaOS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with LaOS. If not, see <http://www.gnu.org/licenses/>. * * Minimal TFTP Server * * Receive and send files via TFTP * * Server handles only one transfer at a time * * Supports only octet (raw 8 bit bytes) mode transfers * * fixed block size: 512 bytes * */ #include "TFTPServer.h" #include "EthernetInterface.h" //#define TFTP_DEBUG /** * @brief Creates a new TFTP server listening on myPort. * @note * @param net A pointer to EthernetInterface object. * @param port A port to listen on (defaults to 69). * @retval */ TFTPServer::TFTPServer(EthernetInterface* net, uint16_t myPort /* = 69 */ ) { port = myPort; #ifdef TFTP_DEBUG printf("TFTPServer(): port=%d\r\n", myPort); #endif socket = new UDPSocket(net); state = LISTENING; if (socket->bind(port)) { socketAddr = SocketAddress(0, port); state = ERROR; } #ifdef TFTP_DEBUG printf("FTP server state = %d\r\n", getState()); #endif socket->set_blocking(false); fileCounter = 0; } /** * @brief Destroys this instance of the TFTP server. * @note * @param * @retval */ TFTPServer::~TFTPServer() { socket->close(); delete(socket); strcpy(remoteIP, ""); state = DELETED; } /** * @brief Resets the TFTP server. * @note * @param * @retval */ void TFTPServer::reset() { socket->close(); delete(socket); strcpy(remoteIP, ""); socket = new UDPSocket(); state = LISTENING; if (socket->bind(port)) { socketAddr = SocketAddress(0, port); state = ERROR; } socket->set_blocking(false); strcpy(fileName, ""); fileCounter = 0; } /** * @brief Gets current TFTP status. * @note * @param * @retval */ TFTPServer::State TFTPServer::getState() { return state; } /** * @brief Temporarily disables incoming TFTP connections. * @note * @param * @retval */ void TFTPServer::suspend() { state = SUSPENDED; } /** * @brief Resumes incoming TFTP connection after suspension. * @note * @param * @retval */ void TFTPServer::resume() { if (state == SUSPENDED) state = LISTENING; } /** * @brief Polls for data or new connection. * @note * @param * @retval */ void TFTPServer::poll() { if ((state == SUSPENDED) || (state == DELETED) || (state == ERROR)) return; socket->set_blocking(false); char buff[516]; int len = socket->recvfrom(&socketAddr, buff, sizeof(buff)); if (len <= 0) return; #ifdef TFTP_DEBUG printf("Got block with size %d.\n\r", len); #endif switch (state) { case LISTENING: { switch (buff[1]) { case 0x01: // RRQ connectRead(buff); break; case 0x02: // WRQ connectWrite(buff); break; case 0x03: // DATA before connection established sendError("No data expected.\r\n"); break; case 0x04: // ACK before connection established sendError("No ack expected.\r\n"); break; case 0x05: // ERROR packet received #ifdef TFTP_DEBUG printf("TFTP Eror received.\r\n"); #endif break; default: // unknown TFTP packet type sendError("Unknown TFTP packet type.\r\n"); break; } // switch buff[1] break; // case listening } case READING: { if (cmpHost()) { switch (buff[1]) { case 0x01: // if this is the receiving host, send first packet again if (blockCounter == 1) { ack(0); dupCounter++; } if (dupCounter > 10) { // too many dups, stop sending sendError("Too many dups"); fclose(file); state = LISTENING; strcpy(remoteIP, ""); } break; case 0x02: // this should never happen, ignore sendError("WRQ received on open read socket"); fclose(file); state = LISTENING; strcpy(remoteIP, ""); break; case 0x03: // we are the sending side, ignore sendError("Received data package on sending socket"); fclose(file); state = LISTENING; strcpy(remoteIP, ""); break; case 0x04: // last packet received, send next if there is one dupCounter = 0; if (blockSize == 516) { getBlock(); sendBlock(); } else { //EOF fclose(file); state = LISTENING; strcpy(remoteIP, ""); } break; default: // this includes 0x05 errors sendError("Received 0x05 error message"); fclose(file); state = LISTENING; strcpy(remoteIP, ""); break; } // switch (buff[1]) } #ifdef TFTP_DEBUG else printf("Ignoring package from other remote client during RRQ.\r\n"); #endif break; // reading } case WRITING: { if (cmpHost()) { switch (buff[1]) { case 0x02: { // if this is a returning host, send ack again ack(0); #ifdef TFTP_DEBUG TFTP_DEBUG("Resending Ack on WRQ.\r\n"); #endif break; // case 0x02 } case 0x03: { int block = (buff[2] << 8) + buff[3]; if ((blockCounter + 1) == block) { ack(block); // new packet char* data = &buff[4]; fwrite(data, 1, len - 4, file); blockCounter++; dupCounter = 0; } else { // mismatch in block nr if ((blockCounter + 1) < block) { // too high sendError("Packet count mismatch"); fclose(file); state = LISTENING; remove(fileName); strcpy(remoteIP, ""); } else { // duplicate packet, send ACK again if (dupCounter > 10) { sendError("Too many dups"); fclose(file); remove(fileName); state = LISTENING; } else { ack(blockCounter); dupCounter++; } } } if (len < 516) { ack(blockCounter); fclose(file); state = LISTENING; strcpy(remoteIP, ""); fileCounter++; #ifdef TFTP_DEBUG printf("File receive finished.\r\n"); #endif } break; // case 0x03 } default: { sendError("No idea why you're sending me this!"); break; // default } } // switch (buff[1]) } #ifdef TFTP_DEBUG else printf("Ignoring packege from other remote client during WRQ.\r\n"); #endif break; // writing } case ERROR: case SUSPENDED: case DELETED: default: { } } // state } /** * @brief Gets the file name during read and write. * @note * @param name A pointer to C-style string to be filled with file name. * @retval */ void TFTPServer::getFileName(char* name) { sprintf(name, "%s", fileName); } /** * @brief Returns number of received files. * @note * @param * @retval */ int TFTPServer::fileCount() { return fileCounter; } /** * @brief Creates a new connection reading a file from server. * @note Sends the file to the remote client. * Sends en error message to the remote client in case of failure. * @param buff A char array to pass data. * @retval */ void TFTPServer::connectRead(char* buff) { remoteIP = const_cast<char*>(socketAddr.get_ip_address()); remotePort = socketAddr.get_port(); blockCounter = 0; dupCounter = 0; sprintf(fileName, "%s", &buff[2]); if (modeOctet(buff)) file = fopen(fileName, "rb"); else file = fopen(fileName, "r"); if (!file) { state = LISTENING; char msg[123] = { "Could not read file: " }; strcat(msg, fileName); strcat(msg, "\r\n"); sendError(msg); } else { // file ready for reading state = READING; #ifdef TFTP_DEBUG printf ( "Listening: Requested file %s from TFTP connection %d.%d.%d.%d port %d\r\n", fileName, clientIp[0], clientIp[1], clientIp[2], clientIp[3], clientPort ); #endif getBlock(); sendBlock(); } } /** * @brief Creates a new connection for writing a file to the server. * @note Sends the file to the TFTP server. * Sends error message to the remote client in case of failure. * @param buff A char array to pass data. * @retval */ void TFTPServer::connectWrite(char* buff) { remoteIP = const_cast<char*>(socketAddr.get_ip_address()); remotePort = socketAddr.get_port(); ack(0); blockCounter = 0; dupCounter = 0; sprintf(fileName, "%s", &buff[2]); if (modeOctet(buff)) file = fopen(fileName, "wb"); else file = fopen(fileName, "w"); if (file == NULL) { sendError("Could not open file to write.\r\n"); state = LISTENING; strcpy(remoteIP, ""); } else { // file ready for writing blockCounter = 0; state = WRITING; #ifdef TFTP_DEBUG printf ( "Listening: Incoming file %s on TFTP connection from %d.%d.%d.%d clientPort %d\r\n", fileName, clientIp[0], clientIp[1], clientIp[2], clientIp[3], clientPort ); #endif } } /** * @brief Gets DATA block from file on disk into memory. * @note * @param * @retval */ void TFTPServer::getBlock() { blockCounter++; blockBuff[0] = 0x00; blockBuff[1] = 0x03; blockBuff[2] = blockCounter >> 8; blockBuff[3] = blockCounter & 255; blockSize = 4 + fread((void*) &blockBuff[4], 1, 512, file); } /** * @brief Sends DATA block to remote client. * @note * @param * @retval */ void TFTPServer::sendBlock() { socket->sendto(socketAddr, blockBuff, blockSize); } /** * @brief Compares host's IP and Port with connected remote machine. * @note * @param * @retval */ int TFTPServer::cmpHost() { char ip[17]; strcpy(ip, socketAddr.get_ip_address()); int port = socketAddr.get_port(); return((strcmp(ip, remoteIP) == 0) && (port == remotePort)); } /** * @brief Sends ACK to remote client. * @note * @param * @retval */ void TFTPServer::ack(int val) { char ack[4]; ack[0] = 0x00; ack[1] = 0x04; if ((val > 603135) || (val < 0)) val = 0; ack[2] = val >> 8; ack[3] = val & 255; socket->sendto(socketAddr, ack, 4); } /** * @brief Sends ERROR message to remote client. * @note msg A C-style string with error message to be sent. * @param * @retval */ void TFTPServer::sendError(const char* msg) { errorBuff[0] = 0x00; errorBuff[1] = 0x05; errorBuff[2] = 0x00; errorBuff[3] = 0x00; errorBuff[4] = '\0'; // termination char strcat(&errorBuff[4], msg); int len = 4 + strlen(&errorBuff[4]) + 1; socket->sendto(socketAddr, errorBuff, len); #ifdef TFTP_DEBUG printf("Error: %s\r\n", msg); #endif } /** * @brief Checks if connection mode of client is octet/binary. * @note buff A char array. * @param * @retval */ int TFTPServer::modeOctet(char* buff) { int x = 2; while (buff[x++] != 0); // get beginning of mode field int y = x; while (buff[y] != 0) { buff[y] = tolower(buff[y]); y++; } // make mode field lowercase return(strcmp(&buff[x++], "octet") == 0); }