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> 43e7ff5475SWarner Losh #include <syslog.h> 44e7ff5475SWarner Losh 45e7ff5475SWarner Losh #include "tftp-file.h" 46e7ff5475SWarner Losh #include "tftp-io.h" 47e7ff5475SWarner Losh #include "tftp-utils.h" 48e7ff5475SWarner Losh #include "tftp-options.h" 49e7ff5475SWarner Losh #include "tftp-transfer.h" 50e7ff5475SWarner Losh 51e7ff5475SWarner Losh /* 52e7ff5475SWarner Losh * Send a file via the TFTP data session. 53e7ff5475SWarner Losh */ 54e7ff5475SWarner Losh void 55e7ff5475SWarner Losh tftp_send(int peer, uint16_t *block, struct tftp_stats *ts) 56e7ff5475SWarner Losh { 57e7ff5475SWarner Losh struct tftphdr *rp; 58e7ff5475SWarner Losh int size, n_data, n_ack, try; 59e7ff5475SWarner Losh uint16_t oldblock; 60e7ff5475SWarner Losh char sendbuffer[MAXPKTSIZE]; 61e7ff5475SWarner Losh char recvbuffer[MAXPKTSIZE]; 62e7ff5475SWarner Losh 63e7ff5475SWarner Losh rp = (struct tftphdr *)recvbuffer; 64e7ff5475SWarner Losh *block = 1; 65e7ff5475SWarner Losh ts->amount = 0; 66e7ff5475SWarner Losh do { 67e7ff5475SWarner Losh if (debug&DEBUG_SIMPLE) 68e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Sending block %d", *block); 69e7ff5475SWarner Losh 70e7ff5475SWarner Losh size = read_file(sendbuffer, segsize); 71e7ff5475SWarner Losh if (size < 0) { 72e7ff5475SWarner Losh tftp_log(LOG_ERR, "read_file returned %d", size); 73e7ff5475SWarner Losh send_error(peer, errno + 100); 74e7ff5475SWarner Losh goto abort; 75e7ff5475SWarner Losh } 76e7ff5475SWarner Losh 77e7ff5475SWarner Losh for (try = 0; ; try++) { 78e7ff5475SWarner Losh n_data = send_data(peer, *block, sendbuffer, size); 79e7ff5475SWarner Losh if (n_data > 0) { 80e7ff5475SWarner Losh if (try == maxtimeouts) { 81e7ff5475SWarner Losh tftp_log(LOG_ERR, 82e7ff5475SWarner Losh "Cannot send DATA packet #%d, " 83e7ff5475SWarner Losh "giving up", *block); 84e7ff5475SWarner Losh return; 85e7ff5475SWarner Losh } 86e7ff5475SWarner Losh tftp_log(LOG_ERR, 87e7ff5475SWarner Losh "Cannot send DATA packet #%d, trying again", 88e7ff5475SWarner Losh *block); 89e7ff5475SWarner Losh continue; 90e7ff5475SWarner Losh } 91e7ff5475SWarner Losh 92e7ff5475SWarner Losh n_ack = receive_packet(peer, recvbuffer, 93e7ff5475SWarner Losh MAXPKTSIZE, NULL, timeoutpacket); 94e7ff5475SWarner Losh if (n_ack < 0) { 95e7ff5475SWarner Losh if (n_ack == RP_TIMEOUT) { 96e7ff5475SWarner Losh if (try == maxtimeouts) { 97e7ff5475SWarner Losh tftp_log(LOG_ERR, 98e7ff5475SWarner Losh "Timeout #%d send ACK %d " 99e7ff5475SWarner Losh "giving up", try, *block); 100e7ff5475SWarner Losh return; 101e7ff5475SWarner Losh } 102e7ff5475SWarner Losh tftp_log(LOG_WARNING, 103e7ff5475SWarner Losh "Timeout #%d on ACK %d", 104e7ff5475SWarner Losh try, *block); 105e7ff5475SWarner Losh continue; 106e7ff5475SWarner Losh } 107e7ff5475SWarner Losh 108e7ff5475SWarner Losh /* Either read failure or ERROR packet */ 109e7ff5475SWarner Losh if (debug&DEBUG_SIMPLE) 110e7ff5475SWarner Losh tftp_log(LOG_ERR, "Aborting: %s", 111e7ff5475SWarner Losh rp_strerror(n_ack)); 112e7ff5475SWarner Losh goto abort; 113e7ff5475SWarner Losh } 114e7ff5475SWarner Losh if (rp->th_opcode == ACK) { 115e7ff5475SWarner Losh ts->blocks++; 116e7ff5475SWarner Losh if (rp->th_block == *block) { 117e7ff5475SWarner Losh ts->amount += size; 118e7ff5475SWarner Losh break; 119e7ff5475SWarner Losh } 120e7ff5475SWarner Losh 121e7ff5475SWarner Losh /* Re-synchronize with the other side */ 122e7ff5475SWarner Losh (void) synchnet(peer); 123e7ff5475SWarner Losh if (rp->th_block == (*block - 1)) { 124e7ff5475SWarner Losh ts->retries++; 125e7ff5475SWarner Losh continue; 126e7ff5475SWarner Losh } 127e7ff5475SWarner Losh } 128e7ff5475SWarner Losh 129e7ff5475SWarner Losh } 130e7ff5475SWarner Losh oldblock = *block; 131e7ff5475SWarner Losh (*block)++; 132e7ff5475SWarner Losh if (oldblock > *block) { 133e7ff5475SWarner Losh if (options[OPT_ROLLOVER].o_request == NULL) { 13438bd7db3SCraig Rodrigues /* 13538bd7db3SCraig Rodrigues * "rollover" option not specified in 13638bd7db3SCraig Rodrigues * tftp client. Default to rolling block 13738bd7db3SCraig Rodrigues * counter to 0. 13838bd7db3SCraig Rodrigues */ 13938bd7db3SCraig Rodrigues *block = 0; 14038bd7db3SCraig Rodrigues } else { 14138bd7db3SCraig Rodrigues *block = atoi(options[OPT_ROLLOVER].o_request); 142e7ff5475SWarner Losh } 143e7ff5475SWarner Losh 144e7ff5475SWarner Losh ts->rollovers++; 145e7ff5475SWarner Losh } 146e7ff5475SWarner Losh gettimeofday(&(ts->tstop), NULL); 147e7ff5475SWarner Losh } while (size == segsize); 148e7ff5475SWarner Losh abort: 149e7ff5475SWarner Losh return; 150e7ff5475SWarner Losh } 151e7ff5475SWarner Losh 152e7ff5475SWarner Losh /* 153e7ff5475SWarner Losh * Receive a file via the TFTP data session. 154e7ff5475SWarner Losh * 155e7ff5475SWarner Losh * - It could be that the first block has already arrived while 156e7ff5475SWarner Losh * trying to figure out if we were receiving options or not. In 157e7ff5475SWarner Losh * that case it is passed to this function. 158e7ff5475SWarner Losh */ 159e7ff5475SWarner Losh void 160e7ff5475SWarner Losh tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts, 161e7ff5475SWarner Losh struct tftphdr *firstblock, size_t fb_size) 162e7ff5475SWarner Losh { 163e7ff5475SWarner Losh struct tftphdr *rp; 164e7ff5475SWarner Losh uint16_t oldblock; 165e7ff5475SWarner Losh int n_data, n_ack, writesize, i, retry; 166e7ff5475SWarner Losh char recvbuffer[MAXPKTSIZE]; 167e7ff5475SWarner Losh 168e7ff5475SWarner Losh ts->amount = 0; 169e7ff5475SWarner Losh 170e7ff5475SWarner Losh if (firstblock != NULL) { 171e7ff5475SWarner Losh writesize = write_file(firstblock->th_data, fb_size); 172e7ff5475SWarner Losh ts->amount += writesize; 173e7ff5475SWarner Losh for (i = 0; ; i++) { 174e7ff5475SWarner Losh n_ack = send_ack(peer, *block); 175e7ff5475SWarner Losh if (n_ack > 0) { 176e7ff5475SWarner Losh if (i == maxtimeouts) { 177e7ff5475SWarner Losh tftp_log(LOG_ERR, 178e7ff5475SWarner Losh "Cannot send ACK packet #%d, " 179e7ff5475SWarner Losh "giving up", *block); 180e7ff5475SWarner Losh return; 181e7ff5475SWarner Losh } 182e7ff5475SWarner Losh tftp_log(LOG_ERR, 183e7ff5475SWarner Losh "Cannot send ACK packet #%d, trying again", 184e7ff5475SWarner Losh *block); 185e7ff5475SWarner Losh continue; 186e7ff5475SWarner Losh } 187e7ff5475SWarner Losh 188e7ff5475SWarner Losh break; 189e7ff5475SWarner Losh } 190e7ff5475SWarner Losh 191e7ff5475SWarner Losh if (fb_size != segsize) { 192e7ff5475SWarner Losh gettimeofday(&(ts->tstop), NULL); 193e7ff5475SWarner Losh return; 194e7ff5475SWarner Losh } 195e7ff5475SWarner Losh } 196e7ff5475SWarner Losh 197e7ff5475SWarner Losh rp = (struct tftphdr *)recvbuffer; 198e7ff5475SWarner Losh do { 199e7ff5475SWarner Losh oldblock = *block; 200e7ff5475SWarner Losh (*block)++; 201e7ff5475SWarner Losh if (oldblock > *block) { 202e7ff5475SWarner Losh if (options[OPT_ROLLOVER].o_request == NULL) { 20338bd7db3SCraig Rodrigues /* 20438bd7db3SCraig Rodrigues * "rollover" option not specified in 20538bd7db3SCraig Rodrigues * tftp client. Default to rolling block 20638bd7db3SCraig Rodrigues * counter to 0. 20738bd7db3SCraig Rodrigues */ 20838bd7db3SCraig Rodrigues *block = 0; 20938bd7db3SCraig Rodrigues } else { 21038bd7db3SCraig Rodrigues *block = atoi(options[OPT_ROLLOVER].o_request); 211e7ff5475SWarner Losh } 212e7ff5475SWarner Losh 213e7ff5475SWarner Losh ts->rollovers++; 214e7ff5475SWarner Losh } 215e7ff5475SWarner Losh 216e7ff5475SWarner Losh for (retry = 0; ; retry++) { 217e7ff5475SWarner Losh if (debug&DEBUG_SIMPLE) 218e7ff5475SWarner Losh tftp_log(LOG_DEBUG, 219e7ff5475SWarner Losh "Receiving DATA block %d", *block); 220e7ff5475SWarner Losh 221e7ff5475SWarner Losh n_data = receive_packet(peer, recvbuffer, 222e7ff5475SWarner Losh MAXPKTSIZE, NULL, timeoutpacket); 223e7ff5475SWarner Losh if (n_data < 0) { 224e7ff5475SWarner Losh if (retry == maxtimeouts) { 225e7ff5475SWarner Losh tftp_log(LOG_ERR, 226e7ff5475SWarner Losh "Timeout #%d on DATA block %d, " 227e7ff5475SWarner Losh "giving up", retry, *block); 228e7ff5475SWarner Losh return; 229e7ff5475SWarner Losh } 230e7ff5475SWarner Losh if (n_data == RP_TIMEOUT) { 231e7ff5475SWarner Losh tftp_log(LOG_WARNING, 232e7ff5475SWarner Losh "Timeout #%d on DATA block %d", 233e7ff5475SWarner Losh retry, *block); 234e7ff5475SWarner Losh send_ack(peer, oldblock); 235e7ff5475SWarner Losh continue; 236e7ff5475SWarner Losh } 237e7ff5475SWarner Losh 238e7ff5475SWarner Losh /* Either read failure or ERROR packet */ 239e7ff5475SWarner Losh if (debug&DEBUG_SIMPLE) 240e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Aborting: %s", 241e7ff5475SWarner Losh rp_strerror(n_data)); 242e7ff5475SWarner Losh goto abort; 243e7ff5475SWarner Losh } 244e7ff5475SWarner Losh if (rp->th_opcode == DATA) { 245e7ff5475SWarner Losh ts->blocks++; 246e7ff5475SWarner Losh 247e7ff5475SWarner Losh if (rp->th_block == *block) 248e7ff5475SWarner Losh break; 249e7ff5475SWarner Losh 250e7ff5475SWarner Losh tftp_log(LOG_WARNING, 251e7ff5475SWarner Losh "Expected DATA block %d, got block %d", 252e7ff5475SWarner Losh *block, rp->th_block); 253e7ff5475SWarner Losh 254e7ff5475SWarner Losh /* Re-synchronize with the other side */ 255e7ff5475SWarner Losh (void) synchnet(peer); 256e7ff5475SWarner Losh if (rp->th_block == (*block-1)) { 257e7ff5475SWarner Losh tftp_log(LOG_INFO, "Trying to sync"); 258e7ff5475SWarner Losh *block = oldblock; 259e7ff5475SWarner Losh ts->retries++; 260e7ff5475SWarner Losh goto send_ack; /* rexmit */ 261e7ff5475SWarner Losh } 262e7ff5475SWarner Losh 263e7ff5475SWarner Losh } else { 264e7ff5475SWarner Losh tftp_log(LOG_WARNING, 265e7ff5475SWarner Losh "Expected DATA block, got %s block", 266e7ff5475SWarner Losh packettype(rp->th_opcode)); 267e7ff5475SWarner Losh } 268e7ff5475SWarner Losh } 269e7ff5475SWarner Losh 270e7ff5475SWarner Losh if (n_data > 0) { 271e7ff5475SWarner Losh writesize = write_file(rp->th_data, n_data); 272e7ff5475SWarner Losh ts->amount += writesize; 273e7ff5475SWarner Losh if (writesize <= 0) { 274e7ff5475SWarner Losh tftp_log(LOG_ERR, 275e7ff5475SWarner Losh "write_file returned %d", writesize); 276e7ff5475SWarner Losh if (writesize < 0) 277e7ff5475SWarner Losh send_error(peer, errno + 100); 278e7ff5475SWarner Losh else 279e7ff5475SWarner Losh send_error(peer, ENOSPACE); 280e7ff5475SWarner Losh goto abort; 281e7ff5475SWarner Losh } 282*7378015bSAlan Somers if (n_data != segsize) 283*7378015bSAlan Somers write_close(); 284e7ff5475SWarner Losh } 285e7ff5475SWarner Losh 286e7ff5475SWarner Losh send_ack: 287e7ff5475SWarner Losh for (i = 0; ; i++) { 288e7ff5475SWarner Losh n_ack = send_ack(peer, *block); 289e7ff5475SWarner Losh if (n_ack > 0) { 290e7ff5475SWarner Losh 291e7ff5475SWarner Losh if (i == maxtimeouts) { 292e7ff5475SWarner Losh tftp_log(LOG_ERR, 293e7ff5475SWarner Losh "Cannot send ACK packet #%d, " 294e7ff5475SWarner Losh "giving up", *block); 295e7ff5475SWarner Losh return; 296e7ff5475SWarner Losh } 297e7ff5475SWarner Losh 298e7ff5475SWarner Losh tftp_log(LOG_ERR, 299e7ff5475SWarner Losh "Cannot send ACK packet #%d, trying again", 300e7ff5475SWarner Losh *block); 301e7ff5475SWarner Losh continue; 302e7ff5475SWarner Losh } 303e7ff5475SWarner Losh 304e7ff5475SWarner Losh break; 305e7ff5475SWarner Losh } 306e7ff5475SWarner Losh gettimeofday(&(ts->tstop), NULL); 307e7ff5475SWarner Losh } while (n_data == segsize); 308e7ff5475SWarner Losh 309e7ff5475SWarner Losh /* Don't do late packet management for the client implementation */ 310e7ff5475SWarner Losh if (acting_as_client) 311e7ff5475SWarner Losh return; 312e7ff5475SWarner Losh 313e7ff5475SWarner Losh for (i = 0; ; i++) { 314e7ff5475SWarner Losh n_data = receive_packet(peer, (char *)rp, pktsize, 315e7ff5475SWarner Losh NULL, timeoutpacket); 316e7ff5475SWarner Losh if (n_data <= 0) 317e7ff5475SWarner Losh break; 318e7ff5475SWarner Losh if (n_data > 0 && 319e7ff5475SWarner Losh rp->th_opcode == DATA && /* and got a data block */ 320e7ff5475SWarner Losh *block == rp->th_block) /* then my last ack was lost */ 321e7ff5475SWarner Losh send_ack(peer, *block); /* resend final ack */ 322e7ff5475SWarner Losh } 323e7ff5475SWarner Losh 324e7ff5475SWarner Losh abort: 325e7ff5475SWarner Losh return; 326e7ff5475SWarner Losh } 327