tftp-transfer.c revision 207614
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: head/libexec/tftpd/tftp-transfer.c 207614 2010-05-04 13:07:40Z imp $"); 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) { 132207614Simp tftp_log(LOG_ERR, 133207614Simp "Block rollover but not allowed."); 134207614Simp send_error(peer, EBADOP); 135207614Simp gettimeofday(&(ts->tstop), NULL); 136207614Simp return; 137207614Simp } 138207614Simp 139207614Simp *block = atoi(options[OPT_ROLLOVER].o_request); 140207614Simp ts->rollovers++; 141207614Simp } 142207614Simp gettimeofday(&(ts->tstop), NULL); 143207614Simp } while (size == segsize); 144207614Simpabort: 145207614Simp return; 146207614Simp} 147207614Simp 148207614Simp/* 149207614Simp * Receive a file via the TFTP data session. 150207614Simp * 151207614Simp * - It could be that the first block has already arrived while 152207614Simp * trying to figure out if we were receiving options or not. In 153207614Simp * that case it is passed to this function. 154207614Simp */ 155207614Simpvoid 156207614Simptftp_receive(int peer, uint16_t *block, struct tftp_stats *ts, 157207614Simp struct tftphdr *firstblock, size_t fb_size) 158207614Simp{ 159207614Simp struct tftphdr *rp; 160207614Simp uint16_t oldblock; 161207614Simp int n_data, n_ack, writesize, i, retry; 162207614Simp char recvbuffer[MAXPKTSIZE]; 163207614Simp 164207614Simp ts->amount = 0; 165207614Simp 166207614Simp if (firstblock != NULL) { 167207614Simp writesize = write_file(firstblock->th_data, fb_size); 168207614Simp ts->amount += writesize; 169207614Simp for (i = 0; ; i++) { 170207614Simp n_ack = send_ack(peer, *block); 171207614Simp if (n_ack > 0) { 172207614Simp if (i == maxtimeouts) { 173207614Simp tftp_log(LOG_ERR, 174207614Simp "Cannot send ACK packet #%d, " 175207614Simp "giving up", *block); 176207614Simp return; 177207614Simp } 178207614Simp tftp_log(LOG_ERR, 179207614Simp "Cannot send ACK packet #%d, trying again", 180207614Simp *block); 181207614Simp continue; 182207614Simp } 183207614Simp 184207614Simp break; 185207614Simp } 186207614Simp 187207614Simp if (fb_size != segsize) { 188207614Simp gettimeofday(&(ts->tstop), NULL); 189207614Simp return; 190207614Simp } 191207614Simp } 192207614Simp 193207614Simp rp = (struct tftphdr *)recvbuffer; 194207614Simp do { 195207614Simp oldblock = *block; 196207614Simp (*block)++; 197207614Simp if (oldblock > *block) { 198207614Simp if (options[OPT_ROLLOVER].o_request == NULL) { 199207614Simp tftp_log(LOG_ERR, 200207614Simp "Block rollover but not allowed."); 201207614Simp send_error(peer, EBADOP); 202207614Simp gettimeofday(&(ts->tstop), NULL); 203207614Simp return; 204207614Simp } 205207614Simp 206207614Simp *block = atoi(options[OPT_ROLLOVER].o_request); 207207614Simp ts->rollovers++; 208207614Simp } 209207614Simp 210207614Simp for (retry = 0; ; retry++) { 211207614Simp if (debug&DEBUG_SIMPLE) 212207614Simp tftp_log(LOG_DEBUG, 213207614Simp "Receiving DATA block %d", *block); 214207614Simp 215207614Simp n_data = receive_packet(peer, recvbuffer, 216207614Simp MAXPKTSIZE, NULL, timeoutpacket); 217207614Simp if (n_data < 0) { 218207614Simp if (retry == maxtimeouts) { 219207614Simp tftp_log(LOG_ERR, 220207614Simp "Timeout #%d on DATA block %d, " 221207614Simp "giving up", retry, *block); 222207614Simp return; 223207614Simp } 224207614Simp if (n_data == RP_TIMEOUT) { 225207614Simp tftp_log(LOG_WARNING, 226207614Simp "Timeout #%d on DATA block %d", 227207614Simp retry, *block); 228207614Simp send_ack(peer, oldblock); 229207614Simp continue; 230207614Simp } 231207614Simp 232207614Simp /* Either read failure or ERROR packet */ 233207614Simp if (debug&DEBUG_SIMPLE) 234207614Simp tftp_log(LOG_DEBUG, "Aborting: %s", 235207614Simp rp_strerror(n_data)); 236207614Simp goto abort; 237207614Simp } 238207614Simp if (rp->th_opcode == DATA) { 239207614Simp ts->blocks++; 240207614Simp 241207614Simp if (rp->th_block == *block) 242207614Simp break; 243207614Simp 244207614Simp tftp_log(LOG_WARNING, 245207614Simp "Expected DATA block %d, got block %d", 246207614Simp *block, rp->th_block); 247207614Simp 248207614Simp /* Re-synchronize with the other side */ 249207614Simp (void) synchnet(peer); 250207614Simp if (rp->th_block == (*block-1)) { 251207614Simp tftp_log(LOG_INFO, "Trying to sync"); 252207614Simp *block = oldblock; 253207614Simp ts->retries++; 254207614Simp goto send_ack; /* rexmit */ 255207614Simp } 256207614Simp 257207614Simp } else { 258207614Simp tftp_log(LOG_WARNING, 259207614Simp "Expected DATA block, got %s block", 260207614Simp packettype(rp->th_opcode)); 261207614Simp } 262207614Simp } 263207614Simp 264207614Simp if (n_data > 0) { 265207614Simp writesize = write_file(rp->th_data, n_data); 266207614Simp ts->amount += writesize; 267207614Simp if (writesize <= 0) { 268207614Simp tftp_log(LOG_ERR, 269207614Simp "write_file returned %d", writesize); 270207614Simp if (writesize < 0) 271207614Simp send_error(peer, errno + 100); 272207614Simp else 273207614Simp send_error(peer, ENOSPACE); 274207614Simp goto abort; 275207614Simp } 276207614Simp } 277207614Simp 278207614Simpsend_ack: 279207614Simp for (i = 0; ; i++) { 280207614Simp n_ack = send_ack(peer, *block); 281207614Simp if (n_ack > 0) { 282207614Simp 283207614Simp if (i == maxtimeouts) { 284207614Simp tftp_log(LOG_ERR, 285207614Simp "Cannot send ACK packet #%d, " 286207614Simp "giving up", *block); 287207614Simp return; 288207614Simp } 289207614Simp 290207614Simp tftp_log(LOG_ERR, 291207614Simp "Cannot send ACK packet #%d, trying again", 292207614Simp *block); 293207614Simp continue; 294207614Simp } 295207614Simp 296207614Simp break; 297207614Simp } 298207614Simp gettimeofday(&(ts->tstop), NULL); 299207614Simp } while (n_data == segsize); 300207614Simp 301207614Simp /* Don't do late packet management for the client implementation */ 302207614Simp if (acting_as_client) 303207614Simp return; 304207614Simp 305207614Simp for (i = 0; ; i++) { 306207614Simp n_data = receive_packet(peer, (char *)rp, pktsize, 307207614Simp NULL, timeoutpacket); 308207614Simp if (n_data <= 0) 309207614Simp break; 310207614Simp if (n_data > 0 && 311207614Simp rp->th_opcode == DATA && /* and got a data block */ 312207614Simp *block == rp->th_block) /* then my last ack was lost */ 313207614Simp send_ack(peer, *block); /* resend final ack */ 314207614Simp } 315207614Simp 316207614Simpabort: 317207614Simp return; 318207614Simp} 319