This is a simple but complete TFTP server, It can receive and send files and listens on port 69. It is part of the LaOS (Laser Open Source) project: http://www.laoslaser.org/
Dependents: TFTPServerTest RMFWeb
Revision 0:3b0027b76acf, committed 2011-12-26
- Comitter:
- tuxic
- Date:
- Mon Dec 26 18:18:32 2011 +0000
- Commit message:
- First version
Changed in this revision
TFTPServer.cpp | Show annotated file Show diff for this revision Revisions of this file |
TFTPServer.h | Show annotated file Show diff for this revision Revisions of this file |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TFTPServer.cpp Mon Dec 26 18:18:32 2011 +0000 @@ -0,0 +1,403 @@ +/* + * TFTPServer.cpp + * Simple TFTP server + * + * Copyright (c) 2011 Jaap Vermaas + * + * 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" + +// 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; +} + +// destroy this instance of the tftp server +TFTPServer::~TFTPServer() { + ListenSock->resetOnEvent(); + delete(ListenSock); + delete(remote); + 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; +} + +// get current tftp status +TFTPServerState TFTPServer::State() { + return state; +} + +// Temporarily disable incoming TFTP connections +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; +} + +// 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; + + 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 { + // 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 + 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; + + sprintf(filename, "%s%s", workdir, &buff[2]); + + fp = fopen(filename, "wb"); + if (fp == NULL) { + Err("Could not open file to write", client); + state = listen; + delete(remote); + } 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 + } +} + +// 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; +} + +// 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)); +} + +// 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); +} + +// 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); + #ifdef TFTP_DEBUG + char debugmsg[256]; + sprintf(debugmsg, "Error: %s", message); + TFTP_DEBUG(debugmsg); + #endif +} + +// check if connection mode of client is octet/binary +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); +} + +// 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; +} + +// 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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TFTPServer.h Mon Dec 26 18:18:32 2011 +0000 @@ -0,0 +1,113 @@ +/** + * TFTPServer.h + * Simple TFTP server + * + * Copyright (c) 2011 Jaap Vermaas + * + * 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 binary mode transfers, no (net)ascii + * * fixed block size: 512 bytes + * + * http://spectral.mscs.mu.edu/RFC/rfc1350.html + * + * Example: + * @code + * TFTPServer *srv; + * ... + * srv = new TFTPServer(); + * ... + * @endcode + * + */ + +#ifndef _TFTPSERVER_H_ +#define _TFTPSERVER_H_ + +#include <stdio.h> +#include <ctype.h> +#include "mbed.h" +#include "SDFileSystem.h" // http://mbed.org/users/NeoBelerophon/libraries/ChaNFSSD +#include "UDPSocket.h" // http://mbed.org/users/donatien/programs/EthernetNetIf + +#define TFTP_PORT 69 +//#define TFTP_DEBUG(x) printf("%s\n\r", x); + +enum TFTPServerState { listen, reading, writing, error, suspended, deleted }; + +class TFTPServer { + +public: + // create a new tftp server, with file directory dir and + // listening on port + TFTPServer(char* dir); + // destroy this instance of the tftp server + void reset(); + // reset socket + ~TFTPServer(); + // get current tftp status + TFTPServerState State(); + // Temporarily disable incoming TFTP connections + void suspend(); + // Resume after suspension + void resume(); + // During read and write, this gives you the filename + void getFilename(char* name); + // Return number of received files + int fileCnt(); + +private: + // create a new connection reading a file from server + void ConnectRead(char* infile, Host* client); + // create a new connection writing a file to the server + void ConnectWrite(char* infile, Host* client); + // get DATA block from file on disk into memory + void getBlock(); + // send DATA block to the client + void sendBlock(); + // handle special files + // anything called *.bin is written to /local/FIRMWARE.BIN + // anything called config.txt is written to /local/config.txt + // even if workdir is not /local + int cmpHost(Host* client); + // send ACK to remote + void Ack(int val); + // send ERR message to named client + void Err(char* msg, Host* client); + // check if connection mode of client is octet/binary + int modeOctet(char* buff); + // timed routine to avoid hanging after interrupted transfers + void cleanUp(); + // event driven routines to handle incoming packets + void onListenUDPSocketEvent(UDPSocketEvent e); + + UDPSocket* ListenSock; // main listening socket (dflt: UDP port 69) + char workdir[256]; // file working directory + TFTPServerState state; // current TFTP server state + Host* remote; // connected remote Host IP and Port + int blockcnt, dupcnt; // block counter, and DUP counter + FILE* fp; // current file to read or write + char sendbuff[516]; // current DATA block; + int blocksize; // last DATA block size while sending + char filename[256]; // current (or most recent) filename + Ticker TFTPServerTimer; // timeout timer + int filecnt; // received file counter +}; + +#endif