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
Diff: TFTPServer.cpp
- Revision:
- 1:9c973065a97e
- Parent:
- 0:3b0027b76acf
- Child:
- 2:f7c0fbc8c5aa
--- a/TFTPServer.cpp Mon Dec 26 18:18:32 2011 +0000
+++ b/TFTPServer.cpp Fri Mar 16 19:22:10 2018 +0000
@@ -3,6 +3,7 @@
* 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)
*
@@ -21,383 +22,551 @@
*
*/
#include "TFTPServer.h"
+#include "EthernetInterface.h"
-// create a new tftp server, with file directory dir and
-// listening on port
-TFTPServer::TFTPServer(char* dir) {
- ListenSock = new UDPSocket();
- ListenSock->setOnEvent(this, &TFTPServer::onListenUDPSocketEvent);
- state = listen;
- if (ListenSock->bind(Host(IpAddr(), TFTP_PORT)))
- state = error;
- TFTPServerTimer.attach(this, &TFTPServer::cleanUp, 5000);
- sprintf(workdir, "%s", dir);
- filecnt = 0;
+//#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;
}
-// destroy this instance of the tftp server
-TFTPServer::~TFTPServer() {
- ListenSock->resetOnEvent();
- delete(ListenSock);
- delete(remote);
- state = deleted;
+/**
+ * @brief Destroys this instance of the TFTP server.
+ * @note
+ * @param
+ * @retval
+ */
+TFTPServer::~TFTPServer()
+{
+ socket->close();
+ delete(socket);
+ strcpy(remoteIP, "");
+ state = DELETED;
}
-void TFTPServer::reset() {
- ListenSock->resetOnEvent();
- delete(ListenSock);
- delete(remote);
- TFTPServerTimer.detach();
- ListenSock = new UDPSocket();
- ListenSock->setOnEvent(this, &TFTPServer::onListenUDPSocketEvent);
- state = listen;
- if (ListenSock->bind(Host(IpAddr(), TFTP_PORT)))
- state = error;
- TFTPServerTimer.attach(this, &TFTPServer::cleanUp, 5000);
- sprintf(filename, "");
- filecnt = 0;
+/**
+ * @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;
}
-// get current tftp status
-TFTPServerState TFTPServer::State() {
+/**
+ * @brief Gets current TFTP status.
+ * @note
+ * @param
+ * @retval
+ */
+TFTPServer::State TFTPServer::getState()
+{
return state;
}
-// Temporarily disable incoming TFTP connections
-void TFTPServer::suspend() {
- state = suspended;
+/**
+ * @brief Temporarily disables incoming TFTP connections.
+ * @note
+ * @param
+ * @retval
+ */
+void TFTPServer::suspend()
+{
+ state = SUSPENDED;
}
-// Resume after suspension
-void TFTPServer::resume() {
- if (state == suspended)
- state = listen;
-}
-
-// During read and write, this gives you the filename
-void TFTPServer::getFilename(char* name) {
- sprintf(name, "%s", filename);
-}
-
-// return number of received files
-int TFTPServer::fileCnt() {
- return filecnt;
+/**
+ * @brief Resumes incoming TFTP connection after suspension.
+ * @note
+ * @param
+ * @retval
+ */
+void TFTPServer::resume()
+{
+ if (state == SUSPENDED)
+ state = LISTENING;
}
-// create a new connection reading a file from server
-void TFTPServer::ConnectRead(char* buff, Host* client) {
- IpAddr clientIp = client->getIp();
- int clientPort = client->getPort();
- remote = new Host(clientIp, clientPort);
- Ack(0);
- blockcnt = 0;
- dupcnt = 0;
+/**
+ * @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
+ }
- sprintf(filename, "%s%s", workdir, &buff[2]);
-
- fp = fopen(filename, "rb");
- if (fp == NULL) {
- state = listen;
- delete(remote);
- Err("Could not read file", client);
- } else {
+ 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
- blockcnt = 0;
- state = reading;
- #ifdef TFTP_DEBUG
- char debugmsg[256];
- sprintf(debugmsg, "Listen: Requested file %s from TFTP connection %d.%d.%d.%d port %d",
- filename, clientIp[0], clientIp[1], clientIp[2], clientIp[3], clientPort);
- TFTP_DEBUG(debugmsg);
- #endif
+ 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();
}
}
-// create a new connection writing a file to the server
-void TFTPServer::ConnectWrite(char* buff, Host* client) {
- IpAddr clientIp = client->getIp();
- int clientPort = client->getPort();
- remote = new Host(clientIp, clientPort);
- Ack(0);
- blockcnt = 0;
- dupcnt = 0;
+/**
+ * @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]);
- sprintf(filename, "%s%s", workdir, &buff[2]);
+ if (modeOctet(buff))
+ file = fopen(fileName, "wb");
+ else
+ file = fopen(fileName, "w");
- fp = fopen(filename, "wb");
- if (fp == NULL) {
- Err("Could not open file to write", client);
- state = listen;
- delete(remote);
- } else {
+ if (file == NULL)
+ {
+ sendError("Could not open file to write.\r\n");
+ state = LISTENING;
+ strcpy(remoteIP, "");
+ }
+ else
+ {
// file ready for writing
- blockcnt = 0;
- state = writing;
- #ifdef TFTP_DEBUG
- char debugmsg[256];
- sprintf(debugmsg, "Listen: Incoming file %s on TFTP connection from %d.%d.%d.%d clientPort %d",
- filename, clientIp[0], clientIp[1], clientIp[2], clientIp[3], clientPort);
- TFTP_DEBUG(debugmsg);
- #endif
+ 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
}
}
-// get DATA block from file on disk into memory
-void TFTPServer::getBlock() {
- blockcnt++;
- char *p;
- p = &sendbuff[4];
- int len = fread(p, 1, 512, fp);
- sendbuff[0] = 0x00;
- sendbuff[1] = 0x03;
- sendbuff[2] = blockcnt >> 8;
- sendbuff[3] = blockcnt & 255;
- blocksize = len+4;
+/**
+ * @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);
}
-// send DATA block to the client
-void TFTPServer::sendBlock() {
- ListenSock->sendto(sendbuff, blocksize, remote);
-}
-
-
-// compare host IP and Port with connected remote machine
-int TFTPServer::cmpHost(Host* client) {
- IpAddr clientIp = client->getIp();
- IpAddr remoteIp = remote->getIp();
- int clientPort = client->getPort();
- int remotePort = remote->getPort();
- return ((clientIp == remoteIp) && (clientPort == remotePort));
+/**
+ * @brief Sends DATA block to remote client.
+ * @note
+ * @param
+ * @retval
+ */
+void TFTPServer::sendBlock()
+{
+ socket->sendto(socketAddr, blockBuff, blockSize);
}
-// send ACK to remote
-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;
- ListenSock->sendto(ack, 4, remote);
+/**
+ * @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));
}
-// send ERR message to named client
-void TFTPServer::Err(char* msg, Host* client) {
- char message[32];
- strncpy(message, msg, 32);
- char err[37];
- sprintf(err, "0000%s0", message);
- err[0] = 0x00;
- err[1] = 0x05;
- err[2]=0x00;
- err[3]=0x00;
- int len = strlen(err);
- err[len-1] = 0x00;
- ListenSock->sendto(err, len, client);
+/**
+ * @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
- char debugmsg[256];
- sprintf(debugmsg, "Error: %s", message);
- TFTP_DEBUG(debugmsg);
+ printf("Error: %s\r\n", msg);
#endif
}
-// check if connection mode of client is octet/binary
-int TFTPServer::modeOctet(char* buff) {
+/**
+ * @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
+
+ while (buff[x++] != 0);
+ // get beginning of mode field
int y = x;
- while (buff[y] != 0) {
+ while (buff[y] != 0)
+ {
buff[y] = tolower(buff[y]);
y++;
- } // make mode field lowercase
- return (strcmp(&buff[x++], "octet") == 0);
-}
-
-// timed routine to avoid hanging after interrupted transfers
-void TFTPServer::cleanUp() {
- static int lastcnt;
- switch (state) {
- case reading:
- if (lastcnt == blockcnt) {
- fclose(fp);
- state=listen;
- delete(remote);
- }
- break;
- case writing:
- if (lastcnt == blockcnt) {
- fclose(fp);
- state = listen;
- remove(filename);
- delete(remote);
- }
- break;
- } // state
- lastcnt = blockcnt;
-}
+ } // make mode field lowercase
-// event driven routines to handle incoming packets
-void TFTPServer::onListenUDPSocketEvent(UDPSocketEvent e) {
- extern SDFileSystem sd;
- Host* client = new Host();
- char buff[516];
- if (e == UDPSOCKET_READABLE) {
- switch (state) {
- case listen:
- if (int len = ListenSock->recvfrom(buff, 516, client)) {
- switch (buff[1]) {
- case 0x01: // RRQ
- if (modeOctet(buff))
- ConnectRead(buff, client);
- else
- Err("Not in octet mode", client);
- break;
- case 0x02: // WRQ
- if (modeOctet(buff))
- ConnectWrite(buff, client);
- else
- Err("Not in octet mode", client);
- break;
- case 0x03: // DATA before connection established
- Err("No data expected", client);
- break;
- case 0x04: // ACK before connection established
- Err("No ack expected", client);
- break;
- case 0x05: // ERROR packet received
- #ifdef TFTP_DEBUG
- TFTP_DEBUG("TFTP Eror received\n\r");
- #endif
- break;
- default: // unknown TFTP packet type
- Err("Unknown TFTP packet type", client);
- break;
- } // switch buff[1]
- } // recvfrom
- break; // case listen
- case reading:
- while (int len = ListenSock->recvfrom(buff, 516, client) ) {
- switch (buff[1]) {
- case 0x01:
- // if this is the receiving host, send first packet again
- if ((cmpHost(client) && blockcnt==1)) {
- sendBlock();
- dupcnt++;
- }
- if (dupcnt>10) { // too many dups, stop sending
- fclose(fp);
- state=listen;
- delete(remote);
- }
- break;
- case 0x02:
- // this should never happen, ignore
- break; // case 0x02
- case 0x03:
- // we are the sending side, ignore
- break;
- case 0x04:
- // last packet received, send next if there is one
- dupcnt = 0;
- if (blocksize == 516) {
- getBlock();
- sendBlock();
- } else { //EOF
- fclose(fp);
- state = listen;
- delete(remote);
- }
- break;
- default: // this includes 0x05 errors
- fclose(fp);
- state = listen;
- delete(remote);
- break;
- } // switch (buff[1])
- } // while
- break; // reading
- case writing:
- while (int len = ListenSock->recvfrom(buff, 516, client) ) {
- switch (buff[1]) {
- case 0x02:
- // if this is a returning host, send ack again
- if (cmpHost(client)) {
- Ack(0);
- #ifdef TFTP_DEBUG
- TFTP_DEBUG("Resending Ack on WRQ");
- #endif
- }
- break; // case 0x02
- case 0x03:
- if (cmpHost(client)) { // check if this is our partner
- int block = (buff[2] << 8) + buff[3];
- if ((blockcnt+1) == block) {
- Ack(block);
- // new packet
- char *data = &buff[4];
- fwrite(data, 1,len-4, fp);
- blockcnt++;
- dupcnt = 0;
- } else {
- if ((blockcnt+1) < block) { // high block nr
- // we missed a packet, error
- #ifdef TFTP_DEBUG
- TFTP_DEBUG("Missed packet!");
- #endif
- fclose(fp);
- state = listen;
- remove(filename);
- delete(remote);
- } else { // duplicate packet, do nothing
- #ifdef TFTP_DEBUG
- char debugmsg[256];
- sprintf(debugmsg, "Dupblicate packet %d", blockcnt);
- TFTP_DEBUG(debugmsg);
- #endif
- if (dupcnt > 10) {
- Err("Too many dups", client);
- fclose(fp);
- remove(filename);
- state = listen;
- } else {
- Ack(blockcnt);
- }
- dupcnt++;
- }
- }
- //printf ("Read packet %d with blocksize = %d\n\r", blockcnt, len);
- if (len<516) {
- #ifdef TFTP_DEBUG
- char debugmsg[256];
- sprintf(debugmsg, "Read last block %d", len);
- TFTP_DEBUG(debugmsg);
- #endif
- fclose(fp);
- state = listen;
- delete(remote);
- filecnt++;
- }
- break; // case 0x03
- } else // if cmpHost
- Err("Wrong IP/Port: Not your connection!", client);
- break; // case 0x03
- default:
- Err("No idea why you're sending me this!", client);
- break; // default
- } // switch (buff[1])
- } // while
- break; // writing
- case suspended:
- if (int len = ListenSock->recvfrom(buff, 516, client))
- Err("Packet received on suspended socket, discarded", client);
- break;
- } // state
- } else {
- #ifdef TFTP_DEBUG
- TFTP_DEBUG("TFTP unknown UDP status");
- #endif
- }
- delete(client);
+ return(strcmp(&buff[x++], "octet") == 0);
}
