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
- Committer:
- hudakz
- Date:
- 2018-03-16
- Revision:
- 1:9c973065a97e
- Parent:
- 0:3b0027b76acf
- Child:
- 2:f7c0fbc8c5aa
File content as of revision 1:9c973065a97e:
/*
* TFTPServer.cpp
* Simple TFTP server
*
* Copyright (c) 2011 Jaap Vermaas
* Modified for MBED-OS5 by Zoltan Hudak 2018
*
* 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/>.
*
*/
#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:
{ }
} // 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 for passing input 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 for passing input 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);
}
