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/param.h> 32e7ff5475SWarner Losh #include <sys/ioctl.h> 33e7ff5475SWarner Losh #include <sys/socket.h> 34eb0292d9SDag-Erling Smørgrav #include <sys/stat.h> 35eb0292d9SDag-Erling Smørgrav #include <sys/time.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> 430c011985SJohn 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 */ 6136242fc0SDag-Erling Smørgrav int 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); 8936242fc0SDag-Erling Smørgrav return -1; 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); 10336242fc0SDag-Erling Smørgrav return -1; 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); 12036242fc0SDag-Erling Smørgrav return -1; 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++; 1280c011985SJohn Baldwin if (seek_file(window[0].offset) != 0) { 1290c011985SJohn Baldwin tftp_log(LOG_ERR, 1300c011985SJohn Baldwin "seek_file failed: %s", 1310c011985SJohn Baldwin strerror(errno)); 1320c011985SJohn Baldwin send_error(peer, errno + 100); 13336242fc0SDag-Erling Smørgrav return -1; 1340c011985SJohn 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)); 14436242fc0SDag-Erling Smørgrav return -1; 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++; 1680c011985SJohn Baldwin if (seek_file(window[0].offset) != 0) { 1690c011985SJohn Baldwin tftp_log(LOG_ERR, 1700c011985SJohn Baldwin "seek_file failed: %s", 1710c011985SJohn Baldwin strerror(errno)); 1720c011985SJohn Baldwin send_error(peer, errno + 100); 17336242fc0SDag-Erling Smørgrav return -1; 1740c011985SJohn 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"); 1990c011985SJohn Baldwin if (seek_file(window[i + 1].offset) != 2000c011985SJohn Baldwin 0) { 2010c011985SJohn Baldwin tftp_log(LOG_ERR, 2020c011985SJohn Baldwin "seek_file failed: %s", 2030c011985SJohn Baldwin strerror(errno)); 2040c011985SJohn Baldwin send_error(peer, errno + 100); 20536242fc0SDag-Erling Smørgrav return -1; 2060c011985SJohn 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); 23536242fc0SDag-Erling Smørgrav return 0; 236e7ff5475SWarner Losh } 237e7ff5475SWarner Losh 238e7ff5475SWarner Losh /* 239e7ff5475SWarner Losh * Receive a file via the TFTP data session. 240e7ff5475SWarner Losh * 241e7ff5475SWarner Losh * - It could be that the first block has already arrived while 242e7ff5475SWarner Losh * trying to figure out if we were receiving options or not. In 243e7ff5475SWarner Losh * that case it is passed to this function. 244e7ff5475SWarner Losh */ 24536242fc0SDag-Erling Smørgrav int 246e7ff5475SWarner Losh tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts, 247e7ff5475SWarner Losh struct tftphdr *firstblock, size_t fb_size) 248e7ff5475SWarner Losh { 249e7ff5475SWarner Losh struct tftphdr *rp; 250fdf929ffSJohn Baldwin uint16_t oldblock, windowstart; 251fdf929ffSJohn Baldwin int n_data, n_ack, writesize, i, retry, windowblock; 252e7ff5475SWarner Losh char recvbuffer[MAXPKTSIZE]; 253e7ff5475SWarner Losh 254e7ff5475SWarner Losh ts->amount = 0; 255fdf929ffSJohn Baldwin windowblock = 0; 256e7ff5475SWarner Losh 257e7ff5475SWarner Losh if (firstblock != NULL) { 258e7ff5475SWarner Losh writesize = write_file(firstblock->th_data, fb_size); 259e7ff5475SWarner Losh ts->amount += writesize; 2603696db92SMichael Tuexen ts->blocks++; 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); 27036242fc0SDag-Erling Smørgrav return -1; 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) { 2831f67c37cSMichael Tuexen write_close(); 284e7ff5475SWarner Losh gettimeofday(&(ts->tstop), NULL); 28536242fc0SDag-Erling Smørgrav return 0; 286e7ff5475SWarner Losh } 287e7ff5475SWarner Losh } 288e7ff5475SWarner Losh 289e7ff5475SWarner Losh rp = (struct tftphdr *)recvbuffer; 290e7ff5475SWarner Losh do { 291e7ff5475SWarner Losh oldblock = *block; 292e7ff5475SWarner Losh (*block)++; 293e7ff5475SWarner Losh if (oldblock > *block) { 294e7ff5475SWarner Losh if (options[OPT_ROLLOVER].o_request == NULL) { 29538bd7db3SCraig Rodrigues /* 29638bd7db3SCraig Rodrigues * "rollover" option not specified in 29738bd7db3SCraig Rodrigues * tftp client. Default to rolling block 29838bd7db3SCraig Rodrigues * counter to 0. 29938bd7db3SCraig Rodrigues */ 30038bd7db3SCraig Rodrigues *block = 0; 30138bd7db3SCraig Rodrigues } else { 30238bd7db3SCraig Rodrigues *block = atoi(options[OPT_ROLLOVER].o_request); 303e7ff5475SWarner Losh } 304e7ff5475SWarner Losh 305e7ff5475SWarner Losh ts->rollovers++; 306e7ff5475SWarner Losh } 307e7ff5475SWarner Losh 308e7ff5475SWarner Losh for (retry = 0; ; retry++) { 309e7ff5475SWarner Losh if (debug & DEBUG_SIMPLE) 310e7ff5475SWarner Losh tftp_log(LOG_DEBUG, 311fdf929ffSJohn Baldwin "Receiving DATA block %d (window block %d)", 312fdf929ffSJohn Baldwin *block, windowblock); 313e7ff5475SWarner Losh 314e7ff5475SWarner Losh n_data = receive_packet(peer, recvbuffer, 315e7ff5475SWarner Losh MAXPKTSIZE, NULL, timeoutpacket); 316e7ff5475SWarner Losh if (n_data < 0) { 317e7ff5475SWarner Losh if (retry == maxtimeouts) { 318e7ff5475SWarner Losh tftp_log(LOG_ERR, 319e7ff5475SWarner Losh "Timeout #%d on DATA block %d, " 320e7ff5475SWarner Losh "giving up", retry, *block); 32136242fc0SDag-Erling Smørgrav return -1; 322e7ff5475SWarner Losh } 323e7ff5475SWarner Losh if (n_data == RP_TIMEOUT) { 324e7ff5475SWarner Losh tftp_log(LOG_WARNING, 325e7ff5475SWarner Losh "Timeout #%d on DATA block %d", 326e7ff5475SWarner Losh retry, *block); 327e7ff5475SWarner Losh send_ack(peer, oldblock); 328fdf929ffSJohn Baldwin windowblock = 0; 329e7ff5475SWarner Losh continue; 330e7ff5475SWarner Losh } 331e7ff5475SWarner Losh 332e7ff5475SWarner Losh /* Either read failure or ERROR packet */ 333e7ff5475SWarner Losh if (debug & DEBUG_SIMPLE) 334e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Aborting: %s", 335e7ff5475SWarner Losh rp_strerror(n_data)); 33636242fc0SDag-Erling Smørgrav return -1; 337e7ff5475SWarner Losh } 338e7ff5475SWarner Losh if (rp->th_opcode == DATA) { 339e7ff5475SWarner Losh ts->blocks++; 340e7ff5475SWarner Losh 341e7ff5475SWarner Losh if (rp->th_block == *block) 342e7ff5475SWarner Losh break; 343e7ff5475SWarner Losh 344fdf929ffSJohn Baldwin /* 345fdf929ffSJohn Baldwin * Ignore duplicate blocks within the 346fdf929ffSJohn Baldwin * window. 347fdf929ffSJohn Baldwin * 348fdf929ffSJohn Baldwin * This does not handle duplicate 349fdf929ffSJohn Baldwin * blocks during a rollover as 350fdf929ffSJohn Baldwin * gracefully, but that should still 351fdf929ffSJohn Baldwin * recover eventually. 352fdf929ffSJohn Baldwin */ 353fdf929ffSJohn Baldwin if (*block > windowsize) 354fdf929ffSJohn Baldwin windowstart = *block - windowsize; 355fdf929ffSJohn Baldwin else 356fdf929ffSJohn Baldwin windowstart = 0; 357fdf929ffSJohn Baldwin if (rp->th_block > windowstart && 358fdf929ffSJohn Baldwin rp->th_block < *block) { 359fdf929ffSJohn Baldwin if (debug & DEBUG_SIMPLE) 360fdf929ffSJohn Baldwin tftp_log(LOG_DEBUG, 361fdf929ffSJohn Baldwin "Ignoring duplicate DATA block %d", 362fdf929ffSJohn Baldwin rp->th_block); 363fdf929ffSJohn Baldwin windowblock++; 364fdf929ffSJohn Baldwin retry = 0; 365fdf929ffSJohn Baldwin continue; 366fdf929ffSJohn Baldwin } 367fdf929ffSJohn Baldwin 368e7ff5475SWarner Losh tftp_log(LOG_WARNING, 369e7ff5475SWarner Losh "Expected DATA block %d, got block %d", 370e7ff5475SWarner Losh *block, rp->th_block); 371e7ff5475SWarner Losh 372e7ff5475SWarner Losh /* Re-synchronize with the other side */ 373e7ff5475SWarner Losh (void) synchnet(peer); 374fdf929ffSJohn Baldwin 375e7ff5475SWarner Losh tftp_log(LOG_INFO, "Trying to sync"); 376e7ff5475SWarner Losh *block = oldblock; 377e7ff5475SWarner Losh ts->retries++; 378e7ff5475SWarner Losh goto send_ack; /* rexmit */ 379e7ff5475SWarner Losh 380e7ff5475SWarner Losh } else { 381e7ff5475SWarner Losh tftp_log(LOG_WARNING, 382e7ff5475SWarner Losh "Expected DATA block, got %s block", 383e7ff5475SWarner Losh packettype(rp->th_opcode)); 384e7ff5475SWarner Losh } 385e7ff5475SWarner Losh } 386e7ff5475SWarner Losh 387e7ff5475SWarner Losh if (n_data > 0) { 388e7ff5475SWarner Losh writesize = write_file(rp->th_data, n_data); 389e7ff5475SWarner Losh ts->amount += writesize; 390e7ff5475SWarner Losh if (writesize <= 0) { 391e7ff5475SWarner Losh tftp_log(LOG_ERR, 392e7ff5475SWarner Losh "write_file returned %d", writesize); 393e7ff5475SWarner Losh if (writesize < 0) 394e7ff5475SWarner Losh send_error(peer, errno + 100); 395e7ff5475SWarner Losh else 396e7ff5475SWarner Losh send_error(peer, ENOSPACE); 39736242fc0SDag-Erling Smørgrav return -1; 398e7ff5475SWarner Losh } 3991d0272a6SMichael Tuexen } 4007378015bSAlan Somers if (n_data != segsize) 4017378015bSAlan Somers write_close(); 402fdf929ffSJohn Baldwin windowblock++; 403e7ff5475SWarner Losh 404fdf929ffSJohn Baldwin /* Only send ACKs for the last block in the window. */ 405fdf929ffSJohn Baldwin if (windowblock < windowsize && n_data == segsize) 406fdf929ffSJohn Baldwin continue; 407e7ff5475SWarner Losh send_ack: 408e7ff5475SWarner Losh for (i = 0; ; i++) { 409e7ff5475SWarner Losh n_ack = send_ack(peer, *block); 410e7ff5475SWarner Losh if (n_ack > 0) { 411e7ff5475SWarner Losh 412e7ff5475SWarner Losh if (i == maxtimeouts) { 413e7ff5475SWarner Losh tftp_log(LOG_ERR, 414e7ff5475SWarner Losh "Cannot send ACK packet #%d, " 415e7ff5475SWarner Losh "giving up", *block); 41636242fc0SDag-Erling Smørgrav return -1; 417e7ff5475SWarner Losh } 418e7ff5475SWarner Losh 419e7ff5475SWarner Losh tftp_log(LOG_ERR, 420e7ff5475SWarner Losh "Cannot send ACK packet #%d, trying again", 421e7ff5475SWarner Losh *block); 422e7ff5475SWarner Losh continue; 423e7ff5475SWarner Losh } 424e7ff5475SWarner Losh 425fdf929ffSJohn Baldwin if (debug & DEBUG_SIMPLE) 426fdf929ffSJohn Baldwin tftp_log(LOG_DEBUG, "Sent ACK for %d", *block); 427fdf929ffSJohn Baldwin windowblock = 0; 428e7ff5475SWarner Losh break; 429e7ff5475SWarner Losh } 430e7ff5475SWarner Losh gettimeofday(&(ts->tstop), NULL); 431e7ff5475SWarner Losh } while (n_data == segsize); 432e7ff5475SWarner Losh 433e7ff5475SWarner Losh /* Don't do late packet management for the client implementation */ 434e7ff5475SWarner Losh if (acting_as_client) 43536242fc0SDag-Erling Smørgrav return 0; 436e7ff5475SWarner Losh 437e7ff5475SWarner Losh for (i = 0; ; i++) { 438e7ff5475SWarner Losh n_data = receive_packet(peer, (char *)rp, pktsize, 439*e3b4cb1bSDag-Erling Smørgrav NULL, -timeoutpacket); 440e7ff5475SWarner Losh if (n_data <= 0) 441e7ff5475SWarner Losh break; 442e7ff5475SWarner Losh if (n_data > 0 && 443e7ff5475SWarner Losh rp->th_opcode == DATA && /* and got a data block */ 444e7ff5475SWarner Losh *block == rp->th_block) /* then my last ack was lost */ 445e7ff5475SWarner Losh send_ack(peer, *block); /* resend final ack */ 446e7ff5475SWarner Losh } 447e7ff5475SWarner Losh 44836242fc0SDag-Erling Smørgrav return 0; 449e7ff5475SWarner Losh } 450