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: stable/11/libexec/tftpd/tftp-io.c 345389 2019-03-21 21:45:18Z asomers $");
28207614Simp
29207614Simp#include <sys/stat.h>
30207614Simp#include <sys/types.h>
31207614Simp#include <sys/socket.h>
32207614Simp
33207614Simp#include <netinet/in.h>
34207614Simp#include <arpa/tftp.h>
35207614Simp#include <arpa/inet.h>
36207614Simp
37339051Sasomers#include <assert.h>
38207614Simp#include <errno.h>
39207614Simp#include <setjmp.h>
40207614Simp#include <signal.h>
41345389Sasomers#include <stddef.h>
42207614Simp#include <stdio.h>
43207614Simp#include <stdlib.h>
44207614Simp#include <string.h>
45207614Simp#include <syslog.h>
46207614Simp#include <unistd.h>
47207614Simp
48207614Simp#include "tftp-file.h"
49207614Simp#include "tftp-io.h"
50207614Simp#include "tftp-utils.h"
51207614Simp#include "tftp-options.h"
52207614Simp
53207614Simpstruct sockaddr_storage peer_sock;
54207614Simpstruct sockaddr_storage me_sock;
55207614Simp
56207614Simpstatic int send_packet(int peer, uint16_t block, char *pkt, int size);
57207614Simp
58241720Sedstatic struct errmsg {
59207614Simp	int	e_code;
60207614Simp	const char	*e_msg;
61207614Simp} errmsgs[] = {
62207614Simp	{ EUNDEF,	"Undefined error code" },
63207614Simp	{ ENOTFOUND,	"File not found" },
64207614Simp	{ EACCESS,	"Access violation" },
65207614Simp	{ ENOSPACE,	"Disk full or allocation exceeded" },
66207614Simp	{ EBADOP,	"Illegal TFTP operation" },
67207614Simp	{ EBADID,	"Unknown transfer ID" },
68207614Simp	{ EEXISTS,	"File already exists" },
69207614Simp	{ ENOUSER,	"No such user" },
70207614Simp	{ EOPTNEG,	"Option negotiation" },
71207614Simp	{ -1,		NULL }
72207614Simp};
73207614Simp
74207614Simp#define DROPPACKET(s)							\
75207614Simp	if (packetdroppercentage != 0 &&				\
76207614Simp	    random()%100 < packetdroppercentage) {			\
77229780Suqs		tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s);	\
78207614Simp		return;							\
79207614Simp	}
80207614Simp#define DROPPACKETn(s,n)						\
81207614Simp	if (packetdroppercentage != 0 &&				\
82207614Simp	    random()%100 < packetdroppercentage) {			\
83229780Suqs		tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s);	\
84207614Simp		return (n);						\
85207614Simp	}
86207614Simp
87207614Simpconst char *
88207614Simperrtomsg(int error)
89207614Simp{
90207614Simp	static char ebuf[40];
91207614Simp	struct errmsg *pe;
92207614Simp
93207614Simp	if (error == 0)
94207614Simp		return ("success");
95207614Simp	for (pe = errmsgs; pe->e_code >= 0; pe++)
96207614Simp		if (pe->e_code == error)
97207614Simp			return (pe->e_msg);
98244686Santoine	snprintf(ebuf, sizeof(ebuf), "error %d", error);
99207614Simp	return (ebuf);
100207614Simp}
101207614Simp
102207614Simpstatic int
103207614Simpsend_packet(int peer, uint16_t block, char *pkt, int size)
104207614Simp{
105207614Simp	int i;
106207614Simp	int t = 1;
107207614Simp
108207614Simp	for (i = 0; i < 12 ; i++) {
109207614Simp		DROPPACKETn("send_packet", 0);
110207614Simp
111246139Smarius		if (sendto(peer, pkt, size, 0, (struct sockaddr *)&peer_sock,
112246139Smarius		    peer_sock.ss_len) == size) {
113207614Simp			if (i)
114207614Simp				tftp_log(LOG_ERR,
115207614Simp				    "%s block %d, attempt %d successful",
116246139Smarius		    		    packettype(ntohs(((struct tftphdr *)
117246139Smarius				    (pkt))->th_opcode)), block, i);
118207614Simp			return (0);
119207614Simp		}
120207614Simp		tftp_log(LOG_ERR,
121207614Simp		    "%s block %d, attempt %d failed (Error %d: %s)",
122207614Simp		    packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)),
123207614Simp		    block, i, errno, strerror(errno));
124207614Simp		sleep(t);
125207614Simp		if (t < 32)
126207614Simp			t <<= 1;
127207614Simp	}
128207614Simp	tftp_log(LOG_ERR, "send_packet: %s", strerror(errno));
129207614Simp	return (1);
130207614Simp}
131207614Simp
132207614Simp/*
133207614Simp * Send an ERROR packet (error message).
134207614Simp * Error code passed in is one of the
135207614Simp * standard TFTP codes, or a UNIX errno
136207614Simp * offset by 100.
137207614Simp */
138207614Simpvoid
139207614Simpsend_error(int peer, int error)
140207614Simp{
141207614Simp	struct tftphdr *tp;
142207614Simp	int length;
143207614Simp	struct errmsg *pe;
144207614Simp	char buf[MAXPKTSIZE];
145207614Simp
146207614Simp	if (debug&DEBUG_PACKETS)
147246106Ssbruno		tftp_log(LOG_DEBUG, "Sending ERROR %d", error);
148207614Simp
149207614Simp	DROPPACKET("send_error");
150207614Simp
151207614Simp	tp = (struct tftphdr *)buf;
152207614Simp	tp->th_opcode = htons((u_short)ERROR);
153207614Simp	tp->th_code = htons((u_short)error);
154207614Simp	for (pe = errmsgs; pe->e_code >= 0; pe++)
155207614Simp		if (pe->e_code == error)
156207614Simp			break;
157207614Simp	if (pe->e_code < 0) {
158207614Simp		pe->e_msg = strerror(error - 100);
159207614Simp		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
160207614Simp	}
161207614Simp	strcpy(tp->th_msg, pe->e_msg);
162207614Simp	length = strlen(pe->e_msg);
163207614Simp	tp->th_msg[length] = '\0';
164207614Simp	length += 5;
165207614Simp
166207614Simp	if (debug&DEBUG_PACKETS)
167207614Simp		tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg);
168207614Simp
169207614Simp	if (sendto(peer, buf, length, 0,
170207614Simp		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != length)
171207614Simp		tftp_log(LOG_ERR, "send_error: %s", strerror(errno));
172207614Simp}
173207614Simp
174207614Simp/*
175207614Simp * Send an WRQ packet (write request).
176207614Simp */
177207614Simpint
178207614Simpsend_wrq(int peer, char *filename, char *mode)
179207614Simp{
180207614Simp	int n;
181207614Simp	struct tftphdr *tp;
182207614Simp	char *bp;
183207614Simp	char buf[MAXPKTSIZE];
184207614Simp	int size;
185207614Simp
186207614Simp	if (debug&DEBUG_PACKETS)
187207614Simp		tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'",
188207614Simp			filename, mode
189207614Simp		);
190207614Simp
191207614Simp	DROPPACKETn("send_wrq", 1);
192207614Simp
193207614Simp	tp = (struct tftphdr *)buf;
194207614Simp	tp->th_opcode = htons((u_short)WRQ);
195345389Sasomers	size = offsetof(struct tftphdr, th_stuff);
196207614Simp
197207614Simp	bp = tp->th_stuff;
198345389Sasomers	strlcpy(bp, filename, sizeof(buf) - size);
199207614Simp	bp += strlen(filename);
200207614Simp	*bp = 0;
201207614Simp	bp++;
202207614Simp	size += strlen(filename) + 1;
203207614Simp
204345389Sasomers	strlcpy(bp, mode, sizeof(buf) - size);
205207614Simp	bp += strlen(mode);
206207614Simp	*bp = 0;
207207614Simp	bp++;
208207614Simp	size += strlen(mode) + 1;
209207614Simp
210207614Simp	if (options_rfc_enabled)
211207614Simp		size += make_options(peer, bp, sizeof(buf) - size);
212207614Simp
213207614Simp	n = sendto(peer, buf, size, 0,
214207614Simp	    (struct sockaddr *)&peer_sock, peer_sock.ss_len);
215207614Simp	if (n != size) {
216207614Simp		tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno));
217207614Simp		return (1);
218207614Simp	}
219207614Simp	return (0);
220207614Simp}
221207614Simp
222207614Simp/*
223207614Simp * Send an RRQ packet (write request).
224207614Simp */
225207614Simpint
226207614Simpsend_rrq(int peer, char *filename, char *mode)
227207614Simp{
228207614Simp	int n;
229207614Simp	struct tftphdr *tp;
230207614Simp	char *bp;
231207614Simp	char buf[MAXPKTSIZE];
232207614Simp	int size;
233207614Simp
234207614Simp	if (debug&DEBUG_PACKETS)
235207614Simp		tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'",
236207614Simp			filename, mode
237207614Simp		);
238207614Simp
239207614Simp	DROPPACKETn("send_rrq", 1);
240207614Simp
241207614Simp	tp = (struct tftphdr *)buf;
242207614Simp	tp->th_opcode = htons((u_short)RRQ);
243345389Sasomers	size = offsetof(struct tftphdr, th_stuff);
244207614Simp
245207614Simp	bp = tp->th_stuff;
246345389Sasomers	strlcpy(bp, filename, sizeof(buf) - size);
247207614Simp	bp += strlen(filename);
248207614Simp	*bp = 0;
249207614Simp	bp++;
250207614Simp	size += strlen(filename) + 1;
251207614Simp
252345389Sasomers	strlcpy(bp, mode, sizeof(buf) - size);
253207614Simp	bp += strlen(mode);
254207614Simp	*bp = 0;
255207614Simp	bp++;
256207614Simp	size += strlen(mode) + 1;
257207614Simp
258207614Simp	if (options_rfc_enabled) {
259207614Simp		options[OPT_TSIZE].o_request = strdup("0");
260207614Simp		size += make_options(peer, bp, sizeof(buf) - size);
261207614Simp	}
262207614Simp
263207614Simp	n = sendto(peer, buf, size, 0,
264207614Simp	    (struct sockaddr *)&peer_sock, peer_sock.ss_len);
265207614Simp	if (n != size) {
266222326Srodrigc		tftp_log(LOG_ERR, "send_rrq: %d %s", n, strerror(errno));
267207614Simp		return (1);
268207614Simp	}
269207614Simp	return (0);
270207614Simp}
271207614Simp
272207614Simp/*
273207614Simp * Send an OACK packet (option acknowledgement).
274207614Simp */
275207614Simpint
276207614Simpsend_oack(int peer)
277207614Simp{
278207614Simp	struct tftphdr *tp;
279207614Simp	int size, i, n;
280207614Simp	char *bp;
281207614Simp	char buf[MAXPKTSIZE];
282207614Simp
283207614Simp	if (debug&DEBUG_PACKETS)
284207614Simp		tftp_log(LOG_DEBUG, "Sending OACK");
285207614Simp
286207614Simp	DROPPACKETn("send_oack", 0);
287207614Simp
288207614Simp	/*
289207614Simp	 * Send back an options acknowledgement (only the ones with
290207614Simp	 * a reply for)
291207614Simp	 */
292207614Simp	tp = (struct tftphdr *)buf;
293207614Simp	bp = buf + 2;
294207614Simp	size = sizeof(buf) - 2;
295207614Simp	tp->th_opcode = htons((u_short)OACK);
296207614Simp	for (i = 0; options[i].o_type != NULL; i++) {
297207614Simp		if (options[i].o_reply != NULL) {
298207614Simp			n = snprintf(bp, size, "%s%c%s", options[i].o_type,
299207614Simp				     0, options[i].o_reply);
300207614Simp			bp += n+1;
301207614Simp			size -= n+1;
302207614Simp			if (size < 0) {
303207614Simp				tftp_log(LOG_ERR, "oack: buffer overflow");
304207614Simp				exit(1);
305207614Simp			}
306207614Simp		}
307207614Simp	}
308207614Simp	size = bp - buf;
309207614Simp
310207614Simp	if (sendto(peer, buf, size, 0,
311207614Simp		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
312207614Simp		tftp_log(LOG_INFO, "send_oack: %s", strerror(errno));
313207614Simp		return (1);
314207614Simp	}
315207614Simp
316207614Simp	return (0);
317207614Simp}
318207614Simp
319207614Simp/*
320207614Simp * Send an ACK packet (acknowledgement).
321207614Simp */
322207614Simpint
323207614Simpsend_ack(int fp, uint16_t block)
324207614Simp{
325207614Simp	struct tftphdr *tp;
326207614Simp	int size;
327207614Simp	char buf[MAXPKTSIZE];
328207614Simp
329207614Simp	if (debug&DEBUG_PACKETS)
330207614Simp		tftp_log(LOG_DEBUG, "Sending ACK for block %d", block);
331207614Simp
332207614Simp	DROPPACKETn("send_ack", 0);
333207614Simp
334207614Simp	tp = (struct tftphdr *)buf;
335207614Simp	size = sizeof(buf) - 2;
336207614Simp	tp->th_opcode = htons((u_short)ACK);
337207614Simp	tp->th_block = htons((u_short)block);
338207614Simp	size = 4;
339207614Simp
340207614Simp	if (sendto(fp, buf, size, 0,
341207614Simp	    (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
342207614Simp		tftp_log(LOG_INFO, "send_ack: %s", strerror(errno));
343207614Simp		return (1);
344207614Simp	}
345207614Simp
346207614Simp	return (0);
347207614Simp}
348207614Simp
349207614Simp/*
350207614Simp * Send a DATA packet
351207614Simp */
352207614Simpint
353207614Simpsend_data(int peer, uint16_t block, char *data, int size)
354207614Simp{
355207614Simp	char buf[MAXPKTSIZE];
356207614Simp	struct tftphdr *pkt;
357207614Simp	int n;
358207614Simp
359207614Simp	if (debug&DEBUG_PACKETS)
360207614Simp		tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes",
361207614Simp			block, size);
362207614Simp
363207614Simp	DROPPACKETn("send_data", 0);
364207614Simp
365207614Simp	pkt = (struct tftphdr *)buf;
366207614Simp
367207614Simp	pkt->th_opcode = htons((u_short)DATA);
368207614Simp	pkt->th_block = htons((u_short)block);
369207614Simp	memcpy(pkt->th_data, data, size);
370207614Simp
371207614Simp	n = send_packet(peer, block, (char *)pkt, size + 4);
372207614Simp	return (n);
373207614Simp}
374207614Simp
375207614Simp
376207614Simp/*
377207614Simp * Receive a packet
378207614Simp */
379241720Sedstatic jmp_buf timeoutbuf;
380207614Simp
381207614Simpstatic void
382207614Simptimeout(int sig __unused)
383207614Simp{
384207614Simp
385207614Simp	/* tftp_log(LOG_DEBUG, "Timeout\n");	Inside a signal handler... */
386207614Simp	longjmp(timeoutbuf, 1);
387207614Simp}
388207614Simp
389207614Simpint
390207614Simpreceive_packet(int peer, char *data, int size, struct sockaddr_storage *from,
391207614Simp    int thistimeout)
392207614Simp{
393207614Simp	struct tftphdr *pkt;
394207614Simp	struct sockaddr_storage from_local;
395207614Simp	struct sockaddr_storage *pfrom;
396207614Simp	socklen_t fromlen;
397207614Simp	int n;
398339051Sasomers	static int timed_out;
399207614Simp
400207614Simp	if (debug&DEBUG_PACKETS)
401207614Simp		tftp_log(LOG_DEBUG,
402207614Simp		    "Waiting %d seconds for packet", timeoutpacket);
403207614Simp
404207614Simp	pkt = (struct tftphdr *)data;
405207614Simp
406207614Simp	signal(SIGALRM, timeout);
407339051Sasomers	timed_out = setjmp(timeoutbuf);
408207614Simp	alarm(thistimeout);
409207614Simp
410339051Sasomers	if (timed_out != 0) {
411207614Simp		tftp_log(LOG_ERR, "receive_packet: timeout");
412207614Simp		alarm(0);
413207614Simp		return (RP_TIMEOUT);
414207614Simp	}
415207614Simp
416212665Simp	pfrom = (from == NULL) ? &from_local : from;
417207614Simp	fromlen = sizeof(*pfrom);
418207614Simp	n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen);
419207614Simp
420207614Simp	alarm(0);
421207614Simp
422207614Simp	DROPPACKETn("receive_packet", RP_TIMEOUT);
423207614Simp
424207614Simp	if (n < 0) {
425207614Simp		tftp_log(LOG_ERR, "receive_packet: timeout");
426207614Simp		return (RP_TIMEOUT);
427207614Simp	}
428207614Simp
429207614Simp	if (n < 0) {
430207614Simp		/* No idea what could have happened if it isn't a timeout */
431207614Simp		tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno));
432207614Simp		return (RP_RECVFROM);
433207614Simp	}
434207614Simp	if (n < 4) {
435207614Simp		tftp_log(LOG_ERR,
436207614Simp		    "receive_packet: packet too small (%d bytes)", n);
437207614Simp		return (RP_TOOSMALL);
438207614Simp	}
439207614Simp
440207614Simp	pkt->th_opcode = ntohs((u_short)pkt->th_opcode);
441207614Simp	if (pkt->th_opcode == DATA ||
442207614Simp	    pkt->th_opcode == ACK)
443207614Simp		pkt->th_block = ntohs((u_short)pkt->th_block);
444207614Simp
445207614Simp	if (pkt->th_opcode == DATA && n > pktsize) {
446207614Simp		tftp_log(LOG_ERR, "receive_packet: packet too big");
447207614Simp		return (RP_TOOBIG);
448207614Simp	}
449207614Simp
450207614Simp	if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr !=
451207614Simp	    ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) {
452207614Simp		tftp_log(LOG_ERR,
453207614Simp			"receive_packet: received packet from wrong source");
454207614Simp		return (RP_WRONGSOURCE);
455207614Simp	}
456207614Simp
457207614Simp	if (pkt->th_opcode == ERROR) {
458231973Semaste		tftp_log(pkt->th_code == EUNDEF ? LOG_DEBUG : LOG_ERR,
459231973Semaste		    "Got ERROR packet: %s", pkt->th_msg);
460207614Simp		return (RP_ERROR);
461207614Simp	}
462207614Simp
463207614Simp	if (debug&DEBUG_PACKETS)
464207614Simp		tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet",
465207614Simp			n, packettype(pkt->th_opcode));
466207614Simp
467207614Simp	return n - 4;
468207614Simp}
469