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
TFTPServer.cpp
- Committer:
- tuxic
- Date:
- 2011-12-26
- Revision:
- 0:3b0027b76acf
File content as of revision 0:3b0027b76acf:
/* * 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); }