2 Copyright 2006 Chris Tallon
4 This file is part of VOMP.
6 VOMP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 VOMP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with VOMP; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "tftpclient.h"
23 TftpClient::TftpClient()
25 log = Log::getInstance();
31 TftpClient::~TftpClient()
36 int TftpClient::shutdown()
38 if (threadIsActive()) threadCancel();
47 log->log("TftpClient", Log::DEBUG, "Shutdown");
52 int TftpClient::run(char* tbaseDir, char* tpeerIP, USHORT tpeerPort, UCHAR* data, int length)
54 if (threadIsActive()) return 1;
55 log->log("TftpClient", Log::DEBUG, "Client handler started");
59 strncpy(peerIP, tpeerIP, 16);
62 if (length > 599) return 0;
63 bufferLength = length;
64 memcpy(buffer, data, length);
68 log->log("TftpClient", Log::DEBUG, "DSock init error");
75 log->log("TftpClient", Log::DEBUG, "Thread start error");
83 void TftpClient::threadMethod()
87 // process the first message received by the parent listener
88 // the first incoming message is placed in buffer by above run method
89 if (!processMessage(buffer, bufferLength))
91 log->log("TftpClient", Log::INFO, "threadMethod terminating connection");
97 for(int counter = 0; counter < 10; counter++)
99 // log->log("TftpClient", Log::DEBUG, "Starting wait");
100 // timer system to expire after x seconds
101 retval = ds.waitforMessage(1);
102 // log->log("TftpClient", Log::DEBUG, "Wait finished");
106 log->log("TftpClient", Log::CRIT, "Wait for packet error");
109 else if (retval == 1)
112 // see if we need to retransmit a data packet
113 if (((state == 1) || (state == 2)) && (lastCom < (time(NULL) - 1)))
115 log->log("TftpClient", Log::DEBUG, "Retransmitting buffer");
123 if (strcmp(ds.getFromIPA(), peerIP))
125 log->log("TftpClient", Log::ERR, "Not my client IP");
126 continue; // not from my client's IP
129 if (ds.getFromPort() != peerPort)
131 log->log("TftpClient", Log::ERR, "Not my client port %i %u", ds.getFromPort(), peerPort);
132 continue; // not from my client's port
135 if (!processMessage((UCHAR*)ds.getData(), ds.getDataLength()))
137 log->log("TftpClient", Log::INFO, "processMessage terminating connection");
141 counter = 0; // that was a valid packet, reset the counter
144 log->log("TftpClient", Log::DEBUG, "Lost connection, exiting");
147 void TftpClient::threadPostStopCleanup()
149 // log->log("TftpClient", Log::DEBUG, "Deleting tftpclient");
150 // delete this; // careful
153 int TftpClient::processMessage(UCHAR* data, int length)
155 // log->log("TftpClient", Log::DEBUG, "Got request");
156 // dump(data, (USHORT)length);
158 if ((UINT)length < sizeof(USHORT)) return 0;
159 USHORT opcode = ntohs(*(USHORT*)data);
160 data += sizeof(USHORT);
161 length -= sizeof(USHORT);
165 case 1: // Read request
167 if (!processReadRequest(data, length)) return 0;
170 case 2: // Write request
172 log->log("TftpClient", Log::ERR, "Client wanted to send us a file!");
177 log->log("TftpClient", Log::ERR, "Client sent a data packet!");
182 if (!processAck(data, length)) return 0;
192 log->log("TftpClient", Log::ERR, "Client TFTP protocol error");
200 int TftpClient::processReadRequest(UCHAR* data, int length)
202 if (state != 0) return 0;
204 // Safety checking - there should be two nulls in the data/length
207 for(int i = 0; i < length; i++)
209 if (data[i] == '\0') nullsFound++;
212 if (nullsFound != 2) return 0;
214 char* filename = (char*)data;
215 char* mode = (char*)(data + strlen(filename) + 1);
217 log->log("TftpClient", Log::DEBUG, "RRQ received for %s", filename);
219 if (strcasecmp(mode, "octet")) return 0;
220 if (!openFile(filename)) return 0;
221 if (!sendBlock()) return 0;
223 lastCom = time(NULL);
229 int TftpClient::processAck(UCHAR* data, int length)
231 if ((state != 1) && (state != 2)) return 0;
233 if (length != 2) return 0;
235 USHORT ackBlock = ntohs(*(USHORT*)data);
237 if (ackBlock == (blockNumber - 1))
239 // successful incoming packet
240 lastCom = time(NULL);
242 // log->log("TftpClient", Log::DEBUG, "Ack received for block %i - success", ackBlock);
244 if (state == 1) // it wasn't the final block
250 // state == 2, end of transfer. kill connection
251 log->log("TftpClient", Log::INFO, "File transfer finished");
259 log->log("TftpClient", Log::DEBUG, "Ack received for block %i - rejected, retransmitting block\n", ackBlock);
266 int TftpClient::openFile(char* requestedFile)
268 char fileName[PATH_MAX];
269 strcpy(fileName, requestedFile);
271 for(UINT i = 0; i < strlen(fileName); i++)
273 if (fileName[i] == '/')
275 log->log("TftpClient", Log::ERR, "TFTP filename from client contained a path");
280 char fileName2[PATH_MAX];
281 snprintf(fileName2, PATH_MAX, "%s%s", baseDir, fileName);
283 log->log("TftpClient", Log::INFO, "File: '%s'", fileName2);
286 file = fopen(fileName2, "r");
291 int TftpClient::sendBlock()
293 *(USHORT*)&buffer[0] = htons(3);
294 *(USHORT*)&buffer[2] = htons(blockNumber++);
295 bufferLength = 4 + fread(&buffer[4], 1, 512, file);
297 if (bufferLength < 516) // 512 + 4 header
312 void TftpClient::transmitBuffer()
315 ds.send(peerIP, peerPort, (char*)buffer, bufferLength);
316 // dump(buffer, bufferLength);
317 // log->log("TftpClient", Log::DEBUG, "Sent block number %i to port %u", blockNumber - 1, peerPort);