]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - libexec/tftpd/tftp-transfer.c
MFC r330710:
[FreeBSD/stable/10.git] / libexec / tftpd / tftp-transfer.c
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         write_close();
306
307         /* Don't do late packet management for the client implementation */
308         if (acting_as_client)
309                 return;
310
311         for (i = 0; ; i++) {
312                 n_data = receive_packet(peer, (char *)rp, pktsize,
313                     NULL, timeoutpacket);
314                 if (n_data <= 0)
315                         break;
316                 if (n_data > 0 &&
317                     rp->th_opcode == DATA &&    /* and got a data block */
318                     *block == rp->th_block)     /* then my last ack was lost */
319                         send_ack(peer, *block); /* resend final ack */
320         }
321
322 abort:
323         return;
324 }