1e6209940SPedro F. Giffuni /*- 2e6209940SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3e6209940SPedro F. Giffuni * 4e7ff5475SWarner Losh * Copyright (C) 2008 Edwin Groothuis. All rights reserved. 5e7ff5475SWarner Losh * 6e7ff5475SWarner Losh * Redistribution and use in source and binary forms, with or without 7e7ff5475SWarner Losh * modification, are permitted provided that the following conditions 8e7ff5475SWarner Losh * are met: 9e7ff5475SWarner Losh * 1. Redistributions of source code must retain the above copyright 10e7ff5475SWarner Losh * notice, this list of conditions and the following disclaimer. 11e7ff5475SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 12e7ff5475SWarner Losh * notice, this list of conditions and the following disclaimer in the 13e7ff5475SWarner Losh * documentation and/or other materials provided with the distribution. 14e7ff5475SWarner Losh * 15e7ff5475SWarner Losh * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16e7ff5475SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17e7ff5475SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18e7ff5475SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 19e7ff5475SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20e7ff5475SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21e7ff5475SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22e7ff5475SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23e7ff5475SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24e7ff5475SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25e7ff5475SWarner Losh * SUCH DAMAGE. 26e7ff5475SWarner Losh */ 27e7ff5475SWarner Losh 28e7ff5475SWarner Losh #include <sys/cdefs.h> 29e7ff5475SWarner Losh __FBSDID("$FreeBSD$"); 30e7ff5475SWarner Losh 31e7ff5475SWarner Losh #include <sys/types.h> 32e7ff5475SWarner Losh #include <sys/param.h> 33e7ff5475SWarner Losh #include <sys/ioctl.h> 34e7ff5475SWarner Losh #include <sys/stat.h> 35e7ff5475SWarner Losh #include <sys/socket.h> 36e7ff5475SWarner Losh 37e7ff5475SWarner Losh #include <netinet/in.h> 38e7ff5475SWarner Losh #include <arpa/tftp.h> 39e7ff5475SWarner Losh 40e7ff5475SWarner Losh #include <errno.h> 41e7ff5475SWarner Losh #include <stdio.h> 42e7ff5475SWarner Losh #include <stdlib.h> 43*0c011985SJohn Baldwin #include <string.h> 44e7ff5475SWarner Losh #include <syslog.h> 45e7ff5475SWarner Losh 46e7ff5475SWarner Losh #include "tftp-file.h" 47e7ff5475SWarner Losh #include "tftp-io.h" 48e7ff5475SWarner Losh #include "tftp-utils.h" 49e7ff5475SWarner Losh #include "tftp-options.h" 50e7ff5475SWarner Losh #include "tftp-transfer.h" 51e7ff5475SWarner Losh 52fdf929ffSJohn Baldwin struct block_data { 53fdf929ffSJohn Baldwin off_t offset; 54fdf929ffSJohn Baldwin uint16_t block; 55fdf929ffSJohn Baldwin int size; 56fdf929ffSJohn Baldwin }; 57fdf929ffSJohn Baldwin 58e7ff5475SWarner Losh /* 59e7ff5475SWarner Losh * Send a file via the TFTP data session. 60e7ff5475SWarner Losh */ 61e7ff5475SWarner Losh void 62e7ff5475SWarner Losh tftp_send(int peer, uint16_t *block, struct tftp_stats *ts) 63e7ff5475SWarner Losh { 64e7ff5475SWarner Losh struct tftphdr *rp; 65fdf929ffSJohn Baldwin int size, n_data, n_ack, sendtry, acktry; 66fdf929ffSJohn Baldwin u_int i, j; 67fdf929ffSJohn Baldwin uint16_t oldblock, windowblock; 68e7ff5475SWarner Losh char sendbuffer[MAXPKTSIZE]; 69e7ff5475SWarner Losh char recvbuffer[MAXPKTSIZE]; 70fdf929ffSJohn Baldwin struct block_data window[WINDOWSIZE_MAX]; 71e7ff5475SWarner Losh 72e7ff5475SWarner Losh rp = (struct tftphdr *)recvbuffer; 73e7ff5475SWarner Losh *block = 1; 74e7ff5475SWarner Losh ts->amount = 0; 75fdf929ffSJohn Baldwin windowblock = 0; 76fdf929ffSJohn Baldwin acktry = 0; 77e7ff5475SWarner Losh do { 78fdf929ffSJohn Baldwin read_block: 79e7ff5475SWarner Losh if (debug&DEBUG_SIMPLE) 80fdf929ffSJohn Baldwin tftp_log(LOG_DEBUG, "Sending block %d (window block %d)", 81fdf929ffSJohn Baldwin *block, windowblock); 82e7ff5475SWarner Losh 83fdf929ffSJohn Baldwin window[windowblock].offset = tell_file(); 84fdf929ffSJohn Baldwin window[windowblock].block = *block; 85e7ff5475SWarner Losh size = read_file(sendbuffer, segsize); 86e7ff5475SWarner Losh if (size < 0) { 87e7ff5475SWarner Losh tftp_log(LOG_ERR, "read_file returned %d", size); 88e7ff5475SWarner Losh send_error(peer, errno + 100); 89e7ff5475SWarner Losh goto abort; 90e7ff5475SWarner Losh } 91fdf929ffSJohn Baldwin window[windowblock].size = size; 92fdf929ffSJohn Baldwin windowblock++; 93e7ff5475SWarner Losh 94fdf929ffSJohn Baldwin for (sendtry = 0; ; sendtry++) { 95e7ff5475SWarner Losh n_data = send_data(peer, *block, sendbuffer, size); 96fdf929ffSJohn Baldwin if (n_data == 0) 97fdf929ffSJohn Baldwin break; 98fdf929ffSJohn Baldwin 99fdf929ffSJohn Baldwin if (sendtry == maxtimeouts) { 100e7ff5475SWarner Losh tftp_log(LOG_ERR, 101e7ff5475SWarner Losh "Cannot send DATA packet #%d, " 102e7ff5475SWarner Losh "giving up", *block); 103e7ff5475SWarner Losh return; 104e7ff5475SWarner Losh } 105e7ff5475SWarner Losh tftp_log(LOG_ERR, 106e7ff5475SWarner Losh "Cannot send DATA packet #%d, trying again", 107e7ff5475SWarner Losh *block); 108e7ff5475SWarner Losh } 109e7ff5475SWarner Losh 110fdf929ffSJohn Baldwin /* Only check for ACK for last block in window. */ 111fdf929ffSJohn Baldwin if (windowblock == windowsize || size != segsize) { 112e7ff5475SWarner Losh n_ack = receive_packet(peer, recvbuffer, 113e7ff5475SWarner Losh MAXPKTSIZE, NULL, timeoutpacket); 114e7ff5475SWarner Losh if (n_ack < 0) { 115e7ff5475SWarner Losh if (n_ack == RP_TIMEOUT) { 116fdf929ffSJohn Baldwin if (acktry == maxtimeouts) { 117e7ff5475SWarner Losh tftp_log(LOG_ERR, 118e7ff5475SWarner Losh "Timeout #%d send ACK %d " 119fdf929ffSJohn Baldwin "giving up", acktry, *block); 120e7ff5475SWarner Losh return; 121e7ff5475SWarner Losh } 122e7ff5475SWarner Losh tftp_log(LOG_WARNING, 123e7ff5475SWarner Losh "Timeout #%d on ACK %d", 124fdf929ffSJohn Baldwin acktry, *block); 125fdf929ffSJohn Baldwin 126fdf929ffSJohn Baldwin acktry++; 127fdf929ffSJohn Baldwin ts->retries++; 128*0c011985SJohn Baldwin if (seek_file(window[0].offset) != 0) { 129*0c011985SJohn Baldwin tftp_log(LOG_ERR, 130*0c011985SJohn Baldwin "seek_file failed: %s", 131*0c011985SJohn Baldwin strerror(errno)); 132*0c011985SJohn Baldwin send_error(peer, errno + 100); 133*0c011985SJohn Baldwin goto abort; 134*0c011985SJohn Baldwin } 135fdf929ffSJohn Baldwin *block = window[0].block; 136fdf929ffSJohn Baldwin windowblock = 0; 137fdf929ffSJohn Baldwin goto read_block; 138e7ff5475SWarner Losh } 139e7ff5475SWarner Losh 140e7ff5475SWarner Losh /* Either read failure or ERROR packet */ 141e7ff5475SWarner Losh if (debug&DEBUG_SIMPLE) 142e7ff5475SWarner Losh tftp_log(LOG_ERR, "Aborting: %s", 143e7ff5475SWarner Losh rp_strerror(n_ack)); 144e7ff5475SWarner Losh goto abort; 145e7ff5475SWarner Losh } 146e7ff5475SWarner Losh if (rp->th_opcode == ACK) { 147fdf929ffSJohn Baldwin /* 148fdf929ffSJohn Baldwin * Look for the ACKed block in our open 149fdf929ffSJohn Baldwin * window. 150fdf929ffSJohn Baldwin */ 151fdf929ffSJohn Baldwin for (i = 0; i < windowblock; i++) { 152fdf929ffSJohn Baldwin if (rp->th_block == window[i].block) 153e7ff5475SWarner Losh break; 154e7ff5475SWarner Losh } 155e7ff5475SWarner Losh 156fdf929ffSJohn Baldwin if (i == windowblock) { 157fdf929ffSJohn Baldwin /* Did not recognize ACK. */ 158fdf929ffSJohn Baldwin if (debug&DEBUG_SIMPLE) 159fdf929ffSJohn Baldwin tftp_log(LOG_DEBUG, 160fdf929ffSJohn Baldwin "ACK %d out of window", 161fdf929ffSJohn Baldwin rp->th_block); 162fdf929ffSJohn Baldwin 163e7ff5475SWarner Losh /* Re-synchronize with the other side */ 164e7ff5475SWarner Losh (void) synchnet(peer); 165fdf929ffSJohn Baldwin 166fdf929ffSJohn Baldwin /* Resend the current window. */ 167e7ff5475SWarner Losh ts->retries++; 168*0c011985SJohn Baldwin if (seek_file(window[0].offset) != 0) { 169*0c011985SJohn Baldwin tftp_log(LOG_ERR, 170*0c011985SJohn Baldwin "seek_file failed: %s", 171*0c011985SJohn Baldwin strerror(errno)); 172*0c011985SJohn Baldwin send_error(peer, errno + 100); 173*0c011985SJohn Baldwin goto abort; 174*0c011985SJohn Baldwin } 175fdf929ffSJohn Baldwin *block = window[0].block; 176fdf929ffSJohn Baldwin windowblock = 0; 177fdf929ffSJohn Baldwin goto read_block; 178e7ff5475SWarner Losh } 179fdf929ffSJohn Baldwin 180fdf929ffSJohn Baldwin /* ACKed at least some data. */ 181fdf929ffSJohn Baldwin acktry = 0; 182fdf929ffSJohn Baldwin for (j = 0; j <= i; j++) { 183fdf929ffSJohn Baldwin if (debug&DEBUG_SIMPLE) 184fdf929ffSJohn Baldwin tftp_log(LOG_DEBUG, 185fdf929ffSJohn Baldwin "ACKed block %d", 186fdf929ffSJohn Baldwin window[j].block); 187fdf929ffSJohn Baldwin ts->blocks++; 188fdf929ffSJohn Baldwin ts->amount += window[j].size; 189fdf929ffSJohn Baldwin } 190fdf929ffSJohn Baldwin 191fdf929ffSJohn Baldwin /* 192fdf929ffSJohn Baldwin * Partial ACK. Rewind state to first 193fdf929ffSJohn Baldwin * un-ACKed block. 194fdf929ffSJohn Baldwin */ 195fdf929ffSJohn Baldwin if (i + 1 != windowblock) { 196fdf929ffSJohn Baldwin if (debug&DEBUG_SIMPLE) 197fdf929ffSJohn Baldwin tftp_log(LOG_DEBUG, 198fdf929ffSJohn Baldwin "Partial ACK"); 199*0c011985SJohn Baldwin if (seek_file(window[i + 1].offset) != 200*0c011985SJohn Baldwin 0) { 201*0c011985SJohn Baldwin tftp_log(LOG_ERR, 202*0c011985SJohn Baldwin "seek_file failed: %s", 203*0c011985SJohn Baldwin strerror(errno)); 204*0c011985SJohn Baldwin send_error(peer, errno + 100); 205*0c011985SJohn Baldwin goto abort; 206*0c011985SJohn Baldwin } 207fdf929ffSJohn Baldwin *block = window[i + 1].block; 208fdf929ffSJohn Baldwin windowblock = 0; 209fdf929ffSJohn Baldwin ts->retries++; 210fdf929ffSJohn Baldwin goto read_block; 211fdf929ffSJohn Baldwin } 212fdf929ffSJohn Baldwin 213fdf929ffSJohn Baldwin windowblock = 0; 214e7ff5475SWarner Losh } 215e7ff5475SWarner Losh 216e7ff5475SWarner Losh } 217e7ff5475SWarner Losh oldblock = *block; 218e7ff5475SWarner Losh (*block)++; 219e7ff5475SWarner Losh if (oldblock > *block) { 220e7ff5475SWarner Losh if (options[OPT_ROLLOVER].o_request == NULL) { 22138bd7db3SCraig Rodrigues /* 22238bd7db3SCraig Rodrigues * "rollover" option not specified in 22338bd7db3SCraig Rodrigues * tftp client. Default to rolling block 22438bd7db3SCraig Rodrigues * counter to 0. 22538bd7db3SCraig Rodrigues */ 22638bd7db3SCraig Rodrigues *block = 0; 22738bd7db3SCraig Rodrigues } else { 22838bd7db3SCraig Rodrigues *block = atoi(options[OPT_ROLLOVER].o_request); 229e7ff5475SWarner Losh } 230e7ff5475SWarner Losh 231e7ff5475SWarner Losh ts->rollovers++; 232e7ff5475SWarner Losh } 233e7ff5475SWarner Losh gettimeofday(&(ts->tstop), NULL); 234e7ff5475SWarner Losh } while (size == segsize); 235e7ff5475SWarner Losh abort: 236e7ff5475SWarner Losh return; 237e7ff5475SWarner Losh } 238e7ff5475SWarner Losh 239e7ff5475SWarner Losh /* 240e7ff5475SWarner Losh * Receive a file via the TFTP data session. 241e7ff5475SWarner Losh * 242e7ff5475SWarner Losh * - It could be that the first block has already arrived while 243e7ff5475SWarner Losh * trying to figure out if we were receiving options or not. In 244e7ff5475SWarner Losh * that case it is passed to this function. 245e7ff5475SWarner Losh */ 246e7ff5475SWarner Losh void 247e7ff5475SWarner Losh tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts, 248e7ff5475SWarner Losh struct tftphdr *firstblock, size_t fb_size) 249e7ff5475SWarner Losh { 250e7ff5475SWarner Losh struct tftphdr *rp; 251fdf929ffSJohn Baldwin uint16_t oldblock, windowstart; 252fdf929ffSJohn Baldwin int n_data, n_ack, writesize, i, retry, windowblock; 253e7ff5475SWarner Losh char recvbuffer[MAXPKTSIZE]; 254e7ff5475SWarner Losh 255e7ff5475SWarner Losh ts->amount = 0; 256fdf929ffSJohn Baldwin windowblock = 0; 257e7ff5475SWarner Losh 258e7ff5475SWarner Losh if (firstblock != NULL) { 259e7ff5475SWarner Losh writesize = write_file(firstblock->th_data, fb_size); 260e7ff5475SWarner Losh ts->amount += writesize; 261fdf929ffSJohn Baldwin windowblock++; 262fdf929ffSJohn Baldwin if (windowsize == 1 || fb_size != segsize) { 263e7ff5475SWarner Losh for (i = 0; ; i++) { 264e7ff5475SWarner Losh n_ack = send_ack(peer, *block); 265e7ff5475SWarner Losh if (n_ack > 0) { 266e7ff5475SWarner Losh if (i == maxtimeouts) { 267e7ff5475SWarner Losh tftp_log(LOG_ERR, 268e7ff5475SWarner Losh "Cannot send ACK packet #%d, " 269e7ff5475SWarner Losh "giving up", *block); 270e7ff5475SWarner Losh return; 271e7ff5475SWarner Losh } 272e7ff5475SWarner Losh tftp_log(LOG_ERR, 273e7ff5475SWarner Losh "Cannot send ACK packet #%d, trying again", 274e7ff5475SWarner Losh *block); 275e7ff5475SWarner Losh continue; 276e7ff5475SWarner Losh } 277e7ff5475SWarner Losh 278e7ff5475SWarner Losh break; 279e7ff5475SWarner Losh } 280fdf929ffSJohn Baldwin } 281e7ff5475SWarner Losh 282e7ff5475SWarner Losh if (fb_size != segsize) { 283e7ff5475SWarner Losh gettimeofday(&(ts->tstop), NULL); 284e7ff5475SWarner Losh return; 285e7ff5475SWarner Losh } 286e7ff5475SWarner Losh } 287e7ff5475SWarner Losh 288e7ff5475SWarner Losh rp = (struct tftphdr *)recvbuffer; 289e7ff5475SWarner Losh do { 290e7ff5475SWarner Losh oldblock = *block; 291e7ff5475SWarner Losh (*block)++; 292e7ff5475SWarner Losh if (oldblock > *block) { 293e7ff5475SWarner Losh if (options[OPT_ROLLOVER].o_request == NULL) { 29438bd7db3SCraig Rodrigues /* 29538bd7db3SCraig Rodrigues * "rollover" option not specified in 29638bd7db3SCraig Rodrigues * tftp client. Default to rolling block 29738bd7db3SCraig Rodrigues * counter to 0. 29838bd7db3SCraig Rodrigues */ 29938bd7db3SCraig Rodrigues *block = 0; 30038bd7db3SCraig Rodrigues } else { 30138bd7db3SCraig Rodrigues *block = atoi(options[OPT_ROLLOVER].o_request); 302e7ff5475SWarner Losh } 303e7ff5475SWarner Losh 304e7ff5475SWarner Losh ts->rollovers++; 305e7ff5475SWarner Losh } 306e7ff5475SWarner Losh 307e7ff5475SWarner Losh for (retry = 0; ; retry++) { 308e7ff5475SWarner Losh if (debug&DEBUG_SIMPLE) 309e7ff5475SWarner Losh tftp_log(LOG_DEBUG, 310fdf929ffSJohn Baldwin "Receiving DATA block %d (window block %d)", 311fdf929ffSJohn Baldwin *block, windowblock); 312e7ff5475SWarner Losh 313e7ff5475SWarner Losh n_data = receive_packet(peer, recvbuffer, 314e7ff5475SWarner Losh MAXPKTSIZE, NULL, timeoutpacket); 315e7ff5475SWarner Losh if (n_data < 0) { 316e7ff5475SWarner Losh if (retry == maxtimeouts) { 317e7ff5475SWarner Losh tftp_log(LOG_ERR, 318e7ff5475SWarner Losh "Timeout #%d on DATA block %d, " 319e7ff5475SWarner Losh "giving up", retry, *block); 320e7ff5475SWarner Losh return; 321e7ff5475SWarner Losh } 322e7ff5475SWarner Losh if (n_data == RP_TIMEOUT) { 323e7ff5475SWarner Losh tftp_log(LOG_WARNING, 324e7ff5475SWarner Losh "Timeout #%d on DATA block %d", 325e7ff5475SWarner Losh retry, *block); 326e7ff5475SWarner Losh send_ack(peer, oldblock); 327fdf929ffSJohn Baldwin windowblock = 0; 328e7ff5475SWarner Losh continue; 329e7ff5475SWarner Losh } 330e7ff5475SWarner Losh 331e7ff5475SWarner Losh /* Either read failure or ERROR packet */ 332e7ff5475SWarner Losh if (debug&DEBUG_SIMPLE) 333e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Aborting: %s", 334e7ff5475SWarner Losh rp_strerror(n_data)); 335e7ff5475SWarner Losh goto abort; 336e7ff5475SWarner Losh } 337e7ff5475SWarner Losh if (rp->th_opcode == DATA) { 338e7ff5475SWarner Losh ts->blocks++; 339e7ff5475SWarner Losh 340e7ff5475SWarner Losh if (rp->th_block == *block) 341e7ff5475SWarner Losh break; 342e7ff5475SWarner Losh 343fdf929ffSJohn Baldwin /* 344fdf929ffSJohn Baldwin * Ignore duplicate blocks within the 345fdf929ffSJohn Baldwin * window. 346fdf929ffSJohn Baldwin * 347fdf929ffSJohn Baldwin * This does not handle duplicate 348fdf929ffSJohn Baldwin * blocks during a rollover as 349fdf929ffSJohn Baldwin * gracefully, but that should still 350fdf929ffSJohn Baldwin * recover eventually. 351fdf929ffSJohn Baldwin */ 352fdf929ffSJohn Baldwin if (*block > windowsize) 353fdf929ffSJohn Baldwin windowstart = *block - windowsize; 354fdf929ffSJohn Baldwin else 355fdf929ffSJohn Baldwin windowstart = 0; 356fdf929ffSJohn Baldwin if (rp->th_block > windowstart && 357fdf929ffSJohn Baldwin rp->th_block < *block) { 358fdf929ffSJohn Baldwin if (debug&DEBUG_SIMPLE) 359fdf929ffSJohn Baldwin tftp_log(LOG_DEBUG, 360fdf929ffSJohn Baldwin "Ignoring duplicate DATA block %d", 361fdf929ffSJohn Baldwin rp->th_block); 362fdf929ffSJohn Baldwin windowblock++; 363fdf929ffSJohn Baldwin retry = 0; 364fdf929ffSJohn Baldwin continue; 365fdf929ffSJohn Baldwin } 366fdf929ffSJohn Baldwin 367e7ff5475SWarner Losh tftp_log(LOG_WARNING, 368e7ff5475SWarner Losh "Expected DATA block %d, got block %d", 369e7ff5475SWarner Losh *block, rp->th_block); 370e7ff5475SWarner Losh 371e7ff5475SWarner Losh /* Re-synchronize with the other side */ 372e7ff5475SWarner Losh (void) synchnet(peer); 373fdf929ffSJohn Baldwin 374e7ff5475SWarner Losh tftp_log(LOG_INFO, "Trying to sync"); 375e7ff5475SWarner Losh *block = oldblock; 376e7ff5475SWarner Losh ts->retries++; 377e7ff5475SWarner Losh goto send_ack; /* rexmit */ 378e7ff5475SWarner Losh 379e7ff5475SWarner Losh } else { 380e7ff5475SWarner Losh tftp_log(LOG_WARNING, 381e7ff5475SWarner Losh "Expected DATA block, got %s block", 382e7ff5475SWarner Losh packettype(rp->th_opcode)); 383e7ff5475SWarner Losh } 384e7ff5475SWarner Losh } 385e7ff5475SWarner Losh 386e7ff5475SWarner Losh if (n_data > 0) { 387e7ff5475SWarner Losh writesize = write_file(rp->th_data, n_data); 388e7ff5475SWarner Losh ts->amount += writesize; 389e7ff5475SWarner Losh if (writesize <= 0) { 390e7ff5475SWarner Losh tftp_log(LOG_ERR, 391e7ff5475SWarner Losh "write_file returned %d", writesize); 392e7ff5475SWarner Losh if (writesize < 0) 393e7ff5475SWarner Losh send_error(peer, errno + 100); 394e7ff5475SWarner Losh else 395e7ff5475SWarner Losh send_error(peer, ENOSPACE); 396e7ff5475SWarner Losh goto abort; 397e7ff5475SWarner Losh } 3987378015bSAlan Somers if (n_data != segsize) 3997378015bSAlan Somers write_close(); 400e7ff5475SWarner Losh } 401fdf929ffSJohn Baldwin windowblock++; 402e7ff5475SWarner Losh 403fdf929ffSJohn Baldwin /* Only send ACKs for the last block in the window. */ 404fdf929ffSJohn Baldwin if (windowblock < windowsize && n_data == segsize) 405fdf929ffSJohn Baldwin continue; 406e7ff5475SWarner Losh send_ack: 407e7ff5475SWarner Losh for (i = 0; ; i++) { 408e7ff5475SWarner Losh n_ack = send_ack(peer, *block); 409e7ff5475SWarner Losh if (n_ack > 0) { 410e7ff5475SWarner Losh 411e7ff5475SWarner Losh if (i == maxtimeouts) { 412e7ff5475SWarner Losh tftp_log(LOG_ERR, 413e7ff5475SWarner Losh "Cannot send ACK packet #%d, " 414e7ff5475SWarner Losh "giving up", *block); 415e7ff5475SWarner Losh return; 416e7ff5475SWarner Losh } 417e7ff5475SWarner Losh 418e7ff5475SWarner Losh tftp_log(LOG_ERR, 419e7ff5475SWarner Losh "Cannot send ACK packet #%d, trying again", 420e7ff5475SWarner Losh *block); 421e7ff5475SWarner Losh continue; 422e7ff5475SWarner Losh } 423e7ff5475SWarner Losh 424fdf929ffSJohn Baldwin if (debug&DEBUG_SIMPLE) 425fdf929ffSJohn Baldwin tftp_log(LOG_DEBUG, "Sent ACK for %d", *block); 426fdf929ffSJohn Baldwin windowblock = 0; 427e7ff5475SWarner Losh break; 428e7ff5475SWarner Losh } 429e7ff5475SWarner Losh gettimeofday(&(ts->tstop), NULL); 430e7ff5475SWarner Losh } while (n_data == segsize); 431e7ff5475SWarner Losh 432e7ff5475SWarner Losh /* Don't do late packet management for the client implementation */ 433e7ff5475SWarner Losh if (acting_as_client) 434e7ff5475SWarner Losh return; 435e7ff5475SWarner Losh 436e7ff5475SWarner Losh for (i = 0; ; i++) { 437e7ff5475SWarner Losh n_data = receive_packet(peer, (char *)rp, pktsize, 438e7ff5475SWarner Losh NULL, timeoutpacket); 439e7ff5475SWarner Losh if (n_data <= 0) 440e7ff5475SWarner Losh break; 441e7ff5475SWarner Losh if (n_data > 0 && 442e7ff5475SWarner Losh rp->th_opcode == DATA && /* and got a data block */ 443e7ff5475SWarner Losh *block == rp->th_block) /* then my last ack was lost */ 444e7ff5475SWarner Losh send_ack(peer, *block); /* resend final ack */ 445e7ff5475SWarner Losh } 446e7ff5475SWarner Losh 447e7ff5475SWarner Losh abort: 448e7ff5475SWarner Losh return; 449e7ff5475SWarner Losh } 450