1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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/param.h> 32 #include <sys/ioctl.h> 33 #include <sys/socket.h> 34 #include <sys/stat.h> 35 #include <sys/time.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 <string.h> 44 #include <syslog.h> 45 46 #include "tftp-file.h" 47 #include "tftp-io.h" 48 #include "tftp-utils.h" 49 #include "tftp-options.h" 50 #include "tftp-transfer.h" 51 52 struct block_data { 53 off_t offset; 54 uint16_t block; 55 int size; 56 }; 57 58 /* 59 * Send a file via the TFTP data session. 60 */ 61 int 62 tftp_send(int peer, uint16_t *block, struct tftp_stats *ts) 63 { 64 struct tftphdr *rp; 65 int size, n_data, n_ack, sendtry, acktry; 66 u_int i, j; 67 uint16_t oldblock, windowblock; 68 char sendbuffer[MAXPKTSIZE]; 69 char recvbuffer[MAXPKTSIZE]; 70 struct block_data window[WINDOWSIZE_MAX]; 71 72 rp = (struct tftphdr *)recvbuffer; 73 *block = 1; 74 ts->amount = 0; 75 windowblock = 0; 76 acktry = 0; 77 do { 78 read_block: 79 if (debug & DEBUG_SIMPLE) 80 tftp_log(LOG_DEBUG, "Sending block %d (window block %d)", 81 *block, windowblock); 82 83 window[windowblock].offset = tell_file(); 84 window[windowblock].block = *block; 85 size = read_file(sendbuffer, segsize); 86 if (size < 0) { 87 tftp_log(LOG_ERR, "read_file returned %d", size); 88 send_error(peer, errno + 100); 89 return -1; 90 } 91 window[windowblock].size = size; 92 windowblock++; 93 94 for (sendtry = 0; ; sendtry++) { 95 n_data = send_data(peer, *block, sendbuffer, size); 96 if (n_data == 0) 97 break; 98 99 if (sendtry == maxtimeouts) { 100 tftp_log(LOG_ERR, 101 "Cannot send DATA packet #%d, " 102 "giving up", *block); 103 return -1; 104 } 105 tftp_log(LOG_ERR, 106 "Cannot send DATA packet #%d, trying again", 107 *block); 108 } 109 110 /* Only check for ACK for last block in window. */ 111 if (windowblock == windowsize || size != segsize) { 112 n_ack = receive_packet(peer, recvbuffer, 113 MAXPKTSIZE, NULL, timeoutpacket); 114 if (n_ack < 0) { 115 if (n_ack == RP_TIMEOUT) { 116 if (acktry == maxtimeouts) { 117 tftp_log(LOG_ERR, 118 "Timeout #%d send ACK %d " 119 "giving up", acktry, *block); 120 return -1; 121 } 122 tftp_log(LOG_WARNING, 123 "Timeout #%d on ACK %d", 124 acktry, *block); 125 126 acktry++; 127 ts->retries++; 128 if (seek_file(window[0].offset) != 0) { 129 tftp_log(LOG_ERR, 130 "seek_file failed: %s", 131 strerror(errno)); 132 send_error(peer, errno + 100); 133 return -1; 134 } 135 *block = window[0].block; 136 windowblock = 0; 137 goto read_block; 138 } 139 140 /* Either read failure or ERROR packet */ 141 if (debug & DEBUG_SIMPLE) 142 tftp_log(LOG_ERR, "Aborting: %s", 143 rp_strerror(n_ack)); 144 return -1; 145 } 146 if (rp->th_opcode == ACK) { 147 /* 148 * Look for the ACKed block in our open 149 * window. 150 */ 151 for (i = 0; i < windowblock; i++) { 152 if (rp->th_block == window[i].block) 153 break; 154 } 155 156 if (i == windowblock) { 157 /* Did not recognize ACK. */ 158 if (debug & DEBUG_SIMPLE) 159 tftp_log(LOG_DEBUG, 160 "ACK %d out of window", 161 rp->th_block); 162 163 /* Re-synchronize with the other side */ 164 (void) synchnet(peer); 165 166 /* Resend the current window. */ 167 ts->retries++; 168 if (seek_file(window[0].offset) != 0) { 169 tftp_log(LOG_ERR, 170 "seek_file failed: %s", 171 strerror(errno)); 172 send_error(peer, errno + 100); 173 return -1; 174 } 175 *block = window[0].block; 176 windowblock = 0; 177 goto read_block; 178 } 179 180 /* ACKed at least some data. */ 181 acktry = 0; 182 for (j = 0; j <= i; j++) { 183 if (debug & DEBUG_SIMPLE) 184 tftp_log(LOG_DEBUG, 185 "ACKed block %d", 186 window[j].block); 187 ts->blocks++; 188 ts->amount += window[j].size; 189 } 190 191 /* 192 * Partial ACK. Rewind state to first 193 * un-ACKed block. 194 */ 195 if (i + 1 != windowblock) { 196 if (debug & DEBUG_SIMPLE) 197 tftp_log(LOG_DEBUG, 198 "Partial ACK"); 199 if (seek_file(window[i + 1].offset) != 200 0) { 201 tftp_log(LOG_ERR, 202 "seek_file failed: %s", 203 strerror(errno)); 204 send_error(peer, errno + 100); 205 return -1; 206 } 207 *block = window[i + 1].block; 208 windowblock = 0; 209 ts->retries++; 210 goto read_block; 211 } 212 213 windowblock = 0; 214 } 215 216 } 217 oldblock = *block; 218 (*block)++; 219 if (oldblock > *block) { 220 if (options[OPT_ROLLOVER].o_request == NULL) { 221 /* 222 * "rollover" option not specified in 223 * tftp client. Default to rolling block 224 * counter to 0. 225 */ 226 *block = 0; 227 } else { 228 *block = atoi(options[OPT_ROLLOVER].o_request); 229 } 230 231 ts->rollovers++; 232 } 233 gettimeofday(&(ts->tstop), NULL); 234 } while (size == segsize); 235 return 0; 236 } 237 238 /* 239 * Receive a file via the TFTP data session. 240 * 241 * - It could be that the first block has already arrived while 242 * trying to figure out if we were receiving options or not. In 243 * that case it is passed to this function. 244 */ 245 int 246 tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts, 247 struct tftphdr *firstblock, size_t fb_size) 248 { 249 struct tftphdr *rp; 250 uint16_t oldblock, windowstart; 251 int n_data, n_ack, writesize, i, retry, windowblock; 252 char recvbuffer[MAXPKTSIZE]; 253 254 ts->amount = 0; 255 windowblock = 0; 256 257 if (firstblock != NULL) { 258 writesize = write_file(firstblock->th_data, fb_size); 259 ts->amount += writesize; 260 ts->blocks++; 261 windowblock++; 262 if (windowsize == 1 || fb_size != segsize) { 263 for (i = 0; ; i++) { 264 n_ack = send_ack(peer, *block); 265 if (n_ack > 0) { 266 if (i == maxtimeouts) { 267 tftp_log(LOG_ERR, 268 "Cannot send ACK packet #%d, " 269 "giving up", *block); 270 return -1; 271 } 272 tftp_log(LOG_ERR, 273 "Cannot send ACK packet #%d, trying again", 274 *block); 275 continue; 276 } 277 278 break; 279 } 280 } 281 282 if (fb_size != segsize) { 283 write_close(); 284 gettimeofday(&(ts->tstop), NULL); 285 return 0; 286 } 287 } 288 289 rp = (struct tftphdr *)recvbuffer; 290 do { 291 oldblock = *block; 292 (*block)++; 293 if (oldblock > *block) { 294 if (options[OPT_ROLLOVER].o_request == NULL) { 295 /* 296 * "rollover" option not specified in 297 * tftp client. Default to rolling block 298 * counter to 0. 299 */ 300 *block = 0; 301 } else { 302 *block = atoi(options[OPT_ROLLOVER].o_request); 303 } 304 305 ts->rollovers++; 306 } 307 308 for (retry = 0; ; retry++) { 309 if (debug & DEBUG_SIMPLE) 310 tftp_log(LOG_DEBUG, 311 "Receiving DATA block %d (window block %d)", 312 *block, windowblock); 313 314 n_data = receive_packet(peer, recvbuffer, 315 MAXPKTSIZE, NULL, timeoutpacket); 316 if (n_data < 0) { 317 if (retry == maxtimeouts) { 318 tftp_log(LOG_ERR, 319 "Timeout #%d on DATA block %d, " 320 "giving up", retry, *block); 321 return -1; 322 } 323 if (n_data == RP_TIMEOUT) { 324 tftp_log(LOG_WARNING, 325 "Timeout #%d on DATA block %d", 326 retry, *block); 327 send_ack(peer, oldblock); 328 windowblock = 0; 329 continue; 330 } 331 332 /* Either read failure or ERROR packet */ 333 if (debug & DEBUG_SIMPLE) 334 tftp_log(LOG_DEBUG, "Aborting: %s", 335 rp_strerror(n_data)); 336 return -1; 337 } 338 if (rp->th_opcode == DATA) { 339 ts->blocks++; 340 341 if (rp->th_block == *block) 342 break; 343 344 /* 345 * Ignore duplicate blocks within the 346 * window. 347 * 348 * This does not handle duplicate 349 * blocks during a rollover as 350 * gracefully, but that should still 351 * recover eventually. 352 */ 353 if (*block > windowsize) 354 windowstart = *block - windowsize; 355 else 356 windowstart = 0; 357 if (rp->th_block > windowstart && 358 rp->th_block < *block) { 359 if (debug & DEBUG_SIMPLE) 360 tftp_log(LOG_DEBUG, 361 "Ignoring duplicate DATA block %d", 362 rp->th_block); 363 windowblock++; 364 retry = 0; 365 continue; 366 } 367 368 tftp_log(LOG_WARNING, 369 "Expected DATA block %d, got block %d", 370 *block, rp->th_block); 371 372 /* Re-synchronize with the other side */ 373 (void) synchnet(peer); 374 375 tftp_log(LOG_INFO, "Trying to sync"); 376 *block = oldblock; 377 ts->retries++; 378 goto send_ack; /* rexmit */ 379 380 } else { 381 tftp_log(LOG_WARNING, 382 "Expected DATA block, got %s block", 383 packettype(rp->th_opcode)); 384 } 385 } 386 387 if (n_data > 0) { 388 writesize = write_file(rp->th_data, n_data); 389 ts->amount += writesize; 390 if (writesize <= 0) { 391 tftp_log(LOG_ERR, 392 "write_file returned %d", writesize); 393 if (writesize < 0) 394 send_error(peer, errno + 100); 395 else 396 send_error(peer, ENOSPACE); 397 return -1; 398 } 399 } 400 if (n_data != segsize) 401 write_close(); 402 windowblock++; 403 404 /* Only send ACKs for the last block in the window. */ 405 if (windowblock < windowsize && n_data == segsize) 406 continue; 407 send_ack: 408 for (i = 0; ; i++) { 409 n_ack = send_ack(peer, *block); 410 if (n_ack > 0) { 411 412 if (i == maxtimeouts) { 413 tftp_log(LOG_ERR, 414 "Cannot send ACK packet #%d, " 415 "giving up", *block); 416 return -1; 417 } 418 419 tftp_log(LOG_ERR, 420 "Cannot send ACK packet #%d, trying again", 421 *block); 422 continue; 423 } 424 425 if (debug & DEBUG_SIMPLE) 426 tftp_log(LOG_DEBUG, "Sent ACK for %d", *block); 427 windowblock = 0; 428 break; 429 } 430 gettimeofday(&(ts->tstop), NULL); 431 } while (n_data == segsize); 432 433 /* Don't do late packet management for the client implementation */ 434 if (acting_as_client) 435 return 0; 436 437 for (i = 0; ; i++) { 438 n_data = receive_packet(peer, (char *)rp, pktsize, 439 NULL, -timeoutpacket); 440 if (n_data <= 0) 441 break; 442 if (n_data > 0 && 443 rp->th_opcode == DATA && /* and got a data block */ 444 *block == rp->th_block) /* then my last ack was lost */ 445 send_ack(peer, *block); /* resend final ack */ 446 } 447 448 return 0; 449 } 450