1*e7ff5475SWarner Losh /* 2*e7ff5475SWarner Losh * Copyright (C) 2008 Edwin Groothuis. All rights reserved. 3*e7ff5475SWarner Losh * 4*e7ff5475SWarner Losh * Redistribution and use in source and binary forms, with or without 5*e7ff5475SWarner Losh * modification, are permitted provided that the following conditions 6*e7ff5475SWarner Losh * are met: 7*e7ff5475SWarner Losh * 1. Redistributions of source code must retain the above copyright 8*e7ff5475SWarner Losh * notice, this list of conditions and the following disclaimer. 9*e7ff5475SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 10*e7ff5475SWarner Losh * notice, this list of conditions and the following disclaimer in the 11*e7ff5475SWarner Losh * documentation and/or other materials provided with the distribution. 12*e7ff5475SWarner Losh * 13*e7ff5475SWarner Losh * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14*e7ff5475SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15*e7ff5475SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16*e7ff5475SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17*e7ff5475SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18*e7ff5475SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19*e7ff5475SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20*e7ff5475SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21*e7ff5475SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22*e7ff5475SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23*e7ff5475SWarner Losh * SUCH DAMAGE. 24*e7ff5475SWarner Losh */ 25*e7ff5475SWarner Losh 26*e7ff5475SWarner Losh #include <sys/cdefs.h> 27*e7ff5475SWarner Losh __FBSDID("$FreeBSD$"); 28*e7ff5475SWarner Losh 29*e7ff5475SWarner Losh #include <sys/types.h> 30*e7ff5475SWarner Losh #include <sys/param.h> 31*e7ff5475SWarner Losh #include <sys/ioctl.h> 32*e7ff5475SWarner Losh #include <sys/stat.h> 33*e7ff5475SWarner Losh #include <sys/socket.h> 34*e7ff5475SWarner Losh 35*e7ff5475SWarner Losh #include <netinet/in.h> 36*e7ff5475SWarner Losh #include <arpa/tftp.h> 37*e7ff5475SWarner Losh 38*e7ff5475SWarner Losh #include <errno.h> 39*e7ff5475SWarner Losh #include <stdio.h> 40*e7ff5475SWarner Losh #include <stdlib.h> 41*e7ff5475SWarner Losh #include <syslog.h> 42*e7ff5475SWarner Losh 43*e7ff5475SWarner Losh #include "tftp-file.h" 44*e7ff5475SWarner Losh #include "tftp-io.h" 45*e7ff5475SWarner Losh #include "tftp-utils.h" 46*e7ff5475SWarner Losh #include "tftp-options.h" 47*e7ff5475SWarner Losh #include "tftp-transfer.h" 48*e7ff5475SWarner Losh 49*e7ff5475SWarner Losh /* 50*e7ff5475SWarner Losh * Send a file via the TFTP data session. 51*e7ff5475SWarner Losh */ 52*e7ff5475SWarner Losh void 53*e7ff5475SWarner Losh tftp_send(int peer, uint16_t *block, struct tftp_stats *ts) 54*e7ff5475SWarner Losh { 55*e7ff5475SWarner Losh struct tftphdr *rp; 56*e7ff5475SWarner Losh int size, n_data, n_ack, try; 57*e7ff5475SWarner Losh uint16_t oldblock; 58*e7ff5475SWarner Losh char sendbuffer[MAXPKTSIZE]; 59*e7ff5475SWarner Losh char recvbuffer[MAXPKTSIZE]; 60*e7ff5475SWarner Losh 61*e7ff5475SWarner Losh rp = (struct tftphdr *)recvbuffer; 62*e7ff5475SWarner Losh *block = 1; 63*e7ff5475SWarner Losh ts->amount = 0; 64*e7ff5475SWarner Losh do { 65*e7ff5475SWarner Losh if (debug&DEBUG_SIMPLE) 66*e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Sending block %d", *block); 67*e7ff5475SWarner Losh 68*e7ff5475SWarner Losh size = read_file(sendbuffer, segsize); 69*e7ff5475SWarner Losh if (size < 0) { 70*e7ff5475SWarner Losh tftp_log(LOG_ERR, "read_file returned %d", size); 71*e7ff5475SWarner Losh send_error(peer, errno + 100); 72*e7ff5475SWarner Losh goto abort; 73*e7ff5475SWarner Losh } 74*e7ff5475SWarner Losh 75*e7ff5475SWarner Losh for (try = 0; ; try++) { 76*e7ff5475SWarner Losh n_data = send_data(peer, *block, sendbuffer, size); 77*e7ff5475SWarner Losh if (n_data > 0) { 78*e7ff5475SWarner Losh if (try == maxtimeouts) { 79*e7ff5475SWarner Losh tftp_log(LOG_ERR, 80*e7ff5475SWarner Losh "Cannot send DATA packet #%d, " 81*e7ff5475SWarner Losh "giving up", *block); 82*e7ff5475SWarner Losh return; 83*e7ff5475SWarner Losh } 84*e7ff5475SWarner Losh tftp_log(LOG_ERR, 85*e7ff5475SWarner Losh "Cannot send DATA packet #%d, trying again", 86*e7ff5475SWarner Losh *block); 87*e7ff5475SWarner Losh continue; 88*e7ff5475SWarner Losh } 89*e7ff5475SWarner Losh 90*e7ff5475SWarner Losh n_ack = receive_packet(peer, recvbuffer, 91*e7ff5475SWarner Losh MAXPKTSIZE, NULL, timeoutpacket); 92*e7ff5475SWarner Losh if (n_ack < 0) { 93*e7ff5475SWarner Losh if (n_ack == RP_TIMEOUT) { 94*e7ff5475SWarner Losh if (try == maxtimeouts) { 95*e7ff5475SWarner Losh tftp_log(LOG_ERR, 96*e7ff5475SWarner Losh "Timeout #%d send ACK %d " 97*e7ff5475SWarner Losh "giving up", try, *block); 98*e7ff5475SWarner Losh return; 99*e7ff5475SWarner Losh } 100*e7ff5475SWarner Losh tftp_log(LOG_WARNING, 101*e7ff5475SWarner Losh "Timeout #%d on ACK %d", 102*e7ff5475SWarner Losh try, *block); 103*e7ff5475SWarner Losh continue; 104*e7ff5475SWarner Losh } 105*e7ff5475SWarner Losh 106*e7ff5475SWarner Losh /* Either read failure or ERROR packet */ 107*e7ff5475SWarner Losh if (debug&DEBUG_SIMPLE) 108*e7ff5475SWarner Losh tftp_log(LOG_ERR, "Aborting: %s", 109*e7ff5475SWarner Losh rp_strerror(n_ack)); 110*e7ff5475SWarner Losh goto abort; 111*e7ff5475SWarner Losh } 112*e7ff5475SWarner Losh if (rp->th_opcode == ACK) { 113*e7ff5475SWarner Losh ts->blocks++; 114*e7ff5475SWarner Losh if (rp->th_block == *block) { 115*e7ff5475SWarner Losh ts->amount += size; 116*e7ff5475SWarner Losh break; 117*e7ff5475SWarner Losh } 118*e7ff5475SWarner Losh 119*e7ff5475SWarner Losh /* Re-synchronize with the other side */ 120*e7ff5475SWarner Losh (void) synchnet(peer); 121*e7ff5475SWarner Losh if (rp->th_block == (*block - 1)) { 122*e7ff5475SWarner Losh ts->retries++; 123*e7ff5475SWarner Losh continue; 124*e7ff5475SWarner Losh } 125*e7ff5475SWarner Losh } 126*e7ff5475SWarner Losh 127*e7ff5475SWarner Losh } 128*e7ff5475SWarner Losh oldblock = *block; 129*e7ff5475SWarner Losh (*block)++; 130*e7ff5475SWarner Losh if (oldblock > *block) { 131*e7ff5475SWarner Losh if (options[OPT_ROLLOVER].o_request == NULL) { 132*e7ff5475SWarner Losh tftp_log(LOG_ERR, 133*e7ff5475SWarner Losh "Block rollover but not allowed."); 134*e7ff5475SWarner Losh send_error(peer, EBADOP); 135*e7ff5475SWarner Losh gettimeofday(&(ts->tstop), NULL); 136*e7ff5475SWarner Losh return; 137*e7ff5475SWarner Losh } 138*e7ff5475SWarner Losh 139*e7ff5475SWarner Losh *block = atoi(options[OPT_ROLLOVER].o_request); 140*e7ff5475SWarner Losh ts->rollovers++; 141*e7ff5475SWarner Losh } 142*e7ff5475SWarner Losh gettimeofday(&(ts->tstop), NULL); 143*e7ff5475SWarner Losh } while (size == segsize); 144*e7ff5475SWarner Losh abort: 145*e7ff5475SWarner Losh return; 146*e7ff5475SWarner Losh } 147*e7ff5475SWarner Losh 148*e7ff5475SWarner Losh /* 149*e7ff5475SWarner Losh * Receive a file via the TFTP data session. 150*e7ff5475SWarner Losh * 151*e7ff5475SWarner Losh * - It could be that the first block has already arrived while 152*e7ff5475SWarner Losh * trying to figure out if we were receiving options or not. In 153*e7ff5475SWarner Losh * that case it is passed to this function. 154*e7ff5475SWarner Losh */ 155*e7ff5475SWarner Losh void 156*e7ff5475SWarner Losh tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts, 157*e7ff5475SWarner Losh struct tftphdr *firstblock, size_t fb_size) 158*e7ff5475SWarner Losh { 159*e7ff5475SWarner Losh struct tftphdr *rp; 160*e7ff5475SWarner Losh uint16_t oldblock; 161*e7ff5475SWarner Losh int n_data, n_ack, writesize, i, retry; 162*e7ff5475SWarner Losh char recvbuffer[MAXPKTSIZE]; 163*e7ff5475SWarner Losh 164*e7ff5475SWarner Losh ts->amount = 0; 165*e7ff5475SWarner Losh 166*e7ff5475SWarner Losh if (firstblock != NULL) { 167*e7ff5475SWarner Losh writesize = write_file(firstblock->th_data, fb_size); 168*e7ff5475SWarner Losh ts->amount += writesize; 169*e7ff5475SWarner Losh for (i = 0; ; i++) { 170*e7ff5475SWarner Losh n_ack = send_ack(peer, *block); 171*e7ff5475SWarner Losh if (n_ack > 0) { 172*e7ff5475SWarner Losh if (i == maxtimeouts) { 173*e7ff5475SWarner Losh tftp_log(LOG_ERR, 174*e7ff5475SWarner Losh "Cannot send ACK packet #%d, " 175*e7ff5475SWarner Losh "giving up", *block); 176*e7ff5475SWarner Losh return; 177*e7ff5475SWarner Losh } 178*e7ff5475SWarner Losh tftp_log(LOG_ERR, 179*e7ff5475SWarner Losh "Cannot send ACK packet #%d, trying again", 180*e7ff5475SWarner Losh *block); 181*e7ff5475SWarner Losh continue; 182*e7ff5475SWarner Losh } 183*e7ff5475SWarner Losh 184*e7ff5475SWarner Losh break; 185*e7ff5475SWarner Losh } 186*e7ff5475SWarner Losh 187*e7ff5475SWarner Losh if (fb_size != segsize) { 188*e7ff5475SWarner Losh gettimeofday(&(ts->tstop), NULL); 189*e7ff5475SWarner Losh return; 190*e7ff5475SWarner Losh } 191*e7ff5475SWarner Losh } 192*e7ff5475SWarner Losh 193*e7ff5475SWarner Losh rp = (struct tftphdr *)recvbuffer; 194*e7ff5475SWarner Losh do { 195*e7ff5475SWarner Losh oldblock = *block; 196*e7ff5475SWarner Losh (*block)++; 197*e7ff5475SWarner Losh if (oldblock > *block) { 198*e7ff5475SWarner Losh if (options[OPT_ROLLOVER].o_request == NULL) { 199*e7ff5475SWarner Losh tftp_log(LOG_ERR, 200*e7ff5475SWarner Losh "Block rollover but not allowed."); 201*e7ff5475SWarner Losh send_error(peer, EBADOP); 202*e7ff5475SWarner Losh gettimeofday(&(ts->tstop), NULL); 203*e7ff5475SWarner Losh return; 204*e7ff5475SWarner Losh } 205*e7ff5475SWarner Losh 206*e7ff5475SWarner Losh *block = atoi(options[OPT_ROLLOVER].o_request); 207*e7ff5475SWarner Losh ts->rollovers++; 208*e7ff5475SWarner Losh } 209*e7ff5475SWarner Losh 210*e7ff5475SWarner Losh for (retry = 0; ; retry++) { 211*e7ff5475SWarner Losh if (debug&DEBUG_SIMPLE) 212*e7ff5475SWarner Losh tftp_log(LOG_DEBUG, 213*e7ff5475SWarner Losh "Receiving DATA block %d", *block); 214*e7ff5475SWarner Losh 215*e7ff5475SWarner Losh n_data = receive_packet(peer, recvbuffer, 216*e7ff5475SWarner Losh MAXPKTSIZE, NULL, timeoutpacket); 217*e7ff5475SWarner Losh if (n_data < 0) { 218*e7ff5475SWarner Losh if (retry == maxtimeouts) { 219*e7ff5475SWarner Losh tftp_log(LOG_ERR, 220*e7ff5475SWarner Losh "Timeout #%d on DATA block %d, " 221*e7ff5475SWarner Losh "giving up", retry, *block); 222*e7ff5475SWarner Losh return; 223*e7ff5475SWarner Losh } 224*e7ff5475SWarner Losh if (n_data == RP_TIMEOUT) { 225*e7ff5475SWarner Losh tftp_log(LOG_WARNING, 226*e7ff5475SWarner Losh "Timeout #%d on DATA block %d", 227*e7ff5475SWarner Losh retry, *block); 228*e7ff5475SWarner Losh send_ack(peer, oldblock); 229*e7ff5475SWarner Losh continue; 230*e7ff5475SWarner Losh } 231*e7ff5475SWarner Losh 232*e7ff5475SWarner Losh /* Either read failure or ERROR packet */ 233*e7ff5475SWarner Losh if (debug&DEBUG_SIMPLE) 234*e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Aborting: %s", 235*e7ff5475SWarner Losh rp_strerror(n_data)); 236*e7ff5475SWarner Losh goto abort; 237*e7ff5475SWarner Losh } 238*e7ff5475SWarner Losh if (rp->th_opcode == DATA) { 239*e7ff5475SWarner Losh ts->blocks++; 240*e7ff5475SWarner Losh 241*e7ff5475SWarner Losh if (rp->th_block == *block) 242*e7ff5475SWarner Losh break; 243*e7ff5475SWarner Losh 244*e7ff5475SWarner Losh tftp_log(LOG_WARNING, 245*e7ff5475SWarner Losh "Expected DATA block %d, got block %d", 246*e7ff5475SWarner Losh *block, rp->th_block); 247*e7ff5475SWarner Losh 248*e7ff5475SWarner Losh /* Re-synchronize with the other side */ 249*e7ff5475SWarner Losh (void) synchnet(peer); 250*e7ff5475SWarner Losh if (rp->th_block == (*block-1)) { 251*e7ff5475SWarner Losh tftp_log(LOG_INFO, "Trying to sync"); 252*e7ff5475SWarner Losh *block = oldblock; 253*e7ff5475SWarner Losh ts->retries++; 254*e7ff5475SWarner Losh goto send_ack; /* rexmit */ 255*e7ff5475SWarner Losh } 256*e7ff5475SWarner Losh 257*e7ff5475SWarner Losh } else { 258*e7ff5475SWarner Losh tftp_log(LOG_WARNING, 259*e7ff5475SWarner Losh "Expected DATA block, got %s block", 260*e7ff5475SWarner Losh packettype(rp->th_opcode)); 261*e7ff5475SWarner Losh } 262*e7ff5475SWarner Losh } 263*e7ff5475SWarner Losh 264*e7ff5475SWarner Losh if (n_data > 0) { 265*e7ff5475SWarner Losh writesize = write_file(rp->th_data, n_data); 266*e7ff5475SWarner Losh ts->amount += writesize; 267*e7ff5475SWarner Losh if (writesize <= 0) { 268*e7ff5475SWarner Losh tftp_log(LOG_ERR, 269*e7ff5475SWarner Losh "write_file returned %d", writesize); 270*e7ff5475SWarner Losh if (writesize < 0) 271*e7ff5475SWarner Losh send_error(peer, errno + 100); 272*e7ff5475SWarner Losh else 273*e7ff5475SWarner Losh send_error(peer, ENOSPACE); 274*e7ff5475SWarner Losh goto abort; 275*e7ff5475SWarner Losh } 276*e7ff5475SWarner Losh } 277*e7ff5475SWarner Losh 278*e7ff5475SWarner Losh send_ack: 279*e7ff5475SWarner Losh for (i = 0; ; i++) { 280*e7ff5475SWarner Losh n_ack = send_ack(peer, *block); 281*e7ff5475SWarner Losh if (n_ack > 0) { 282*e7ff5475SWarner Losh 283*e7ff5475SWarner Losh if (i == maxtimeouts) { 284*e7ff5475SWarner Losh tftp_log(LOG_ERR, 285*e7ff5475SWarner Losh "Cannot send ACK packet #%d, " 286*e7ff5475SWarner Losh "giving up", *block); 287*e7ff5475SWarner Losh return; 288*e7ff5475SWarner Losh } 289*e7ff5475SWarner Losh 290*e7ff5475SWarner Losh tftp_log(LOG_ERR, 291*e7ff5475SWarner Losh "Cannot send ACK packet #%d, trying again", 292*e7ff5475SWarner Losh *block); 293*e7ff5475SWarner Losh continue; 294*e7ff5475SWarner Losh } 295*e7ff5475SWarner Losh 296*e7ff5475SWarner Losh break; 297*e7ff5475SWarner Losh } 298*e7ff5475SWarner Losh gettimeofday(&(ts->tstop), NULL); 299*e7ff5475SWarner Losh } while (n_data == segsize); 300*e7ff5475SWarner Losh 301*e7ff5475SWarner Losh /* Don't do late packet management for the client implementation */ 302*e7ff5475SWarner Losh if (acting_as_client) 303*e7ff5475SWarner Losh return; 304*e7ff5475SWarner Losh 305*e7ff5475SWarner Losh for (i = 0; ; i++) { 306*e7ff5475SWarner Losh n_data = receive_packet(peer, (char *)rp, pktsize, 307*e7ff5475SWarner Losh NULL, timeoutpacket); 308*e7ff5475SWarner Losh if (n_data <= 0) 309*e7ff5475SWarner Losh break; 310*e7ff5475SWarner Losh if (n_data > 0 && 311*e7ff5475SWarner Losh rp->th_opcode == DATA && /* and got a data block */ 312*e7ff5475SWarner Losh *block == rp->th_block) /* then my last ack was lost */ 313*e7ff5475SWarner Losh send_ack(peer, *block); /* resend final ack */ 314*e7ff5475SWarner Losh } 315*e7ff5475SWarner Losh 316*e7ff5475SWarner Losh abort: 317*e7ff5475SWarner Losh return; 318*e7ff5475SWarner Losh } 319