1207614Simp/* 2207614Simp * Copyright (C) 2008 Edwin Groothuis. All rights reserved. 3207614Simp * 4207614Simp * Redistribution and use in source and binary forms, with or without 5207614Simp * modification, are permitted provided that the following conditions 6207614Simp * are met: 7207614Simp * 1. Redistributions of source code must retain the above copyright 8207614Simp * notice, this list of conditions and the following disclaimer. 9207614Simp * 2. Redistributions in binary form must reproduce the above copyright 10207614Simp * notice, this list of conditions and the following disclaimer in the 11207614Simp * documentation and/or other materials provided with the distribution. 12207614Simp * 13207614Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14207614Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15207614Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16207614Simp * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17207614Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18207614Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19207614Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20207614Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21207614Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22207614Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23207614Simp * SUCH DAMAGE. 24207614Simp */ 25207614Simp 26207614Simp#include <sys/cdefs.h> 27207614Simp__FBSDID("$FreeBSD$"); 28207614Simp 29207614Simp#include <sys/types.h> 30207614Simp#include <sys/param.h> 31207614Simp#include <sys/ioctl.h> 32207614Simp#include <sys/stat.h> 33207614Simp#include <sys/socket.h> 34207614Simp 35207614Simp#include <netinet/in.h> 36207614Simp#include <arpa/tftp.h> 37207614Simp 38207614Simp#include <errno.h> 39207614Simp#include <stdio.h> 40207614Simp#include <stdlib.h> 41207614Simp#include <syslog.h> 42207614Simp 43207614Simp#include "tftp-file.h" 44207614Simp#include "tftp-io.h" 45207614Simp#include "tftp-utils.h" 46207614Simp#include "tftp-options.h" 47207614Simp#include "tftp-transfer.h" 48207614Simp 49207614Simp/* 50207614Simp * Send a file via the TFTP data session. 51207614Simp */ 52207614Simpvoid 53207614Simptftp_send(int peer, uint16_t *block, struct tftp_stats *ts) 54207614Simp{ 55207614Simp struct tftphdr *rp; 56207614Simp int size, n_data, n_ack, try; 57207614Simp uint16_t oldblock; 58207614Simp char sendbuffer[MAXPKTSIZE]; 59207614Simp char recvbuffer[MAXPKTSIZE]; 60207614Simp 61207614Simp rp = (struct tftphdr *)recvbuffer; 62207614Simp *block = 1; 63207614Simp ts->amount = 0; 64207614Simp do { 65207614Simp if (debug&DEBUG_SIMPLE) 66207614Simp tftp_log(LOG_DEBUG, "Sending block %d", *block); 67207614Simp 68207614Simp size = read_file(sendbuffer, segsize); 69207614Simp if (size < 0) { 70207614Simp tftp_log(LOG_ERR, "read_file returned %d", size); 71207614Simp send_error(peer, errno + 100); 72207614Simp goto abort; 73207614Simp } 74207614Simp 75207614Simp for (try = 0; ; try++) { 76207614Simp n_data = send_data(peer, *block, sendbuffer, size); 77207614Simp if (n_data > 0) { 78207614Simp if (try == maxtimeouts) { 79207614Simp tftp_log(LOG_ERR, 80207614Simp "Cannot send DATA packet #%d, " 81207614Simp "giving up", *block); 82207614Simp return; 83207614Simp } 84207614Simp tftp_log(LOG_ERR, 85207614Simp "Cannot send DATA packet #%d, trying again", 86207614Simp *block); 87207614Simp continue; 88207614Simp } 89207614Simp 90207614Simp n_ack = receive_packet(peer, recvbuffer, 91207614Simp MAXPKTSIZE, NULL, timeoutpacket); 92207614Simp if (n_ack < 0) { 93207614Simp if (n_ack == RP_TIMEOUT) { 94207614Simp if (try == maxtimeouts) { 95207614Simp tftp_log(LOG_ERR, 96207614Simp "Timeout #%d send ACK %d " 97207614Simp "giving up", try, *block); 98207614Simp return; 99207614Simp } 100207614Simp tftp_log(LOG_WARNING, 101207614Simp "Timeout #%d on ACK %d", 102207614Simp try, *block); 103207614Simp continue; 104207614Simp } 105207614Simp 106207614Simp /* Either read failure or ERROR packet */ 107207614Simp if (debug&DEBUG_SIMPLE) 108207614Simp tftp_log(LOG_ERR, "Aborting: %s", 109207614Simp rp_strerror(n_ack)); 110207614Simp goto abort; 111207614Simp } 112207614Simp if (rp->th_opcode == ACK) { 113207614Simp ts->blocks++; 114207614Simp if (rp->th_block == *block) { 115207614Simp ts->amount += size; 116207614Simp break; 117207614Simp } 118207614Simp 119207614Simp /* Re-synchronize with the other side */ 120207614Simp (void) synchnet(peer); 121207614Simp if (rp->th_block == (*block - 1)) { 122207614Simp ts->retries++; 123207614Simp continue; 124207614Simp } 125207614Simp } 126207614Simp 127207614Simp } 128207614Simp oldblock = *block; 129207614Simp (*block)++; 130207614Simp if (oldblock > *block) { 131207614Simp if (options[OPT_ROLLOVER].o_request == NULL) { 132224536Srodrigc /* 133224536Srodrigc * "rollover" option not specified in 134224536Srodrigc * tftp client. Default to rolling block 135224536Srodrigc * counter to 0. 136224536Srodrigc */ 137224536Srodrigc *block = 0; 138224536Srodrigc } else { 139224536Srodrigc *block = atoi(options[OPT_ROLLOVER].o_request); 140207614Simp } 141207614Simp 142207614Simp ts->rollovers++; 143207614Simp } 144207614Simp gettimeofday(&(ts->tstop), NULL); 145207614Simp } while (size == segsize); 146207614Simpabort: 147207614Simp return; 148207614Simp} 149207614Simp 150207614Simp/* 151207614Simp * Receive a file via the TFTP data session. 152207614Simp * 153207614Simp * - It could be that the first block has already arrived while 154207614Simp * trying to figure out if we were receiving options or not. In 155207614Simp * that case it is passed to this function. 156207614Simp */ 157207614Simpvoid 158207614Simptftp_receive(int peer, uint16_t *block, struct tftp_stats *ts, 159207614Simp struct tftphdr *firstblock, size_t fb_size) 160207614Simp{ 161207614Simp struct tftphdr *rp; 162207614Simp uint16_t oldblock; 163207614Simp int n_data, n_ack, writesize, i, retry; 164207614Simp char recvbuffer[MAXPKTSIZE]; 165207614Simp 166207614Simp ts->amount = 0; 167207614Simp 168207614Simp if (firstblock != NULL) { 169207614Simp writesize = write_file(firstblock->th_data, fb_size); 170207614Simp ts->amount += writesize; 171207614Simp for (i = 0; ; i++) { 172207614Simp n_ack = send_ack(peer, *block); 173207614Simp if (n_ack > 0) { 174207614Simp if (i == maxtimeouts) { 175207614Simp tftp_log(LOG_ERR, 176207614Simp "Cannot send ACK packet #%d, " 177207614Simp "giving up", *block); 178207614Simp return; 179207614Simp } 180207614Simp tftp_log(LOG_ERR, 181207614Simp "Cannot send ACK packet #%d, trying again", 182207614Simp *block); 183207614Simp continue; 184207614Simp } 185207614Simp 186207614Simp break; 187207614Simp } 188207614Simp 189207614Simp if (fb_size != segsize) { 190207614Simp gettimeofday(&(ts->tstop), NULL); 191207614Simp return; 192207614Simp } 193207614Simp } 194207614Simp 195207614Simp rp = (struct tftphdr *)recvbuffer; 196207614Simp do { 197207614Simp oldblock = *block; 198207614Simp (*block)++; 199207614Simp if (oldblock > *block) { 200207614Simp if (options[OPT_ROLLOVER].o_request == NULL) { 201224536Srodrigc /* 202224536Srodrigc * "rollover" option not specified in 203224536Srodrigc * tftp client. Default to rolling block 204224536Srodrigc * counter to 0. 205224536Srodrigc */ 206224536Srodrigc *block = 0; 207224536Srodrigc } else { 208224536Srodrigc *block = atoi(options[OPT_ROLLOVER].o_request); 209207614Simp } 210207614Simp 211207614Simp ts->rollovers++; 212207614Simp } 213207614Simp 214207614Simp for (retry = 0; ; retry++) { 215207614Simp if (debug&DEBUG_SIMPLE) 216207614Simp tftp_log(LOG_DEBUG, 217207614Simp "Receiving DATA block %d", *block); 218207614Simp 219207614Simp n_data = receive_packet(peer, recvbuffer, 220207614Simp MAXPKTSIZE, NULL, timeoutpacket); 221207614Simp if (n_data < 0) { 222207614Simp if (retry == maxtimeouts) { 223207614Simp tftp_log(LOG_ERR, 224207614Simp "Timeout #%d on DATA block %d, " 225207614Simp "giving up", retry, *block); 226207614Simp return; 227207614Simp } 228207614Simp if (n_data == RP_TIMEOUT) { 229207614Simp tftp_log(LOG_WARNING, 230207614Simp "Timeout #%d on DATA block %d", 231207614Simp retry, *block); 232207614Simp send_ack(peer, oldblock); 233207614Simp continue; 234207614Simp } 235207614Simp 236207614Simp /* Either read failure or ERROR packet */ 237207614Simp if (debug&DEBUG_SIMPLE) 238207614Simp tftp_log(LOG_DEBUG, "Aborting: %s", 239207614Simp rp_strerror(n_data)); 240207614Simp goto abort; 241207614Simp } 242207614Simp if (rp->th_opcode == DATA) { 243207614Simp ts->blocks++; 244207614Simp 245207614Simp if (rp->th_block == *block) 246207614Simp break; 247207614Simp 248207614Simp tftp_log(LOG_WARNING, 249207614Simp "Expected DATA block %d, got block %d", 250207614Simp *block, rp->th_block); 251207614Simp 252207614Simp /* Re-synchronize with the other side */ 253207614Simp (void) synchnet(peer); 254207614Simp if (rp->th_block == (*block-1)) { 255207614Simp tftp_log(LOG_INFO, "Trying to sync"); 256207614Simp *block = oldblock; 257207614Simp ts->retries++; 258207614Simp goto send_ack; /* rexmit */ 259207614Simp } 260207614Simp 261207614Simp } else { 262207614Simp tftp_log(LOG_WARNING, 263207614Simp "Expected DATA block, got %s block", 264207614Simp packettype(rp->th_opcode)); 265207614Simp } 266207614Simp } 267207614Simp 268207614Simp if (n_data > 0) { 269207614Simp writesize = write_file(rp->th_data, n_data); 270207614Simp ts->amount += writesize; 271207614Simp if (writesize <= 0) { 272207614Simp tftp_log(LOG_ERR, 273207614Simp "write_file returned %d", writesize); 274207614Simp if (writesize < 0) 275207614Simp send_error(peer, errno + 100); 276207614Simp else 277207614Simp send_error(peer, ENOSPACE); 278207614Simp goto abort; 279207614Simp } 280207614Simp } 281207614Simp 282207614Simpsend_ack: 283207614Simp for (i = 0; ; i++) { 284207614Simp n_ack = send_ack(peer, *block); 285207614Simp if (n_ack > 0) { 286207614Simp 287207614Simp if (i == maxtimeouts) { 288207614Simp tftp_log(LOG_ERR, 289207614Simp "Cannot send ACK packet #%d, " 290207614Simp "giving up", *block); 291207614Simp return; 292207614Simp } 293207614Simp 294207614Simp tftp_log(LOG_ERR, 295207614Simp "Cannot send ACK packet #%d, trying again", 296207614Simp *block); 297207614Simp continue; 298207614Simp } 299207614Simp 300207614Simp break; 301207614Simp } 302207614Simp gettimeofday(&(ts->tstop), NULL); 303207614Simp } while (n_data == segsize); 304207614Simp 305207614Simp /* Don't do late packet management for the client implementation */ 306207614Simp if (acting_as_client) 307207614Simp return; 308207614Simp 309207614Simp for (i = 0; ; i++) { 310207614Simp n_data = receive_packet(peer, (char *)rp, pktsize, 311207614Simp NULL, timeoutpacket); 312207614Simp if (n_data <= 0) 313207614Simp break; 314207614Simp if (n_data > 0 && 315207614Simp rp->th_opcode == DATA && /* and got a data block */ 316207614Simp *block == rp->th_block) /* then my last ack was lost */ 317207614Simp send_ack(peer, *block); /* resend final ack */ 318207614Simp } 319207614Simp 320207614Simpabort: 321207614Simp return; 322207614Simp} 323