18a16b7a1SPedro F. Giffuni /*- 28a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 38a16b7a1SPedro F. Giffuni * 49b50d902SRodney W. Grimes * Copyright (c) 1983, 1993 59b50d902SRodney W. Grimes * The Regents of the University of California. All rights reserved. 69b50d902SRodney W. Grimes * 79b50d902SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 89b50d902SRodney W. Grimes * modification, are permitted provided that the following conditions 99b50d902SRodney W. Grimes * are met: 109b50d902SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 119b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 129b50d902SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 139b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 149b50d902SRodney W. Grimes * documentation and/or other materials provided with the distribution. 15fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 169b50d902SRodney W. Grimes * may be used to endorse or promote products derived from this software 179b50d902SRodney W. Grimes * without specific prior written permission. 189b50d902SRodney W. Grimes * 199b50d902SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 209b50d902SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 219b50d902SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 229b50d902SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 239b50d902SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 249b50d902SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 259b50d902SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 269b50d902SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 279b50d902SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 289b50d902SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 299b50d902SRodney W. Grimes * SUCH DAMAGE. 309b50d902SRodney W. Grimes */ 319b50d902SRodney W. Grimes 326dfc2060SDavid Malone #if 0 339b50d902SRodney W. Grimes #ifndef lint 346dfc2060SDavid Malone static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93"; 356dfc2060SDavid Malone #endif /* not lint */ 36fd129a02SPhilippe Charnier #endif 379b50d902SRodney W. Grimes 386dfc2060SDavid Malone #include <sys/cdefs.h> 396dfc2060SDavid Malone __FBSDID("$FreeBSD$"); 406dfc2060SDavid Malone 419b50d902SRodney W. Grimes /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 429b50d902SRodney W. Grimes 439b50d902SRodney W. Grimes /* 449b50d902SRodney W. Grimes * TFTP User Program -- Protocol Machines 459b50d902SRodney W. Grimes */ 469b50d902SRodney W. Grimes #include <sys/socket.h> 47752fa694SWarner Losh #include <sys/stat.h> 489b50d902SRodney W. Grimes 499b50d902SRodney W. Grimes #include <netinet/in.h> 509b50d902SRodney W. Grimes 519b50d902SRodney W. Grimes #include <arpa/tftp.h> 529b50d902SRodney W. Grimes 53ca2d3691SAlan Somers #include <assert.h> 54fd129a02SPhilippe Charnier #include <err.h> 554dac6235SHajimu UMEMOTO #include <netdb.h> 56752fa694SWarner Losh #include <stdio.h> 57752fa694SWarner Losh #include <stdlib.h> 58752fa694SWarner Losh #include <string.h> 59752fa694SWarner Losh #include <syslog.h> 609b50d902SRodney W. Grimes 61752fa694SWarner Losh #include "tftp.h" 62752fa694SWarner Losh #include "tftp-file.h" 63752fa694SWarner Losh #include "tftp-utils.h" 64752fa694SWarner Losh #include "tftp-io.h" 65752fa694SWarner Losh #include "tftp-transfer.h" 66752fa694SWarner Losh #include "tftp-options.h" 679b50d902SRodney W. Grimes 689b50d902SRodney W. Grimes /* 699b50d902SRodney W. Grimes * Send the requested file. 709b50d902SRodney W. Grimes */ 71*92570f67SDag-Erling Smørgrav int 72752fa694SWarner Losh xmitfile(int peer, char *port, int fd, char *name, char *mode) 739b50d902SRodney W. Grimes { 74752fa694SWarner Losh struct tftphdr *rp; 75*92570f67SDag-Erling Smørgrav int n, i, ret = 0; 76752fa694SWarner Losh uint16_t block; 774dac6235SHajimu UMEMOTO struct sockaddr_storage serv; /* valid server port number */ 78752fa694SWarner Losh char recvbuffer[MAXPKTSIZE]; 79752fa694SWarner Losh struct tftp_stats tftp_stats; 809b50d902SRodney W. Grimes 81752fa694SWarner Losh stats_init(&tftp_stats); 82752fa694SWarner Losh 834dac6235SHajimu UMEMOTO memset(&serv, 0, sizeof(serv)); 84752fa694SWarner Losh rp = (struct tftphdr *)recvbuffer; 859b50d902SRodney W. Grimes 86752fa694SWarner Losh if (port == NULL) { 87752fa694SWarner Losh struct servent *se; 88752fa694SWarner Losh se = getservbyname("tftp", "udp"); 89ca2d3691SAlan Somers assert(se != NULL); 90752fa694SWarner Losh ((struct sockaddr_in *)&peer_sock)->sin_port = se->s_port; 91752fa694SWarner Losh } else 92752fa694SWarner Losh ((struct sockaddr_in *)&peer_sock)->sin_port = 93752fa694SWarner Losh htons(atoi(port)); 949b50d902SRodney W. Grimes 95752fa694SWarner Losh for (i = 0; i < 12; i++) { 96752fa694SWarner Losh struct sockaddr_storage from; 97752fa694SWarner Losh 98752fa694SWarner Losh /* Tell the other side what we want to do */ 99752fa694SWarner Losh if (debug & DEBUG_SIMPLE) 100752fa694SWarner Losh printf("Sending %s\n", name); 101752fa694SWarner Losh 102752fa694SWarner Losh n = send_wrq(peer, name, mode); 103752fa694SWarner Losh if (n > 0) { 104752fa694SWarner Losh printf("Cannot send WRQ packet\n"); 105*92570f67SDag-Erling Smørgrav return -1; 1069b50d902SRodney W. Grimes } 107752fa694SWarner Losh 108752fa694SWarner Losh /* 109752fa694SWarner Losh * The first packet we receive has the new destination port 110752fa694SWarner Losh * we have to send the next packets to. 1119b50d902SRodney W. Grimes */ 112752fa694SWarner Losh n = receive_packet(peer, recvbuffer, 113752fa694SWarner Losh MAXPKTSIZE, &from, timeoutpacket); 114752fa694SWarner Losh 115752fa694SWarner Losh /* We got some data! */ 116752fa694SWarner Losh if (n >= 0) { 117752fa694SWarner Losh ((struct sockaddr_in *)&peer_sock)->sin_port = 118752fa694SWarner Losh ((struct sockaddr_in *)&from)->sin_port; 119752fa694SWarner Losh break; 1209b50d902SRodney W. Grimes } 121752fa694SWarner Losh 122752fa694SWarner Losh /* This should be retried */ 123752fa694SWarner Losh if (n == RP_TIMEOUT) { 124752fa694SWarner Losh printf("Try %d, didn't receive answer from remote.\n", 125752fa694SWarner Losh i + 1); 126752fa694SWarner Losh continue; 1279b50d902SRodney W. Grimes } 128752fa694SWarner Losh 129752fa694SWarner Losh /* Everything else is fatal */ 130752fa694SWarner Losh break; 1319b50d902SRodney W. Grimes } 132752fa694SWarner Losh if (i == 12) { 133752fa694SWarner Losh printf("Transfer timed out.\n"); 134*92570f67SDag-Erling Smørgrav return -1; 1359b50d902SRodney W. Grimes } 136752fa694SWarner Losh if (rp->th_opcode == ERROR) { 137752fa694SWarner Losh printf("Got ERROR, aborted\n"); 138*92570f67SDag-Erling Smørgrav return -1; 139752fa694SWarner Losh } 140752fa694SWarner Losh 141752fa694SWarner Losh /* 142752fa694SWarner Losh * If the first packet is an OACK instead of an ACK packet, 143752fa694SWarner Losh * handle it different. 144752fa694SWarner Losh */ 145752fa694SWarner Losh if (rp->th_opcode == OACK) { 146752fa694SWarner Losh if (!options_rfc_enabled) { 147752fa694SWarner Losh printf("Got OACK while options are not enabled!\n"); 148752fa694SWarner Losh send_error(peer, EBADOP); 149*92570f67SDag-Erling Smørgrav return -1; 150752fa694SWarner Losh } 151752fa694SWarner Losh 152752fa694SWarner Losh parse_options(peer, rp->th_stuff, n + 2); 153752fa694SWarner Losh } 154752fa694SWarner Losh 155752fa694SWarner Losh if (read_init(fd, NULL, mode) < 0) { 156752fa694SWarner Losh warn("read_init()"); 157*92570f67SDag-Erling Smørgrav return -1; 158752fa694SWarner Losh } 159752fa694SWarner Losh 160752fa694SWarner Losh block = 1; 161*92570f67SDag-Erling Smørgrav if (tftp_send(peer, &block, &tftp_stats) != 0) 162*92570f67SDag-Erling Smørgrav ret = -1; 163752fa694SWarner Losh 164752fa694SWarner Losh read_close(); 165c54da672SGavin Atkinson if (tftp_stats.amount > 0) 166752fa694SWarner Losh printstats("Sent", verbose, &tftp_stats); 167*92570f67SDag-Erling Smørgrav return ret; 1689b50d902SRodney W. Grimes } 1699b50d902SRodney W. Grimes 1709b50d902SRodney W. Grimes /* 1719b50d902SRodney W. Grimes * Receive a file. 1729b50d902SRodney W. Grimes */ 173*92570f67SDag-Erling Smørgrav int 174752fa694SWarner Losh recvfile(int peer, char *port, int fd, char *name, char *mode) 1759b50d902SRodney W. Grimes { 176752fa694SWarner Losh struct tftphdr *rp; 177752fa694SWarner Losh uint16_t block; 178752fa694SWarner Losh char recvbuffer[MAXPKTSIZE]; 179*92570f67SDag-Erling Smørgrav int n, i, ret = 0; 180752fa694SWarner Losh struct tftp_stats tftp_stats; 181752fa694SWarner Losh 182752fa694SWarner Losh stats_init(&tftp_stats); 183752fa694SWarner Losh 184752fa694SWarner Losh rp = (struct tftphdr *)recvbuffer; 185752fa694SWarner Losh 186752fa694SWarner Losh if (port == NULL) { 187752fa694SWarner Losh struct servent *se; 188752fa694SWarner Losh se = getservbyname("tftp", "udp"); 189ca2d3691SAlan Somers assert(se != NULL); 190752fa694SWarner Losh ((struct sockaddr_in *)&peer_sock)->sin_port = se->s_port; 191752fa694SWarner Losh } else 192752fa694SWarner Losh ((struct sockaddr_in *)&peer_sock)->sin_port = 193752fa694SWarner Losh htons(atoi(port)); 194752fa694SWarner Losh 195752fa694SWarner Losh for (i = 0; i < 12; i++) { 1964dac6235SHajimu UMEMOTO struct sockaddr_storage from; 1979b50d902SRodney W. Grimes 198752fa694SWarner Losh /* Tell the other side what we want to do */ 199752fa694SWarner Losh if (debug & DEBUG_SIMPLE) 200752fa694SWarner Losh printf("Requesting %s\n", name); 2019b50d902SRodney W. Grimes 202752fa694SWarner Losh n = send_rrq(peer, name, mode); 203752fa694SWarner Losh if (n > 0) { 204752fa694SWarner Losh printf("Cannot send RRQ packet\n"); 205*92570f67SDag-Erling Smørgrav return -1; 2069b50d902SRodney W. Grimes } 2079b50d902SRodney W. Grimes 2089b50d902SRodney W. Grimes /* 209752fa694SWarner Losh * The first packet we receive has the new destination port 210752fa694SWarner Losh * we have to send the next packets to. 2119b50d902SRodney W. Grimes */ 212752fa694SWarner Losh n = receive_packet(peer, recvbuffer, 213752fa694SWarner Losh MAXPKTSIZE, &from, timeoutpacket); 2149b50d902SRodney W. Grimes 215752fa694SWarner Losh /* We got something useful! */ 216752fa694SWarner Losh if (n >= 0) { 217752fa694SWarner Losh ((struct sockaddr_in *)&peer_sock)->sin_port = 218752fa694SWarner Losh ((struct sockaddr_in *)&from)->sin_port; 2199b50d902SRodney W. Grimes break; 2209b50d902SRodney W. Grimes } 221752fa694SWarner Losh 222752fa694SWarner Losh /* We should retry if this happens */ 223752fa694SWarner Losh if (n == RP_TIMEOUT) { 224752fa694SWarner Losh printf("Try %d, didn't receive answer from remote.\n", 225752fa694SWarner Losh i + 1); 226752fa694SWarner Losh continue; 2279b50d902SRodney W. Grimes } 2289b50d902SRodney W. Grimes 229752fa694SWarner Losh /* Otherwise it is a fatal error */ 230752fa694SWarner Losh break; 2319b50d902SRodney W. Grimes } 232a1ec94b8SGavin Atkinson if (i == 12) { 233a1ec94b8SGavin Atkinson printf("Transfer timed out.\n"); 234*92570f67SDag-Erling Smørgrav return -1; 235a1ec94b8SGavin Atkinson } 236752fa694SWarner Losh if (rp->th_opcode == ERROR) { 237752fa694SWarner Losh tftp_log(LOG_ERR, "Error code %d: %s", rp->th_code, rp->th_msg); 238*92570f67SDag-Erling Smørgrav return -1; 2399b50d902SRodney W. Grimes } 2409b50d902SRodney W. Grimes 241752fa694SWarner Losh if (write_init(fd, NULL, mode) < 0) { 242752fa694SWarner Losh warn("write_init"); 243*92570f67SDag-Erling Smørgrav return -1; 2449b50d902SRodney W. Grimes } 2459b50d902SRodney W. Grimes 246752fa694SWarner Losh /* 247752fa694SWarner Losh * If the first packet is an OACK packet instead of an DATA packet, 248752fa694SWarner Losh * handle it different. 249752fa694SWarner Losh */ 250752fa694SWarner Losh if (rp->th_opcode == OACK) { 251752fa694SWarner Losh if (!options_rfc_enabled) { 252752fa694SWarner Losh printf("Got OACK while options are not enabled!\n"); 253752fa694SWarner Losh send_error(peer, EBADOP); 254*92570f67SDag-Erling Smørgrav return -1; 2559b50d902SRodney W. Grimes } 2564dac6235SHajimu UMEMOTO 257752fa694SWarner Losh parse_options(peer, rp->th_stuff, n + 2); 2584dac6235SHajimu UMEMOTO 259752fa694SWarner Losh n = send_ack(peer, 0); 260752fa694SWarner Losh if (n > 0) { 261752fa694SWarner Losh printf("Cannot send ACK on OACK.\n"); 262*92570f67SDag-Erling Smørgrav return -1; 263752fa694SWarner Losh } 264752fa694SWarner Losh block = 0; 265*92570f67SDag-Erling Smørgrav if (tftp_receive(peer, &block, &tftp_stats, NULL, 0) != 0) 266*92570f67SDag-Erling Smørgrav ret = -1; 267752fa694SWarner Losh } else { 268752fa694SWarner Losh block = 1; 269*92570f67SDag-Erling Smørgrav if (tftp_receive(peer, &block, &tftp_stats, rp, n) != 0) 270*92570f67SDag-Erling Smørgrav ret = -1; 271752fa694SWarner Losh } 2724dac6235SHajimu UMEMOTO 273752fa694SWarner Losh if (tftp_stats.amount > 0) 274752fa694SWarner Losh printstats("Received", verbose, &tftp_stats); 275*92570f67SDag-Erling Smørgrav return ret; 2764dac6235SHajimu UMEMOTO } 277