11590Srgrimes/*
21590Srgrimes * Copyright (c) 1983, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
3093428Sdwmalone#if 0
3193428Sdwmalone#ifndef lint
3293428Sdwmalonestatic char sccsid[] = "@(#)tftp.c	8.1 (Berkeley) 6/6/93";
3393428Sdwmalone#endif /* not lint */
3493428Sdwmalone#endif
3593428Sdwmalone
3687708Smarkm#include <sys/cdefs.h>
3787708Smarkm__FBSDID("$FreeBSD: stable/10/usr.bin/tftp/tftp.c 339060 2018-10-01 16:09:20Z asomers $");
3887708Smarkm
391590Srgrimes/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
401590Srgrimes
411590Srgrimes/*
421590Srgrimes * TFTP User Program -- Protocol Machines
431590Srgrimes */
441590Srgrimes#include <sys/socket.h>
45207607Simp#include <sys/stat.h>
461590Srgrimes
471590Srgrimes#include <netinet/in.h>
481590Srgrimes
491590Srgrimes#include <arpa/tftp.h>
501590Srgrimes
51339060Sasomers#include <assert.h>
5228202Scharnier#include <err.h>
53207607Simp#include <netdb.h>
541590Srgrimes#include <stdio.h>
55207607Simp#include <stdlib.h>
5633645Sjb#include <string.h>
57207607Simp#include <syslog.h>
581590Srgrimes
59207607Simp#include "tftp.h"
60207607Simp#include "tftp-file.h"
61207607Simp#include "tftp-utils.h"
62207607Simp#include "tftp-io.h"
63207607Simp#include "tftp-transfer.h"
64207607Simp#include "tftp-options.h"
651590Srgrimes
661590Srgrimes/*
671590Srgrimes * Send the requested file.
681590Srgrimes */
691590Srgrimesvoid
70207607Simpxmitfile(int peer, char *port, int fd, char *name, char *mode)
711590Srgrimes{
72207607Simp	struct tftphdr *rp;
73207607Simp	int n, i;
74207607Simp	uint16_t block;
7594443Sume	struct sockaddr_storage serv;	/* valid server port number */
76207607Simp	char recvbuffer[MAXPKTSIZE];
77207607Simp	struct tftp_stats tftp_stats;
781590Srgrimes
79207607Simp	stats_init(&tftp_stats);
80207607Simp
8194443Sume	memset(&serv, 0, sizeof(serv));
82207607Simp	rp = (struct tftphdr *)recvbuffer;
831590Srgrimes
84207607Simp	if (port == NULL) {
85207607Simp		struct servent *se;
86207607Simp		se = getservbyname("tftp", "udp");
87339060Sasomers		assert(se != NULL);
88207607Simp		((struct sockaddr_in *)&peer_sock)->sin_port = se->s_port;
89207607Simp	} else
90207607Simp		((struct sockaddr_in *)&peer_sock)->sin_port =
91207607Simp		    htons(atoi(port));
92207607Simp
93207607Simp	for (i = 0; i < 12; i++) {
94207607Simp		struct sockaddr_storage from;
95207607Simp
96207607Simp		/* Tell the other side what we want to do */
97207607Simp		if (debug&DEBUG_SIMPLE)
98207607Simp			printf("Sending %s\n", name);
99207607Simp
100207607Simp		n = send_wrq(peer, name, mode);
101207607Simp		if (n > 0) {
102207607Simp			printf("Cannot send WRQ packet\n");
103207607Simp			return;
1041590Srgrimes		}
105207607Simp
106207607Simp		/*
107207607Simp		 * The first packet we receive has the new destination port
108207607Simp		 * we have to send the next packets to.
109207607Simp		 */
110207607Simp		n = receive_packet(peer, recvbuffer,
111207607Simp		    MAXPKTSIZE, &from, timeoutpacket);
112207607Simp
113207607Simp		/* We got some data! */
114207607Simp		if (n >= 0) {
115207607Simp			((struct sockaddr_in *)&peer_sock)->sin_port =
116207607Simp			    ((struct sockaddr_in *)&from)->sin_port;
117207607Simp			break;
1181590Srgrimes		}
1191590Srgrimes
120207607Simp		/* This should be retried */
121207607Simp		if (n == RP_TIMEOUT) {
122207607Simp			printf("Try %d, didn't receive answer from remote.\n",
123207607Simp			    i + 1);
124207607Simp			continue;
1251590Srgrimes		}
126207607Simp
127207607Simp		/* Everything else is fatal */
128207607Simp		break;
129207607Simp	}
130207607Simp	if (i == 12) {
131207607Simp		printf("Transfer timed out.\n");
132207607Simp		return;
133207607Simp	}
134207607Simp	if (rp->th_opcode == ERROR) {
135207607Simp		printf("Got ERROR, aborted\n");
136207607Simp		return;
137207607Simp	}
138207607Simp
139207607Simp	/*
140207607Simp	 * If the first packet is an OACK instead of an ACK packet,
141207607Simp	 * handle it different.
142207607Simp	 */
143207607Simp	if (rp->th_opcode == OACK) {
144207607Simp		if (!options_rfc_enabled) {
145207607Simp			printf("Got OACK while options are not enabled!\n");
146207607Simp			send_error(peer, EBADOP);
147207607Simp			return;
148207607Simp		}
149207607Simp
150207607Simp		parse_options(peer, rp->th_stuff, n + 2);
151207607Simp	}
152207607Simp
153207607Simp	if (read_init(fd, NULL, mode) < 0) {
154207607Simp		warn("read_init()");
155207607Simp		return;
156207607Simp	}
157207607Simp
158207607Simp	block = 1;
159207607Simp	tftp_send(peer, &block, &tftp_stats);
160207607Simp
161207607Simp	read_close();
162209551Sgavin	if (tftp_stats.amount > 0)
163207607Simp		printstats("Sent", verbose, &tftp_stats);
164207607Simp
165207607Simp	txrx_error = 1;
1661590Srgrimes}
1671590Srgrimes
1681590Srgrimes/*
1691590Srgrimes * Receive a file.
1701590Srgrimes */
1711590Srgrimesvoid
172207607Simprecvfile(int peer, char *port, int fd, char *name, char *mode)
1731590Srgrimes{
174207607Simp	struct tftphdr *rp;
175207607Simp	uint16_t block;
176207607Simp	char recvbuffer[MAXPKTSIZE];
177207607Simp	int n, i;
178207607Simp	struct tftp_stats tftp_stats;
1791590Srgrimes
180207607Simp	stats_init(&tftp_stats);
1811590Srgrimes
182207607Simp	rp = (struct tftphdr *)recvbuffer;
1831590Srgrimes
184207607Simp	if (port == NULL) {
185207607Simp		struct servent *se;
186207607Simp		se = getservbyname("tftp", "udp");
187339060Sasomers		assert(se != NULL);
188207607Simp		((struct sockaddr_in *)&peer_sock)->sin_port = se->s_port;
189207607Simp	} else
190207607Simp		((struct sockaddr_in *)&peer_sock)->sin_port =
191207607Simp		    htons(atoi(port));
1921590Srgrimes
193207607Simp	for (i = 0; i < 12; i++) {
194207607Simp		struct sockaddr_storage from;
1951590Srgrimes
196207607Simp		/* Tell the other side what we want to do */
197207607Simp		if (debug&DEBUG_SIMPLE)
198207607Simp			printf("Requesting %s\n", name);
1991590Srgrimes
200207607Simp		n = send_rrq(peer, name, mode);
201207607Simp		if (n > 0) {
202207607Simp			printf("Cannot send RRQ packet\n");
203207607Simp			return;
204207607Simp		}
2051590Srgrimes
206207607Simp		/*
207207607Simp		 * The first packet we receive has the new destination port
208207607Simp		 * we have to send the next packets to.
209207607Simp		 */
210207607Simp		n = receive_packet(peer, recvbuffer,
211207607Simp		    MAXPKTSIZE, &from, timeoutpacket);
2121590Srgrimes
213207607Simp		/* We got something useful! */
214207607Simp		if (n >= 0) {
215207607Simp			((struct sockaddr_in *)&peer_sock)->sin_port =
216207607Simp			    ((struct sockaddr_in *)&from)->sin_port;
2171590Srgrimes			break;
218207607Simp		}
2191590Srgrimes
220207607Simp		/* We should retry if this happens */
221207607Simp		if (n == RP_TIMEOUT) {
222207607Simp			printf("Try %d, didn't receive answer from remote.\n",
223207607Simp			    i + 1);
224207607Simp			continue;
225207607Simp		}
2261590Srgrimes
227207607Simp		/* Otherwise it is a fatal error */
2281590Srgrimes		break;
229207607Simp	}
230209550Sgavin	if (i == 12) {
231209550Sgavin		printf("Transfer timed out.\n");
232209550Sgavin		return;
233209550Sgavin	}
234207607Simp	if (rp->th_opcode == ERROR) {
235207607Simp		tftp_log(LOG_ERR, "Error code %d: %s", rp->th_code, rp->th_msg);
236207607Simp		return;
237207607Simp	}
2381590Srgrimes
239207607Simp	if (write_init(fd, NULL, mode) < 0) {
240207607Simp		warn("write_init");
241207607Simp		return;
2421590Srgrimes	}
2431590Srgrimes
244207607Simp	/*
245207607Simp	 * If the first packet is an OACK packet instead of an DATA packet,
246207607Simp	 * handle it different.
247207607Simp	 */
248207607Simp	if (rp->th_opcode == OACK) {
249207607Simp		if (!options_rfc_enabled) {
250207607Simp			printf("Got OACK while options are not enabled!\n");
251207607Simp			send_error(peer, EBADOP);
252207607Simp			return;
253207607Simp		}
2541590Srgrimes
255207607Simp		parse_options(peer, rp->th_stuff, n + 2);
2561590Srgrimes
257207607Simp		n = send_ack(peer, 0);
258207607Simp		if (n > 0) {
259207607Simp			printf("Cannot send ACK on OACK.\n");
260207607Simp			return;
261207607Simp		}
262207607Simp		block = 0;
263207607Simp		tftp_receive(peer, &block, &tftp_stats, NULL, 0);
264207607Simp	} else {
265207607Simp		block = 1;
266207607Simp		tftp_receive(peer, &block, &tftp_stats, rp, n);
2671590Srgrimes	}
26894443Sume
269207607Simp	if (tftp_stats.amount > 0)
270207607Simp		printstats("Received", verbose, &tftp_stats);
271207607Simp	return;
27294443Sume}
273