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