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