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