]> git.vomp.tv Git - vompserver.git/blob - tftpclient.c
15 years that line of code has been waiting to crash
[vompserver.git] / tftpclient.c
1 /*
2     Copyright 2006 Chris Tallon
3
4     This file is part of VOMP.
5
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.
10
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.
15
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.
19 */
20
21 #include <linux/limits.h>
22
23 #include "tftpclient.h"
24
25 TftpClient::TftpClient()
26 {
27   log = Log::getInstance();
28   state = 0;
29   blockNumber = 1;
30   lastCom = 0;
31 }
32
33 TftpClient::~TftpClient()
34 {
35   shutdown();
36 }
37
38 int TftpClient::shutdown()
39 {
40   if (threadIsActive()) threadCancel();
41   ds.shutdown();
42
43   if (file)
44   {
45     fclose(file);
46     file = NULL;
47   }
48
49   log->log("TftpClient", Log::DEBUG, "Shutdown");
50
51   return 1;
52 }
53
54 int TftpClient::run(char* tbaseDir, char* tpeerIP, USHORT tpeerPort, UCHAR* data, int length)
55 {
56   if (threadIsActive()) return 1;
57   log->log("TftpClient", Log::DEBUG, "Client handler started");
58
59   baseDir = tbaseDir;
60
61   strncpy(peerIP, tpeerIP, 16);
62   peerPort = tpeerPort;
63
64   if (length > 599) return 0;
65   bufferLength = length;
66   memcpy(buffer, data, length);
67
68   if (!ds.init(0))
69   {
70     log->log("TftpClient", Log::DEBUG, "DSock init error");
71     shutdown();
72     return 0;
73   }
74
75   if (!threadStart())
76   {
77     log->log("TftpClient", Log::DEBUG, "Thread start error");
78     shutdown();
79     return 0;
80   }
81
82   return 1;
83 }
84
85 void TftpClient::threadMethod()
86 {
87   threadDetach();
88
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))
92   {
93     log->log("TftpClient", Log::INFO, "threadMethod terminating connection");
94     return;
95   }
96
97   int retval;
98
99   for(int counter = 0; counter < 10; counter++)
100   {
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");
105
106     if (retval == 0)
107     {
108       log->log("TftpClient", Log::CRIT, "Wait for packet error");
109       return;
110     }
111     else if (retval == 1)
112     {
113       // 1s timer expired
114       // see if we need to retransmit a data packet
115       if (((state == 1) || (state == 2)) && (lastCom < (time(NULL) - 1)))
116       {
117         log->log("TftpClient", Log::DEBUG, "Retransmitting buffer");
118         transmitBuffer();
119       }
120
121       continue;
122     }
123     else
124     {
125       if (strcmp(ds.getFromIPA(), peerIP))
126       {
127         log->log("TftpClient", Log::ERR, "Not my client IP");
128         continue; // not from my client's IP
129       }
130
131       if (ds.getFromPort() != peerPort)
132       {
133         log->log("TftpClient", Log::ERR, "Not my client port %i %u", ds.getFromPort(), peerPort);
134         continue; // not from my client's port
135       }
136
137       if (!processMessage((UCHAR*)ds.getData(), ds.getDataLength()))
138       {
139         log->log("TftpClient", Log::INFO, "processMessage terminating connection");
140         return;
141       }
142
143       counter = 0; // that was a valid packet, reset the counter
144     }
145   }
146   log->log("TftpClient", Log::DEBUG, "Lost connection, exiting");
147 }
148
149 void TftpClient::threadPostStopCleanup()
150 {
151 //  log->log("TftpClient", Log::DEBUG, "Deleting tftpclient");
152 //  delete this; // careful
153 }
154
155 int TftpClient::processMessage(UCHAR* data, int length)
156 {
157 //  log->log("TftpClient", Log::DEBUG, "Got request");
158 //  dump(data, (USHORT)length);
159
160   if ((UINT)length < sizeof(USHORT)) return 0;
161   USHORT opcode = ntohs(*(USHORT*)data);
162   data += sizeof(USHORT);
163   length -= sizeof(USHORT);
164
165   switch(opcode)
166   {
167     case 1: // Read request
168     {
169       if (!processReadRequest(data, length)) return 0;
170       break;
171     }
172     case 2: // Write request
173     {
174       log->log("TftpClient", Log::ERR, "Client wanted to send us a file!");
175       return 0; // quit
176     }
177     case 3: // Data
178     {
179       log->log("TftpClient", Log::ERR, "Client sent a data packet!");
180       return 0; // quit
181     }
182     case 4: // Ack
183     {
184       if (!processAck(data, length)) return 0;
185       break;
186     }
187     case 5: // Error
188     {
189       break;
190     }
191
192     default:
193     {
194       log->log("TftpClient", Log::ERR, "Client TFTP protocol error");
195       return 0;
196     }
197   }
198
199   return 1;
200 }
201
202 int TftpClient::processReadRequest(UCHAR* data, int length)
203 {
204   if (state != 0) return 0;
205
206   // Safety checking - there should be two nulls in the data/length
207
208   int nullsFound = 0;
209   for(int i = 0; i < length; i++)
210   {
211     if (data[i] == '\0') nullsFound++;
212   }
213
214   if (nullsFound != 2) return 0;
215
216   char* filename = (char*)data;
217   char* mode = (char*)(data + strlen(filename) + 1);
218
219   log->log("TftpClient", Log::DEBUG, "RRQ received for %s", filename);
220
221   if (strcasecmp(mode, "octet")) return 0;
222   if (!openFile(filename)) return 0;
223   if (!sendBlock()) return 0;
224
225   lastCom = time(NULL);
226   state = 1;
227
228   return 1;
229 }
230
231 int TftpClient::processAck(UCHAR* data, int length)
232 {
233   if ((state != 1) && (state != 2)) return 0;
234
235   if (length != 2) return 0;
236
237   USHORT ackBlock = ntohs(*(USHORT*)data);
238
239   if (ackBlock == (blockNumber - 1))
240   {
241     // successful incoming packet
242     lastCom = time(NULL);
243
244 //    log->log("TftpClient", Log::DEBUG, "Ack received for block %i - success", ackBlock);
245
246     if (state == 1) // it wasn't the final block
247     {
248       sendBlock();
249     }
250     else
251     {
252       // state == 2, end of transfer. kill connection
253       log->log("TftpClient", Log::INFO, "File transfer finished");
254       fclose(file);
255       file = NULL;
256       return 0;
257     }
258   }
259   else
260   {
261     log->log("TftpClient", Log::DEBUG, "Ack received for block %i - rejected, retransmitting block\n", ackBlock);
262     transmitBuffer();
263   }
264
265   return 1;
266 }
267
268 int TftpClient::openFile(char* requestedFile)
269 {
270   char fileName[PATH_MAX];
271   strcpy(fileName, requestedFile);
272
273   for(UINT i = 0; i < strlen(fileName); i++)
274   {
275     if (fileName[i] == '/')
276     {
277       log->log("TftpClient", Log::ERR, "TFTP filename from client contained a path");
278       return 0;
279     }
280   }
281
282   char fileName2[PATH_MAX];
283   snprintf(fileName2, PATH_MAX, "%s%s", baseDir, fileName);
284
285   log->log("TftpClient", Log::INFO, "File: '%s'", fileName2);
286
287
288   file = fopen(fileName2, "r");
289   if (!file) return 0;
290   return 1;
291 }
292
293 int TftpClient::sendBlock()
294 {
295   USHORT* p;
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);
299
300   if (bufferLength < 516) // 512 + 4 header
301   {
302     // end of file
303     state = 2;
304   }
305   else
306   {
307     state = 1;
308   }
309
310   transmitBuffer();
311
312   return 1;
313 }
314
315 void TftpClient::transmitBuffer()
316 {
317
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);
321 }