xref: /freebsd/libexec/tftpd/tftp-transfer.c (revision ab0b9f6b3073e6c4d1dfbf07444d7db67a189a96)
1 /*
2  * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/ioctl.h>
32 #include <sys/stat.h>
33 #include <sys/socket.h>
34 
35 #include <netinet/in.h>
36 #include <arpa/tftp.h>
37 
38 #include <errno.h>
39 #include <stdio.h>
40 #include <stdlib.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 /*
50  * Send a file via the TFTP data session.
51  */
52 void
53 tftp_send(int peer, uint16_t *block, struct tftp_stats *ts)
54 {
55 	struct tftphdr *rp;
56 	int size, n_data, n_ack, try;
57 	uint16_t oldblock;
58 	char sendbuffer[MAXPKTSIZE];
59 	char recvbuffer[MAXPKTSIZE];
60 
61 	rp = (struct tftphdr *)recvbuffer;
62 	*block = 1;
63 	ts->amount = 0;
64 	do {
65 		if (debug&DEBUG_SIMPLE)
66 			tftp_log(LOG_DEBUG, "Sending block %d", *block);
67 
68 		size = read_file(sendbuffer, segsize);
69 		if (size < 0) {
70 			tftp_log(LOG_ERR, "read_file returned %d", size);
71 			send_error(peer, errno + 100);
72 			goto abort;
73 		}
74 
75 		for (try = 0; ; try++) {
76 			n_data = send_data(peer, *block, sendbuffer, size);
77 			if (n_data > 0) {
78 				if (try == maxtimeouts) {
79 					tftp_log(LOG_ERR,
80 					    "Cannot send DATA packet #%d, "
81 					    "giving up", *block);
82 					return;
83 				}
84 				tftp_log(LOG_ERR,
85 				    "Cannot send DATA packet #%d, trying again",
86 				    *block);
87 				continue;
88 			}
89 
90 			n_ack = receive_packet(peer, recvbuffer,
91 			    MAXPKTSIZE, NULL, timeoutpacket);
92 			if (n_ack < 0) {
93 				if (n_ack == RP_TIMEOUT) {
94 					if (try == maxtimeouts) {
95 						tftp_log(LOG_ERR,
96 						    "Timeout #%d send ACK %d "
97 						    "giving up", try, *block);
98 						return;
99 					}
100 					tftp_log(LOG_WARNING,
101 					    "Timeout #%d on ACK %d",
102 					    try, *block);
103 					continue;
104 				}
105 
106 				/* Either read failure or ERROR packet */
107 				if (debug&DEBUG_SIMPLE)
108 					tftp_log(LOG_ERR, "Aborting: %s",
109 					    rp_strerror(n_ack));
110 				goto abort;
111 			}
112 			if (rp->th_opcode == ACK) {
113 				ts->blocks++;
114 				if (rp->th_block == *block) {
115 					ts->amount += size;
116 					break;
117 				}
118 
119 				/* Re-synchronize with the other side */
120 				(void) synchnet(peer);
121 				if (rp->th_block == (*block - 1)) {
122 					ts->retries++;
123 					continue;
124 				}
125 			}
126 
127 		}
128 		oldblock = *block;
129 		(*block)++;
130 		if (oldblock > *block) {
131 			if (options[OPT_ROLLOVER].o_request == NULL) {
132 				/*
133 				 * "rollover" option not specified in
134 				 * tftp client.  Default to rolling block
135 				 * counter to 0.
136 				 */
137 				*block = 0;
138 			} else {
139 				*block = atoi(options[OPT_ROLLOVER].o_request);
140 			}
141 
142 			ts->rollovers++;
143 		}
144 		gettimeofday(&(ts->tstop), NULL);
145 	} while (size == segsize);
146 abort:
147 	return;
148 }
149 
150 /*
151  * Receive a file via the TFTP data session.
152  *
153  * - It could be that the first block has already arrived while
154  *   trying to figure out if we were receiving options or not. In
155  *   that case it is passed to this function.
156  */
157 void
158 tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
159     struct tftphdr *firstblock, size_t fb_size)
160 {
161 	struct tftphdr *rp;
162 	uint16_t oldblock;
163 	int n_data, n_ack, writesize, i, retry;
164 	char recvbuffer[MAXPKTSIZE];
165 
166 	ts->amount = 0;
167 
168 	if (firstblock != NULL) {
169 		writesize = write_file(firstblock->th_data, fb_size);
170 		ts->amount += writesize;
171 		for (i = 0; ; i++) {
172 			n_ack = send_ack(peer, *block);
173 			if (n_ack > 0) {
174 				if (i == maxtimeouts) {
175 					tftp_log(LOG_ERR,
176 					    "Cannot send ACK packet #%d, "
177 					    "giving up", *block);
178 					return;
179 				}
180 				tftp_log(LOG_ERR,
181 				    "Cannot send ACK packet #%d, trying again",
182 				    *block);
183 				continue;
184 			}
185 
186 			break;
187 		}
188 
189 		if (fb_size != segsize) {
190 			gettimeofday(&(ts->tstop), NULL);
191 			return;
192 		}
193 	}
194 
195 	rp = (struct tftphdr *)recvbuffer;
196 	do {
197 		oldblock = *block;
198 		(*block)++;
199 		if (oldblock > *block) {
200 			if (options[OPT_ROLLOVER].o_request == NULL) {
201 				/*
202 				 * "rollover" option not specified in
203 				 * tftp client.  Default to rolling block
204 				 * counter to 0.
205 				 */
206 				*block = 0;
207 			} else {
208 				*block = atoi(options[OPT_ROLLOVER].o_request);
209 			}
210 
211 			ts->rollovers++;
212 		}
213 
214 		for (retry = 0; ; retry++) {
215 			if (debug&DEBUG_SIMPLE)
216 				tftp_log(LOG_DEBUG,
217 				    "Receiving DATA block %d", *block);
218 
219 			n_data = receive_packet(peer, recvbuffer,
220 			    MAXPKTSIZE, NULL, timeoutpacket);
221 			if (n_data < 0) {
222 				if (retry == maxtimeouts) {
223 					tftp_log(LOG_ERR,
224 					    "Timeout #%d on DATA block %d, "
225 					    "giving up", retry, *block);
226 					return;
227 				}
228 				if (n_data == RP_TIMEOUT) {
229 					tftp_log(LOG_WARNING,
230 					    "Timeout #%d on DATA block %d",
231 					    retry, *block);
232 					send_ack(peer, oldblock);
233 					continue;
234 				}
235 
236 				/* Either read failure or ERROR packet */
237 				if (debug&DEBUG_SIMPLE)
238 					tftp_log(LOG_DEBUG, "Aborting: %s",
239 					    rp_strerror(n_data));
240 				goto abort;
241 			}
242 			if (rp->th_opcode == DATA) {
243 				ts->blocks++;
244 
245 				if (rp->th_block == *block)
246 					break;
247 
248 				tftp_log(LOG_WARNING,
249 				    "Expected DATA block %d, got block %d",
250 				    *block, rp->th_block);
251 
252 				/* Re-synchronize with the other side */
253 				(void) synchnet(peer);
254 				if (rp->th_block == (*block-1)) {
255 					tftp_log(LOG_INFO, "Trying to sync");
256 					*block = oldblock;
257 					ts->retries++;
258 					goto send_ack;	/* rexmit */
259 				}
260 
261 			} else {
262 				tftp_log(LOG_WARNING,
263 				    "Expected DATA block, got %s block",
264 				    packettype(rp->th_opcode));
265 			}
266 		}
267 
268 		if (n_data > 0) {
269 			writesize = write_file(rp->th_data, n_data);
270 			ts->amount += writesize;
271 			if (writesize <= 0) {
272 				tftp_log(LOG_ERR,
273 				    "write_file returned %d", writesize);
274 				if (writesize < 0)
275 					send_error(peer, errno + 100);
276 				else
277 					send_error(peer, ENOSPACE);
278 				goto abort;
279 			}
280 		}
281 
282 send_ack:
283 		for (i = 0; ; i++) {
284 			n_ack = send_ack(peer, *block);
285 			if (n_ack > 0) {
286 
287 				if (i == maxtimeouts) {
288 					tftp_log(LOG_ERR,
289 					    "Cannot send ACK packet #%d, "
290 					    "giving up", *block);
291 					return;
292 				}
293 
294 				tftp_log(LOG_ERR,
295 				    "Cannot send ACK packet #%d, trying again",
296 				    *block);
297 				continue;
298 			}
299 
300 			break;
301 		}
302 		gettimeofday(&(ts->tstop), NULL);
303 	} while (n_data == segsize);
304 
305 	/* Don't do late packet management for the client implementation */
306 	if (acting_as_client)
307 		return;
308 
309 	for (i = 0; ; i++) {
310 		n_data = receive_packet(peer, (char *)rp, pktsize,
311 		    NULL, timeoutpacket);
312 		if (n_data <= 0)
313 			break;
314 		if (n_data > 0 &&
315 		    rp->th_opcode == DATA &&	/* and got a data block */
316 		    *block == rp->th_block)	/* then my last ack was lost */
317 			send_ack(peer, *block);	/* resend final ack */
318 	}
319 
320 abort:
321 	return;
322 }
323