.cvsignore.
[wvapps.git] / wvtftp / wvtftpbase.cc
blob70bf3da2e7fefa9996b5e72a556e249ef05f9478
1 /*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2004 Net Integration Technologies, Inc.
4 */
5 #include "wvtftpbase.h"
6 #include "strutils.h"
7 #include "wvtimeutils.h"
8 #include <assert.h>
10 PktTime::PktTime(int _pktclump)
12 pktclump = _pktclump;
13 times = new struct timeval[pktclump];
14 idx = 0;
17 PktTime::~PktTime()
19 deletev times;
22 void PktTime::set(int pktnum, struct timeval &tv)
24 assert(pktnum >= idx);
25 if ((pktnum - idx) >= pktclump)
27 int d = pktnum - pktclump - idx + 1;
28 idx += d;
29 if (d < pktclump && d > 0)
31 memmove(times, times + d, (pktclump - d) * sizeof(struct
32 timeval));
33 memset(times + pktclump - d, 0, d * sizeof(struct timeval));
35 else if (d >= pktclump)
36 memset(times, 0, pktclump * sizeof(struct timeval));
38 times[pktnum - idx].tv_sec = tv.tv_sec;
39 times[pktnum - idx].tv_usec = tv.tv_usec;
42 struct timeval *PktTime::get(int pktnum)
44 if (pktnum < idx || pktnum > (idx + pktclump))
45 return NULL;
46 return &times[pktnum - idx];
49 WvTFTPBase::WvTFTPBase(int _tftp_tick, int port)
50 : WvUDPStream(port, WvIPPortAddr()), conns(5), log("WvTFTP", WvLog::Debug),
51 tftp_tick(_tftp_tick)
55 WvTFTPBase::~WvTFTPBase()
59 void WvTFTPBase::dump_pkt()
61 //log(WvLog::Debug5, "Packet:\n");
62 //log(WvLog::Debug5, hexdump_buffer(packet, packetsize));
65 void WvTFTPBase::handle_packet()
67 log(WvLog::Debug4, "Handling packet from %s\n", remaddr);
69 TFTPConn *c = conns[remaddr];
70 TFTPOpcode opcode = (TFTPOpcode)(packet[0] * 256 + packet[1]);
72 if (opcode == ERROR)
74 log(WvLog::Warning, "Received error packet; aborting.\n");
75 conns.remove(c);
76 return;
79 if (c->direction == tftpread)
81 // Packet should be an ack.
82 if (opcode != ACK)
84 log(WvLog::Warning, "Expected ACK (read); aborting.\n");
85 send_err(4);
86 conns.remove(c);
87 return;
90 c->mult = 1;
91 int small_blocknum = (unsigned char)(packet[2]) * 256 +
92 (unsigned char)(packet[3]);
93 int mult = c->unack / 65536;
94 int blocknum = mult * 65536 + small_blocknum;
95 if (blocknum > c->unack + 32000)
96 blocknum = (mult - 1) * 65536 + small_blocknum;
97 log(WvLog::Debug5,
98 "handle: got small_blocknum %s; unack is %s; lastsent is %s; "
99 "blocknum is %s.\n",
100 small_blocknum, c->unack, c->lastsent, blocknum);
102 if (blocknum == 0 && c->send_oack)
104 // treat the first block specially if we need to send an option
105 // acknowledgement.
106 c->send_oack = false;
107 log(WvLog::Debug5, "last sent: %s unack: %s pktclump: %s\n",
108 c->lastsent, c->unack, c->pktclump);
109 int pktsremain = c->lastsent - c->unack;
110 while (pktsremain < c->pktclump - 1)
112 log(WvLog::Debug5, "result is %s\n", pktsremain);
113 log(WvLog::Debug5, "send\n");
114 send_data(c);
115 if (c->donefile)
116 break;
117 pktsremain = c->lastsent - c->unack;
120 c->numtimeouts = 0;
122 else if (blocknum != (c->unack - 1)) // ignore duplicate ACK
124 // Add rtt to cumulative sum.
125 if (blocknum == c->unack && blocknum > c->timed_out_ignore)
127 struct timeval tv = wvtime();
129 time_t rtt = msecdiff(tv, *(c->pkttimes->get(blocknum)));
130 log(WvLog::Debug4, "rtt is %s.\n", rtt);
132 c->rtt += rtt;
133 c->total_packets++;
136 if (blocknum == c->unack && blocknum == c->lastsent
137 && c->donefile)
139 // transfer completed if we haven't sent any packets last
140 // time we acked, and this is the right ack.
141 log(WvLog::Info, "File transferred successfully.\n");
142 log(WvLog::Info, "Average rtt was %s ms.\n", c->rtt /
143 blocknum);
145 if (c->alias_once)
146 c->alias.remove();
148 conns.remove(c);
149 c = NULL;
151 else if (blocknum == c->unack)
153 // send the next packet if the first unacked packet is the
154 // one being acked.
155 c->unack = blocknum + 1;
157 while ((c->lastsent - c->unack) < c->pktclump - 1)
159 if (c->donefile)
160 break;
161 send_data(c);
162 c->numtimeouts = 0;
167 // 'c' might be invalid here if it was deleted!
169 else
171 // Packet should be data.
172 if (opcode != DATA)
174 log(WvLog::Warning, "Badly formed packet (write); aborting.\n");
175 send_err(4);
176 conns.remove(c);
177 return;
180 c->mult = 1;
181 int small_blocknum = (unsigned char)(packet[2]) * 256 +
182 (unsigned char)(packet[3]);
183 int mult = c->lastsent / 65536;
184 int blocknum = mult * 65536 + small_blocknum;
185 if (blocknum < c->lastsent - 32000)
186 blocknum = (mult + 1) * 65536 + small_blocknum;
188 if (blocknum == c->lastsent + 1)
190 unsigned int data_packetsize = packetsize;
191 fwrite(&packet[4], sizeof(char), data_packetsize-4, c->tftpfile);
193 // Add rtt to cumulative sum.
194 if (blocknum > c->timed_out_ignore)
196 struct timeval tv = wvtime();
197 time_t rtt = msecdiff(tv, *(c->pkttimes->get(blocknum - 1)));
198 log("rtt is %s.\n", rtt);
200 c->rtt += rtt;
201 c->total_packets++;
203 send_ack(c);
205 if (data_packetsize < c->blksize + 4)
207 log(WvLog::Info, "File transferred successfully.\n");
208 log(WvLog::Info, "Average rtt was %s ms.\n", c->rtt /
209 blocknum);
210 conns.remove(c);
211 c = NULL;
217 // Send out the next packet, unless resend is true, in which case
218 // send out packets unack through lastsent.
219 void WvTFTPBase::send_data(TFTPConn *c, bool resend)
221 // log("Sending data.\n");
222 int firstpkt, lastpkt;
224 if (resend)
226 firstpkt = c->unack;
227 lastpkt = c->lastsent;
228 fseek(c->tftpfile, (firstpkt - 1) * c->blksize, SEEK_SET);
230 else
232 c->lastsent++;
233 firstpkt = c->lastsent;
234 lastpkt = c->lastsent;
237 log(WvLog::Debug5, "send_data: sending packets %s->%s\n",
238 firstpkt, lastpkt);
240 for (int pktcount = firstpkt; pktcount <= lastpkt; pktcount++)
242 size_t datalen = 0;
243 packetsize = 4;
245 // DATA opcode
246 packet[0] = 0;
247 packet[1] = 3;
248 // block num
249 packet[2] = (pktcount % 65536) / 256;
250 packet[3] = (pktcount % 65536) % 256;
251 // data
252 datalen = fread(&packet[4], sizeof(char), c->blksize, c->tftpfile);
253 log(WvLog::Debug5, "send_data: read %s bytes from file.\n", datalen);
254 if (datalen < c->blksize)
255 c->donefile = true;
256 packetsize += datalen;
257 // log(WvLog::Debug5, "Sending ");
258 dump_pkt();
259 setdest(c->remote); // often redundant (after reading), but safer
260 write(packet, packetsize);
262 struct timeval tv = wvtime();
263 c->pkttimes->set(pktcount, tv);
267 // Send an acknowledgement.
268 void WvTFTPBase::send_ack(TFTPConn *c, bool resend)
270 if (!resend)
271 c->lastsent++;
273 packetsize = 4;
274 packet[0] = 0;
275 packet[1] = 4;
276 packet[2] = (c->lastsent % 65536) / 256;
277 packet[3] = (c->lastsent % 65536) % 256;
278 // log(WvLog::Debug5, "Sending ");
279 dump_pkt();
280 write(packet, packetsize);
282 struct timeval tv = wvtime();
283 log(WvLog::Debug4, "Setting %s\n", c->lastsent);
284 c->pkttimes->set(c->lastsent, tv);
287 void WvTFTPBase::send_err(char errcode, WvString errmsg)
289 packetsize = 4;
291 packet[0] = 0;
292 packet[1] = 5;
293 packet[2] = 0;
294 packet[3] = errcode;
296 if (errmsg == "")
298 switch (errcode)
300 case 1: errmsg = "File not found.";
301 break;
302 case 2: errmsg = "Access violation.";
303 break;
304 case 3: errmsg = "Disk full or allocation exceeded.";
305 break;
306 case 4: errmsg = "Illegal TFTP operation.";
307 break;
308 case 5: errmsg = "Unknown transfer ID.";
309 break;
310 case 6: errmsg = "File already exists.";
311 break;
312 case 7: errmsg = "No such user.";
315 strcpy(&packet[4],errmsg.edit());
316 packetsize += errmsg.len() + 1;
317 // log(WvLog::Debug5, "Sending Error ");
318 dump_pkt();
319 write(packet, packetsize);