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