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