xref: /freebsd/libexec/tftpd/tftp-transfer.c (revision 4f52dfbb)
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$");
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 <syslog.h>
44 
45 #include "tftp-file.h"
46 #include "tftp-io.h"
47 #include "tftp-utils.h"
48 #include "tftp-options.h"
49 #include "tftp-transfer.h"
50 
51 /*
52  * Send a file via the TFTP data session.
53  */
54 void
55 tftp_send(int peer, uint16_t *block, struct tftp_stats *ts)
56 {
57 	struct tftphdr *rp;
58 	int size, n_data, n_ack, try;
59 	uint16_t oldblock;
60 	char sendbuffer[MAXPKTSIZE];
61 	char recvbuffer[MAXPKTSIZE];
62 
63 	rp = (struct tftphdr *)recvbuffer;
64 	*block = 1;
65 	ts->amount = 0;
66 	do {
67 		if (debug&DEBUG_SIMPLE)
68 			tftp_log(LOG_DEBUG, "Sending block %d", *block);
69 
70 		size = read_file(sendbuffer, segsize);
71 		if (size < 0) {
72 			tftp_log(LOG_ERR, "read_file returned %d", size);
73 			send_error(peer, errno + 100);
74 			goto abort;
75 		}
76 
77 		for (try = 0; ; try++) {
78 			n_data = send_data(peer, *block, sendbuffer, size);
79 			if (n_data > 0) {
80 				if (try == maxtimeouts) {
81 					tftp_log(LOG_ERR,
82 					    "Cannot send DATA packet #%d, "
83 					    "giving up", *block);
84 					return;
85 				}
86 				tftp_log(LOG_ERR,
87 				    "Cannot send DATA packet #%d, trying again",
88 				    *block);
89 				continue;
90 			}
91 
92 			n_ack = receive_packet(peer, recvbuffer,
93 			    MAXPKTSIZE, NULL, timeoutpacket);
94 			if (n_ack < 0) {
95 				if (n_ack == RP_TIMEOUT) {
96 					if (try == maxtimeouts) {
97 						tftp_log(LOG_ERR,
98 						    "Timeout #%d send ACK %d "
99 						    "giving up", try, *block);
100 						return;
101 					}
102 					tftp_log(LOG_WARNING,
103 					    "Timeout #%d on ACK %d",
104 					    try, *block);
105 					continue;
106 				}
107 
108 				/* Either read failure or ERROR packet */
109 				if (debug&DEBUG_SIMPLE)
110 					tftp_log(LOG_ERR, "Aborting: %s",
111 					    rp_strerror(n_ack));
112 				goto abort;
113 			}
114 			if (rp->th_opcode == ACK) {
115 				ts->blocks++;
116 				if (rp->th_block == *block) {
117 					ts->amount += size;
118 					break;
119 				}
120 
121 				/* Re-synchronize with the other side */
122 				(void) synchnet(peer);
123 				if (rp->th_block == (*block - 1)) {
124 					ts->retries++;
125 					continue;
126 				}
127 			}
128 
129 		}
130 		oldblock = *block;
131 		(*block)++;
132 		if (oldblock > *block) {
133 			if (options[OPT_ROLLOVER].o_request == NULL) {
134 				/*
135 				 * "rollover" option not specified in
136 				 * tftp client.  Default to rolling block
137 				 * counter to 0.
138 				 */
139 				*block = 0;
140 			} else {
141 				*block = atoi(options[OPT_ROLLOVER].o_request);
142 			}
143 
144 			ts->rollovers++;
145 		}
146 		gettimeofday(&(ts->tstop), NULL);
147 	} while (size == segsize);
148 abort:
149 	return;
150 }
151 
152 /*
153  * Receive a file via the TFTP data session.
154  *
155  * - It could be that the first block has already arrived while
156  *   trying to figure out if we were receiving options or not. In
157  *   that case it is passed to this function.
158  */
159 void
160 tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
161     struct tftphdr *firstblock, size_t fb_size)
162 {
163 	struct tftphdr *rp;
164 	uint16_t oldblock;
165 	int n_data, n_ack, writesize, i, retry;
166 	char recvbuffer[MAXPKTSIZE];
167 
168 	ts->amount = 0;
169 
170 	if (firstblock != NULL) {
171 		writesize = write_file(firstblock->th_data, fb_size);
172 		ts->amount += writesize;
173 		for (i = 0; ; i++) {
174 			n_ack = send_ack(peer, *block);
175 			if (n_ack > 0) {
176 				if (i == maxtimeouts) {
177 					tftp_log(LOG_ERR,
178 					    "Cannot send ACK packet #%d, "
179 					    "giving up", *block);
180 					return;
181 				}
182 				tftp_log(LOG_ERR,
183 				    "Cannot send ACK packet #%d, trying again",
184 				    *block);
185 				continue;
186 			}
187 
188 			break;
189 		}
190 
191 		if (fb_size != segsize) {
192 			gettimeofday(&(ts->tstop), NULL);
193 			return;
194 		}
195 	}
196 
197 	rp = (struct tftphdr *)recvbuffer;
198 	do {
199 		oldblock = *block;
200 		(*block)++;
201 		if (oldblock > *block) {
202 			if (options[OPT_ROLLOVER].o_request == NULL) {
203 				/*
204 				 * "rollover" option not specified in
205 				 * tftp client.  Default to rolling block
206 				 * counter to 0.
207 				 */
208 				*block = 0;
209 			} else {
210 				*block = atoi(options[OPT_ROLLOVER].o_request);
211 			}
212 
213 			ts->rollovers++;
214 		}
215 
216 		for (retry = 0; ; retry++) {
217 			if (debug&DEBUG_SIMPLE)
218 				tftp_log(LOG_DEBUG,
219 				    "Receiving DATA block %d", *block);
220 
221 			n_data = receive_packet(peer, recvbuffer,
222 			    MAXPKTSIZE, NULL, timeoutpacket);
223 			if (n_data < 0) {
224 				if (retry == maxtimeouts) {
225 					tftp_log(LOG_ERR,
226 					    "Timeout #%d on DATA block %d, "
227 					    "giving up", retry, *block);
228 					return;
229 				}
230 				if (n_data == RP_TIMEOUT) {
231 					tftp_log(LOG_WARNING,
232 					    "Timeout #%d on DATA block %d",
233 					    retry, *block);
234 					send_ack(peer, oldblock);
235 					continue;
236 				}
237 
238 				/* Either read failure or ERROR packet */
239 				if (debug&DEBUG_SIMPLE)
240 					tftp_log(LOG_DEBUG, "Aborting: %s",
241 					    rp_strerror(n_data));
242 				goto abort;
243 			}
244 			if (rp->th_opcode == DATA) {
245 				ts->blocks++;
246 
247 				if (rp->th_block == *block)
248 					break;
249 
250 				tftp_log(LOG_WARNING,
251 				    "Expected DATA block %d, got block %d",
252 				    *block, rp->th_block);
253 
254 				/* Re-synchronize with the other side */
255 				(void) synchnet(peer);
256 				if (rp->th_block == (*block-1)) {
257 					tftp_log(LOG_INFO, "Trying to sync");
258 					*block = oldblock;
259 					ts->retries++;
260 					goto send_ack;	/* rexmit */
261 				}
262 
263 			} else {
264 				tftp_log(LOG_WARNING,
265 				    "Expected DATA block, got %s block",
266 				    packettype(rp->th_opcode));
267 			}
268 		}
269 
270 		if (n_data > 0) {
271 			writesize = write_file(rp->th_data, n_data);
272 			ts->amount += writesize;
273 			if (writesize <= 0) {
274 				tftp_log(LOG_ERR,
275 				    "write_file returned %d", writesize);
276 				if (writesize < 0)
277 					send_error(peer, errno + 100);
278 				else
279 					send_error(peer, ENOSPACE);
280 				goto abort;
281 			}
282 		}
283 
284 send_ack:
285 		for (i = 0; ; i++) {
286 			n_ack = send_ack(peer, *block);
287 			if (n_ack > 0) {
288 
289 				if (i == maxtimeouts) {
290 					tftp_log(LOG_ERR,
291 					    "Cannot send ACK packet #%d, "
292 					    "giving up", *block);
293 					return;
294 				}
295 
296 				tftp_log(LOG_ERR,
297 				    "Cannot send ACK packet #%d, trying again",
298 				    *block);
299 				continue;
300 			}
301 
302 			break;
303 		}
304 		gettimeofday(&(ts->tstop), NULL);
305 	} while (n_data == segsize);
306 
307 	write_close();
308 
309 	/* Don't do late packet management for the client implementation */
310 	if (acting_as_client)
311 		return;
312 
313 	for (i = 0; ; i++) {
314 		n_data = receive_packet(peer, (char *)rp, pktsize,
315 		    NULL, timeoutpacket);
316 		if (n_data <= 0)
317 			break;
318 		if (n_data > 0 &&
319 		    rp->th_opcode == DATA &&	/* and got a data block */
320 		    *block == rp->th_block)	/* then my last ack was lost */
321 			send_ack(peer, *block);	/* resend final ack */
322 	}
323 
324 abort:
325 	return;
326 }
327