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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include <linux/limits.h>
23 #include "tftpclient.h"
25 TftpClient::TftpClient()
27 log = Log::getInstance();
33 TftpClient::~TftpClient()
38 int TftpClient::shutdown()
40 if (threadIsActive()) threadCancel();
49 log->log("TftpClient", Log::DEBUG, "Shutdown");
54 int TftpClient::run(char* tbaseDir, char* tpeerIP, USHORT tpeerPort, UCHAR* data, int length)
56 if (threadIsActive()) return 1;
57 log->log("TftpClient", Log::DEBUG, "Client handler started");
61 strncpy(peerIP, tpeerIP, 16);
64 if (length > 599) return 0;
65 bufferLength = length;
66 memcpy(buffer, data, length);
70 log->log("TftpClient", Log::DEBUG, "DSock init error");
77 log->log("TftpClient", Log::DEBUG, "Thread start error");
85 void TftpClient::threadMethod()
89 // process the first message received by the parent listener
90 // the first incoming message is placed in buffer by above run method
91 if (!processMessage(buffer, bufferLength))
93 log->log("TftpClient", Log::INFO, "threadMethod terminating connection");
99 for(int counter = 0; counter < 10; counter++)
101 // log->log("TftpClient", Log::DEBUG, "Starting wait");
102 // timer system to expire after x seconds
103 retval = ds.waitforMessage(1);
104 // log->log("TftpClient", Log::DEBUG, "Wait finished");
108 log->log("TftpClient", Log::CRIT, "Wait for packet error");
111 else if (retval == 1)
114 // see if we need to retransmit a data packet
115 if (((state == 1) || (state == 2)) && (lastCom < (time(NULL) - 1)))
117 log->log("TftpClient", Log::DEBUG, "Retransmitting buffer");
125 if (strcmp(ds.getFromIPA(), peerIP))
127 log->log("TftpClient", Log::ERR, "Not my client IP");
128 continue; // not from my client's IP
131 if (ds.getFromPort() != peerPort)
133 log->log("TftpClient", Log::ERR, "Not my client port %i %u", ds.getFromPort(), peerPort);
134 continue; // not from my client's port
137 if (!processMessage((UCHAR*)ds.getData(), ds.getDataLength()))
139 log->log("TftpClient", Log::INFO, "processMessage terminating connection");
143 counter = 0; // that was a valid packet, reset the counter
146 log->log("TftpClient", Log::DEBUG, "Lost connection, exiting");
149 void TftpClient::threadPostStopCleanup()
151 // log->log("TftpClient", Log::DEBUG, "Deleting tftpclient");
152 // delete this; // careful
155 int TftpClient::processMessage(UCHAR* data, int length)
157 // log->log("TftpClient", Log::DEBUG, "Got request");
158 // dump(data, (USHORT)length);
160 if ((UINT)length < sizeof(USHORT)) return 0;
161 USHORT opcode = ntohs(*(USHORT*)data);
162 data += sizeof(USHORT);
163 length -= sizeof(USHORT);
167 case 1: // Read request
169 if (!processReadRequest(data, length)) return 0;
172 case 2: // Write request
174 log->log("TftpClient", Log::ERR, "Client wanted to send us a file!");
179 log->log("TftpClient", Log::ERR, "Client sent a data packet!");
184 if (!processAck(data, length)) return 0;
194 log->log("TftpClient", Log::ERR, "Client TFTP protocol error");
202 int TftpClient::processReadRequest(UCHAR* data, int length)
204 if (state != 0) return 0;
206 // Safety checking - there should be two nulls in the data/length
209 for(int i = 0; i < length; i++)
211 if (data[i] == '\0') nullsFound++;
214 if (nullsFound != 2) return 0;
216 char* filename = (char*)data;
217 char* mode = (char*)(data + strlen(filename) + 1);
219 log->log("TftpClient", Log::DEBUG, "RRQ received for %s", filename);
221 if (strcasecmp(mode, "octet")) return 0;
222 if (!openFile(filename)) return 0;
223 if (!sendBlock()) return 0;
225 lastCom = time(NULL);
231 int TftpClient::processAck(UCHAR* data, int length)
233 if ((state != 1) && (state != 2)) return 0;
235 if (length != 2) return 0;
237 USHORT ackBlock = ntohs(*(USHORT*)data);
239 if (ackBlock == (blockNumber - 1))
241 // successful incoming packet
242 lastCom = time(NULL);
244 // log->log("TftpClient", Log::DEBUG, "Ack received for block %i - success", ackBlock);
246 if (state == 1) // it wasn't the final block
252 // state == 2, end of transfer. kill connection
253 log->log("TftpClient", Log::INFO, "File transfer finished");
261 log->log("TftpClient", Log::DEBUG, "Ack received for block %i - rejected, retransmitting block\n", ackBlock);
268 int TftpClient::openFile(char* requestedFile)
270 char fileName[PATH_MAX];
271 strcpy(fileName, requestedFile);
273 for(UINT i = 0; i < strlen(fileName); i++)
275 if (fileName[i] == '/')
277 log->log("TftpClient", Log::ERR, "TFTP filename from client contained a path");
282 char fileName2[PATH_MAX];
283 snprintf(fileName2, PATH_MAX, "%s%s", baseDir, fileName);
285 log->log("TftpClient", Log::INFO, "File: '%s'", fileName2);
288 file = fopen(fileName2, "r");
293 int TftpClient::sendBlock()
296 p = (USHORT*)&buffer[0]; *p = htons(3);
297 p = (USHORT*)&buffer[2]; *p = htons(blockNumber++);
298 bufferLength = 4 + fread(&buffer[4], 1, 512, file);
300 if (bufferLength < 516) // 512 + 4 header
315 void TftpClient::transmitBuffer()
318 ds.send(peerIP, peerPort, (char*)buffer, bufferLength);
319 // dump(buffer, bufferLength);
320 // log->log("TftpClient", Log::DEBUG, "Sent block number %i to port %u", blockNumber - 1, peerPort);