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 } 327