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
tftp_send(int peer,uint16_t * block,struct tftp_stats * ts)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
tftp_receive(int peer,uint16_t * block,struct tftp_stats * ts,struct tftphdr * firstblock,size_t fb_size)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 if (n_data != segsize)
283 write_close();
284 }
285
286 send_ack:
287 for (i = 0; ; i++) {
288 n_ack = send_ack(peer, *block);
289 if (n_ack > 0) {
290
291 if (i == maxtimeouts) {
292 tftp_log(LOG_ERR,
293 "Cannot send ACK packet #%d, "
294 "giving up", *block);
295 return;
296 }
297
298 tftp_log(LOG_ERR,
299 "Cannot send ACK packet #%d, trying again",
300 *block);
301 continue;
302 }
303
304 break;
305 }
306 gettimeofday(&(ts->tstop), NULL);
307 } while (n_data == segsize);
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