]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - libexec/tftpd/tftp-transfer.c
Add two missing eventhandler.h headers
[FreeBSD/FreeBSD.git] / libexec / tftpd / tftp-transfer.c
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                         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 }