]> CyberLeo.Net >> Repos - FreeBSD/releng/8.2.git/blob - libexec/tftpd/tftp-transfer.c
MFC: r216691
[FreeBSD/releng/8.2.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                                 tftp_log(LOG_ERR,
133                                     "Block rollover but not allowed.");
134                                 send_error(peer, EBADOP);
135                                 gettimeofday(&(ts->tstop), NULL);
136                                 return;
137                         }
138
139                         *block = atoi(options[OPT_ROLLOVER].o_request);
140                         ts->rollovers++;
141                 }
142                 gettimeofday(&(ts->tstop), NULL);
143         } while (size == segsize);
144 abort:
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  */
155 void
156 tftp_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) {
199                                 tftp_log(LOG_ERR,
200                                     "Block rollover but not allowed.");
201                                 send_error(peer, EBADOP);
202                                 gettimeofday(&(ts->tstop), NULL);
203                                 return;
204                         }
205
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
278 send_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
316 abort:
317         return;
318 }