1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/param.h>
29 #include <sys/ioctl.h>
30 #include <sys/socket.h>
31 #include <sys/stat.h>
32 #include <sys/time.h>
33
34 #include <netinet/in.h>
35 #include <arpa/tftp.h>
36
37 #include <errno.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <syslog.h>
42
43 #include "tftp-file.h"
44 #include "tftp-io.h"
45 #include "tftp-utils.h"
46 #include "tftp-options.h"
47 #include "tftp-transfer.h"
48
49 struct block_data {
50 off_t offset;
51 uint16_t block;
52 int size;
53 };
54
55 /*
56 * Send a file via the TFTP data session.
57 */
58 int
tftp_send(int peer,uint16_t * block,struct tftp_stats * ts)59 tftp_send(int peer, uint16_t *block, struct tftp_stats *ts)
60 {
61 struct tftphdr *rp;
62 int size, n_data, n_ack, sendtry, acktry;
63 u_int i, j;
64 uint16_t oldblock, windowblock;
65 char sendbuffer[MAXPKTSIZE];
66 char recvbuffer[MAXPKTSIZE];
67 struct block_data window[WINDOWSIZE_MAX];
68
69 rp = (struct tftphdr *)recvbuffer;
70 *block = 1;
71 ts->amount = 0;
72 windowblock = 0;
73 acktry = 0;
74 do {
75 read_block:
76 if (debug & DEBUG_SIMPLE)
77 tftp_log(LOG_DEBUG, "Sending block %d (window block %d)",
78 *block, windowblock);
79
80 window[windowblock].offset = tell_file();
81 window[windowblock].block = *block;
82 size = read_file(sendbuffer, segsize);
83 if (size < 0) {
84 tftp_log(LOG_ERR, "read_file returned %d", size);
85 send_error(peer, errno + 100);
86 return -1;
87 }
88 window[windowblock].size = size;
89 windowblock++;
90
91 for (sendtry = 0; ; sendtry++) {
92 n_data = send_data(peer, *block, sendbuffer, size);
93 if (n_data == 0)
94 break;
95
96 if (sendtry == maxtimeouts) {
97 tftp_log(LOG_ERR,
98 "Cannot send DATA packet #%d, "
99 "giving up", *block);
100 return -1;
101 }
102 tftp_log(LOG_ERR,
103 "Cannot send DATA packet #%d, trying again",
104 *block);
105 }
106
107 /* Only check for ACK for last block in window. */
108 if (windowblock == windowsize || size != segsize) {
109 n_ack = receive_packet(peer, recvbuffer,
110 MAXPKTSIZE, NULL, timeoutpacket);
111 if (n_ack < 0) {
112 if (n_ack == RP_TIMEOUT) {
113 if (acktry == maxtimeouts) {
114 tftp_log(LOG_ERR,
115 "Timeout #%d send ACK %d "
116 "giving up", acktry, *block);
117 return -1;
118 }
119 tftp_log(LOG_WARNING,
120 "Timeout #%d on ACK %d",
121 acktry, *block);
122
123 acktry++;
124 ts->retries++;
125 if (seek_file(window[0].offset) != 0) {
126 tftp_log(LOG_ERR,
127 "seek_file failed: %s",
128 strerror(errno));
129 send_error(peer, errno + 100);
130 return -1;
131 }
132 *block = window[0].block;
133 windowblock = 0;
134 goto read_block;
135 }
136
137 /* Either read failure or ERROR packet */
138 if (debug & DEBUG_SIMPLE)
139 tftp_log(LOG_ERR, "Aborting: %s",
140 rp_strerror(n_ack));
141 return -1;
142 }
143 if (rp->th_opcode == ACK) {
144 /*
145 * Look for the ACKed block in our open
146 * window.
147 */
148 for (i = 0; i < windowblock; i++) {
149 if (rp->th_block == window[i].block)
150 break;
151 }
152
153 if (i == windowblock) {
154 /* Did not recognize ACK. */
155 if (debug & DEBUG_SIMPLE)
156 tftp_log(LOG_DEBUG,
157 "ACK %d out of window",
158 rp->th_block);
159
160 /* Re-synchronize with the other side */
161 (void) synchnet(peer);
162
163 /* Resend the current window. */
164 ts->retries++;
165 if (seek_file(window[0].offset) != 0) {
166 tftp_log(LOG_ERR,
167 "seek_file failed: %s",
168 strerror(errno));
169 send_error(peer, errno + 100);
170 return -1;
171 }
172 *block = window[0].block;
173 windowblock = 0;
174 goto read_block;
175 }
176
177 /* ACKed at least some data. */
178 acktry = 0;
179 for (j = 0; j <= i; j++) {
180 if (debug & DEBUG_SIMPLE)
181 tftp_log(LOG_DEBUG,
182 "ACKed block %d",
183 window[j].block);
184 ts->blocks++;
185 ts->amount += window[j].size;
186 }
187
188 /*
189 * Partial ACK. Rewind state to first
190 * un-ACKed block.
191 */
192 if (i + 1 != windowblock) {
193 if (debug & DEBUG_SIMPLE)
194 tftp_log(LOG_DEBUG,
195 "Partial ACK");
196 if (seek_file(window[i + 1].offset) !=
197 0) {
198 tftp_log(LOG_ERR,
199 "seek_file failed: %s",
200 strerror(errno));
201 send_error(peer, errno + 100);
202 return -1;
203 }
204 *block = window[i + 1].block;
205 windowblock = 0;
206 ts->retries++;
207 goto read_block;
208 }
209
210 windowblock = 0;
211 }
212
213 }
214 oldblock = *block;
215 (*block)++;
216 if (oldblock > *block) {
217 if (options[OPT_ROLLOVER].o_request == NULL) {
218 /*
219 * "rollover" option not specified in
220 * tftp client. Default to rolling block
221 * counter to 0.
222 */
223 *block = 0;
224 } else {
225 *block = atoi(options[OPT_ROLLOVER].o_request);
226 }
227
228 ts->rollovers++;
229 }
230 gettimeofday(&(ts->tstop), NULL);
231 } while (size == segsize);
232 return 0;
233 }
234
235 /*
236 * Receive a file via the TFTP data session.
237 *
238 * - It could be that the first block has already arrived while
239 * trying to figure out if we were receiving options or not. In
240 * that case it is passed to this function.
241 */
242 int
tftp_receive(int peer,uint16_t * block,struct tftp_stats * ts,struct tftphdr * firstblock,size_t fb_size)243 tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
244 struct tftphdr *firstblock, size_t fb_size)
245 {
246 struct tftphdr *rp;
247 uint16_t oldblock, windowstart;
248 int n_data, n_ack, writesize, i, retry, windowblock;
249 char recvbuffer[MAXPKTSIZE];
250
251 ts->amount = 0;
252 windowblock = 0;
253
254 if (firstblock != NULL) {
255 writesize = write_file(firstblock->th_data, fb_size);
256 ts->amount += writesize;
257 ts->blocks++;
258 windowblock++;
259 if (windowsize == 1 || fb_size != segsize) {
260 for (i = 0; ; i++) {
261 n_ack = send_ack(peer, *block);
262 if (n_ack > 0) {
263 if (i == maxtimeouts) {
264 tftp_log(LOG_ERR,
265 "Cannot send ACK packet #%d, "
266 "giving up", *block);
267 return -1;
268 }
269 tftp_log(LOG_ERR,
270 "Cannot send ACK packet #%d, trying again",
271 *block);
272 continue;
273 }
274
275 break;
276 }
277 }
278
279 if (fb_size != segsize) {
280 write_close();
281 gettimeofday(&(ts->tstop), NULL);
282 return 0;
283 }
284 }
285
286 rp = (struct tftphdr *)recvbuffer;
287 do {
288 oldblock = *block;
289 (*block)++;
290 if (oldblock > *block) {
291 if (options[OPT_ROLLOVER].o_request == NULL) {
292 /*
293 * "rollover" option not specified in
294 * tftp client. Default to rolling block
295 * counter to 0.
296 */
297 *block = 0;
298 } else {
299 *block = atoi(options[OPT_ROLLOVER].o_request);
300 }
301
302 ts->rollovers++;
303 }
304
305 for (retry = 0; ; retry++) {
306 if (debug & DEBUG_SIMPLE)
307 tftp_log(LOG_DEBUG,
308 "Receiving DATA block %d (window block %d)",
309 *block, windowblock);
310
311 n_data = receive_packet(peer, recvbuffer,
312 MAXPKTSIZE, NULL, timeoutpacket);
313 if (n_data < 0) {
314 if (retry == maxtimeouts) {
315 tftp_log(LOG_ERR,
316 "Timeout #%d on DATA block %d, "
317 "giving up", retry, *block);
318 return -1;
319 }
320 if (n_data == RP_TIMEOUT) {
321 tftp_log(LOG_WARNING,
322 "Timeout #%d on DATA block %d",
323 retry, *block);
324 send_ack(peer, oldblock);
325 windowblock = 0;
326 continue;
327 }
328
329 /* Either read failure or ERROR packet */
330 if (debug & DEBUG_SIMPLE)
331 tftp_log(LOG_DEBUG, "Aborting: %s",
332 rp_strerror(n_data));
333 return -1;
334 }
335 if (rp->th_opcode == DATA) {
336 ts->blocks++;
337
338 if (rp->th_block == *block)
339 break;
340
341 /*
342 * Ignore duplicate blocks within the
343 * window.
344 *
345 * This does not handle duplicate
346 * blocks during a rollover as
347 * gracefully, but that should still
348 * recover eventually.
349 */
350 if (*block > windowsize)
351 windowstart = *block - windowsize;
352 else
353 windowstart = 0;
354 if (rp->th_block > windowstart &&
355 rp->th_block < *block) {
356 if (debug & DEBUG_SIMPLE)
357 tftp_log(LOG_DEBUG,
358 "Ignoring duplicate DATA block %d",
359 rp->th_block);
360 windowblock++;
361 retry = 0;
362 continue;
363 }
364
365 tftp_log(LOG_WARNING,
366 "Expected DATA block %d, got block %d",
367 *block, rp->th_block);
368
369 /* Re-synchronize with the other side */
370 (void) synchnet(peer);
371
372 tftp_log(LOG_INFO, "Trying to sync");
373 *block = oldblock;
374 ts->retries++;
375 goto send_ack; /* rexmit */
376
377 } else {
378 tftp_log(LOG_WARNING,
379 "Expected DATA block, got %s block",
380 packettype(rp->th_opcode));
381 }
382 }
383
384 if (n_data > 0) {
385 writesize = write_file(rp->th_data, n_data);
386 ts->amount += writesize;
387 if (writesize <= 0) {
388 tftp_log(LOG_ERR,
389 "write_file returned %d", writesize);
390 if (writesize < 0)
391 send_error(peer, errno + 100);
392 else
393 send_error(peer, ENOSPACE);
394 return -1;
395 }
396 }
397 if (n_data != segsize)
398 write_close();
399 windowblock++;
400
401 /* Only send ACKs for the last block in the window. */
402 if (windowblock < windowsize && n_data == segsize)
403 continue;
404 send_ack:
405 for (i = 0; ; i++) {
406 n_ack = send_ack(peer, *block);
407 if (n_ack > 0) {
408
409 if (i == maxtimeouts) {
410 tftp_log(LOG_ERR,
411 "Cannot send ACK packet #%d, "
412 "giving up", *block);
413 return -1;
414 }
415
416 tftp_log(LOG_ERR,
417 "Cannot send ACK packet #%d, trying again",
418 *block);
419 continue;
420 }
421
422 if (debug & DEBUG_SIMPLE)
423 tftp_log(LOG_DEBUG, "Sent ACK for %d", *block);
424 windowblock = 0;
425 break;
426 }
427 gettimeofday(&(ts->tstop), NULL);
428 } while (n_data == segsize);
429
430 /* Don't do late packet management for the client implementation */
431 if (acting_as_client)
432 return 0;
433
434 for (i = 0; ; i++) {
435 n_data = receive_packet(peer, (char *)rp, pktsize,
436 NULL, -timeoutpacket);
437 if (n_data <= 0)
438 break;
439 if (n_data > 0 &&
440 rp->th_opcode == DATA && /* and got a data block */
441 *block == rp->th_block) /* then my last ack was lost */
442 send_ack(peer, *block); /* resend final ack */
443 }
444
445 return 0;
446 }
447