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