Deleted Added
full compact
tftp-transfer.c (207614) tftp-transfer.c (224536)
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>
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: head/libexec/tftpd/tftp-transfer.c 207614 2010-05-04 13:07:40Z imp $");
27__FBSDID("$FreeBSD: head/libexec/tftpd/tftp-transfer.c 224536 2011-07-31 03:12:20Z rodrigc $");
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 */
52void
53tftp_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) {
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 */
52void
53tftp_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 tftp_log(LOG_ERR,
133 "Block rollover but not allowed.");
134 send_error(peer, EBADOP);
135 gettimeofday(&(ts->tstop), NULL);
136 return;
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);
137 }
138
140 }
141
139 *block = atoi(options[OPT_ROLLOVER].o_request);
140 ts->rollovers++;
141 }
142 gettimeofday(&(ts->tstop), NULL);
143 } while (size == segsize);
144abort:
145 return;
146}
147
148/*
149 * Receive a file via the TFTP data session.
150 *
151 * - It could be that the first block has already arrived while
152 * trying to figure out if we were receiving options or not. In
153 * that case it is passed to this function.
154 */
155void
156tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
157 struct tftphdr *firstblock, size_t fb_size)
158{
159 struct tftphdr *rp;
160 uint16_t oldblock;
161 int n_data, n_ack, writesize, i, retry;
162 char recvbuffer[MAXPKTSIZE];
163
164 ts->amount = 0;
165
166 if (firstblock != NULL) {
167 writesize = write_file(firstblock->th_data, fb_size);
168 ts->amount += writesize;
169 for (i = 0; ; i++) {
170 n_ack = send_ack(peer, *block);
171 if (n_ack > 0) {
172 if (i == maxtimeouts) {
173 tftp_log(LOG_ERR,
174 "Cannot send ACK packet #%d, "
175 "giving up", *block);
176 return;
177 }
178 tftp_log(LOG_ERR,
179 "Cannot send ACK packet #%d, trying again",
180 *block);
181 continue;
182 }
183
184 break;
185 }
186
187 if (fb_size != segsize) {
188 gettimeofday(&(ts->tstop), NULL);
189 return;
190 }
191 }
192
193 rp = (struct tftphdr *)recvbuffer;
194 do {
195 oldblock = *block;
196 (*block)++;
197 if (oldblock > *block) {
198 if (options[OPT_ROLLOVER].o_request == NULL) {
142 ts->rollovers++;
143 }
144 gettimeofday(&(ts->tstop), NULL);
145 } while (size == segsize);
146abort:
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 */
157void
158tftp_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) {
199 tftp_log(LOG_ERR,
200 "Block rollover but not allowed.");
201 send_error(peer, EBADOP);
202 gettimeofday(&(ts->tstop), NULL);
203 return;
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);
204 }
205
209 }
210
206 *block = atoi(options[OPT_ROLLOVER].o_request);
207 ts->rollovers++;
208 }
209
210 for (retry = 0; ; retry++) {
211 if (debug&DEBUG_SIMPLE)
212 tftp_log(LOG_DEBUG,
213 "Receiving DATA block %d", *block);
214
215 n_data = receive_packet(peer, recvbuffer,
216 MAXPKTSIZE, NULL, timeoutpacket);
217 if (n_data < 0) {
218 if (retry == maxtimeouts) {
219 tftp_log(LOG_ERR,
220 "Timeout #%d on DATA block %d, "
221 "giving up", retry, *block);
222 return;
223 }
224 if (n_data == RP_TIMEOUT) {
225 tftp_log(LOG_WARNING,
226 "Timeout #%d on DATA block %d",
227 retry, *block);
228 send_ack(peer, oldblock);
229 continue;
230 }
231
232 /* Either read failure or ERROR packet */
233 if (debug&DEBUG_SIMPLE)
234 tftp_log(LOG_DEBUG, "Aborting: %s",
235 rp_strerror(n_data));
236 goto abort;
237 }
238 if (rp->th_opcode == DATA) {
239 ts->blocks++;
240
241 if (rp->th_block == *block)
242 break;
243
244 tftp_log(LOG_WARNING,
245 "Expected DATA block %d, got block %d",
246 *block, rp->th_block);
247
248 /* Re-synchronize with the other side */
249 (void) synchnet(peer);
250 if (rp->th_block == (*block-1)) {
251 tftp_log(LOG_INFO, "Trying to sync");
252 *block = oldblock;
253 ts->retries++;
254 goto send_ack; /* rexmit */
255 }
256
257 } else {
258 tftp_log(LOG_WARNING,
259 "Expected DATA block, got %s block",
260 packettype(rp->th_opcode));
261 }
262 }
263
264 if (n_data > 0) {
265 writesize = write_file(rp->th_data, n_data);
266 ts->amount += writesize;
267 if (writesize <= 0) {
268 tftp_log(LOG_ERR,
269 "write_file returned %d", writesize);
270 if (writesize < 0)
271 send_error(peer, errno + 100);
272 else
273 send_error(peer, ENOSPACE);
274 goto abort;
275 }
276 }
277
278send_ack:
279 for (i = 0; ; i++) {
280 n_ack = send_ack(peer, *block);
281 if (n_ack > 0) {
282
283 if (i == maxtimeouts) {
284 tftp_log(LOG_ERR,
285 "Cannot send ACK packet #%d, "
286 "giving up", *block);
287 return;
288 }
289
290 tftp_log(LOG_ERR,
291 "Cannot send ACK packet #%d, trying again",
292 *block);
293 continue;
294 }
295
296 break;
297 }
298 gettimeofday(&(ts->tstop), NULL);
299 } while (n_data == segsize);
300
301 /* Don't do late packet management for the client implementation */
302 if (acting_as_client)
303 return;
304
305 for (i = 0; ; i++) {
306 n_data = receive_packet(peer, (char *)rp, pktsize,
307 NULL, timeoutpacket);
308 if (n_data <= 0)
309 break;
310 if (n_data > 0 &&
311 rp->th_opcode == DATA && /* and got a data block */
312 *block == rp->th_block) /* then my last ack was lost */
313 send_ack(peer, *block); /* resend final ack */
314 }
315
316abort:
317 return;
318}
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
282send_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
320abort:
321 return;
322}