1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD$");
30
31#include <sys/stat.h>
32#include <sys/types.h>
33#include <sys/socket.h>
34
35#include <netinet/in.h>
36#include <arpa/tftp.h>
37#include <arpa/inet.h>
38
39#include <assert.h>
40#include <errno.h>
41#include <setjmp.h>
42#include <signal.h>
43#include <stddef.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <syslog.h>
48#include <unistd.h>
49
50#include "tftp-file.h"
51#include "tftp-io.h"
52#include "tftp-utils.h"
53#include "tftp-options.h"
54
55struct sockaddr_storage peer_sock;
56struct sockaddr_storage me_sock;
57
58static int send_packet(int peer, uint16_t block, char *pkt, int size);
59
60static struct errmsg {
61	int	e_code;
62	const char	*e_msg;
63} errmsgs[] = {
64	{ EUNDEF,	"Undefined error code" },
65	{ ENOTFOUND,	"File not found" },
66	{ EACCESS,	"Access violation" },
67	{ ENOSPACE,	"Disk full or allocation exceeded" },
68	{ EBADOP,	"Illegal TFTP operation" },
69	{ EBADID,	"Unknown transfer ID" },
70	{ EEXISTS,	"File already exists" },
71	{ ENOUSER,	"No such user" },
72	{ EOPTNEG,	"Option negotiation" },
73	{ -1,		NULL }
74};
75
76#define DROPPACKET(s)							\
77	if (packetdroppercentage != 0 &&				\
78	    random()%100 < packetdroppercentage) {			\
79		tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s);	\
80		return;							\
81	}
82#define DROPPACKETn(s,n)						\
83	if (packetdroppercentage != 0 &&				\
84	    random()%100 < packetdroppercentage) {			\
85		tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s);	\
86		return (n);						\
87	}
88
89const char *
90errtomsg(int error)
91{
92	static char ebuf[40];
93	struct errmsg *pe;
94
95	if (error == 0)
96		return ("success");
97	for (pe = errmsgs; pe->e_code >= 0; pe++)
98		if (pe->e_code == error)
99			return (pe->e_msg);
100	snprintf(ebuf, sizeof(ebuf), "error %d", error);
101	return (ebuf);
102}
103
104static int
105send_packet(int peer, uint16_t block, char *pkt, int size)
106{
107	int i;
108	int t = 1;
109
110	for (i = 0; i < 12 ; i++) {
111		DROPPACKETn("send_packet", 0);
112
113		if (sendto(peer, pkt, size, 0, (struct sockaddr *)&peer_sock,
114		    peer_sock.ss_len) == size) {
115			if (i)
116				tftp_log(LOG_ERR,
117				    "%s block %d, attempt %d successful",
118		    		    packettype(ntohs(((struct tftphdr *)
119				    (pkt))->th_opcode)), block, i);
120			return (0);
121		}
122		tftp_log(LOG_ERR,
123		    "%s block %d, attempt %d failed (Error %d: %s)",
124		    packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)),
125		    block, i, errno, strerror(errno));
126		sleep(t);
127		if (t < 32)
128			t <<= 1;
129	}
130	tftp_log(LOG_ERR, "send_packet: %s", strerror(errno));
131	return (1);
132}
133
134/*
135 * Send an ERROR packet (error message).
136 * Error code passed in is one of the
137 * standard TFTP codes, or a UNIX errno
138 * offset by 100.
139 */
140void
141send_error(int peer, int error)
142{
143	struct tftphdr *tp;
144	int length;
145	struct errmsg *pe;
146	char buf[MAXPKTSIZE];
147
148	if (debug&DEBUG_PACKETS)
149		tftp_log(LOG_DEBUG, "Sending ERROR %d", error);
150
151	DROPPACKET("send_error");
152
153	tp = (struct tftphdr *)buf;
154	tp->th_opcode = htons((u_short)ERROR);
155	tp->th_code = htons((u_short)error);
156	for (pe = errmsgs; pe->e_code >= 0; pe++)
157		if (pe->e_code == error)
158			break;
159	if (pe->e_code < 0) {
160		pe->e_msg = strerror(error - 100);
161		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
162	}
163	strcpy(tp->th_msg, pe->e_msg);
164	length = strlen(pe->e_msg);
165	tp->th_msg[length] = '\0';
166	length += 5;
167
168	if (debug&DEBUG_PACKETS)
169		tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg);
170
171	if (sendto(peer, buf, length, 0,
172		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != length)
173		tftp_log(LOG_ERR, "send_error: %s", strerror(errno));
174}
175
176/*
177 * Send an WRQ packet (write request).
178 */
179int
180send_wrq(int peer, char *filename, char *mode)
181{
182	int n;
183	struct tftphdr *tp;
184	char *bp;
185	char buf[MAXPKTSIZE];
186	int size;
187
188	if (debug&DEBUG_PACKETS)
189		tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'",
190			filename, mode
191		);
192
193	DROPPACKETn("send_wrq", 0);
194
195	tp = (struct tftphdr *)buf;
196	tp->th_opcode = htons((u_short)WRQ);
197	size = offsetof(struct tftphdr, th_stuff);
198
199	bp = tp->th_stuff;
200	strlcpy(bp, filename, sizeof(buf) - size);
201	bp += strlen(filename);
202	*bp = 0;
203	bp++;
204	size += strlen(filename) + 1;
205
206	strlcpy(bp, mode, sizeof(buf) - size);
207	bp += strlen(mode);
208	*bp = 0;
209	bp++;
210	size += strlen(mode) + 1;
211
212	if (options_rfc_enabled)
213		size += make_options(peer, bp, sizeof(buf) - size);
214
215	n = sendto(peer, buf, size, 0,
216	    (struct sockaddr *)&peer_sock, peer_sock.ss_len);
217	if (n != size) {
218		tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno));
219		return (1);
220	}
221	return (0);
222}
223
224/*
225 * Send an RRQ packet (write request).
226 */
227int
228send_rrq(int peer, char *filename, char *mode)
229{
230	int n;
231	struct tftphdr *tp;
232	char *bp;
233	char buf[MAXPKTSIZE];
234	int size;
235
236	if (debug&DEBUG_PACKETS)
237		tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'",
238			filename, mode
239		);
240
241	DROPPACKETn("send_rrq", 0);
242
243	tp = (struct tftphdr *)buf;
244	tp->th_opcode = htons((u_short)RRQ);
245	size = offsetof(struct tftphdr, th_stuff);
246
247	bp = tp->th_stuff;
248	strlcpy(bp, filename, sizeof(buf) - size);
249	bp += strlen(filename);
250	*bp = 0;
251	bp++;
252	size += strlen(filename) + 1;
253
254	strlcpy(bp, mode, sizeof(buf) - size);
255	bp += strlen(mode);
256	*bp = 0;
257	bp++;
258	size += strlen(mode) + 1;
259
260	if (options_rfc_enabled) {
261		options[OPT_TSIZE].o_request = strdup("0");
262		size += make_options(peer, bp, sizeof(buf) - size);
263	}
264
265	n = sendto(peer, buf, size, 0,
266	    (struct sockaddr *)&peer_sock, peer_sock.ss_len);
267	if (n != size) {
268		tftp_log(LOG_ERR, "send_rrq: %d %s", n, strerror(errno));
269		return (1);
270	}
271	return (0);
272}
273
274/*
275 * Send an OACK packet (option acknowledgement).
276 */
277int
278send_oack(int peer)
279{
280	struct tftphdr *tp;
281	int size, i, n;
282	char *bp;
283	char buf[MAXPKTSIZE];
284
285	if (debug&DEBUG_PACKETS)
286		tftp_log(LOG_DEBUG, "Sending OACK");
287
288	DROPPACKETn("send_oack", 0);
289
290	/*
291	 * Send back an options acknowledgement (only the ones with
292	 * a reply for)
293	 */
294	tp = (struct tftphdr *)buf;
295	bp = buf + 2;
296	size = sizeof(buf) - 2;
297	tp->th_opcode = htons((u_short)OACK);
298	for (i = 0; options[i].o_type != NULL; i++) {
299		if (options[i].o_reply != NULL) {
300			n = snprintf(bp, size, "%s%c%s", options[i].o_type,
301				     0, options[i].o_reply);
302			bp += n+1;
303			size -= n+1;
304			if (size < 0) {
305				tftp_log(LOG_ERR, "oack: buffer overflow");
306				exit(1);
307			}
308		}
309	}
310	size = bp - buf;
311
312	if (sendto(peer, buf, size, 0,
313		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
314		tftp_log(LOG_INFO, "send_oack: %s", strerror(errno));
315		return (1);
316	}
317
318	return (0);
319}
320
321/*
322 * Send an ACK packet (acknowledgement).
323 */
324int
325send_ack(int fp, uint16_t block)
326{
327	struct tftphdr *tp;
328	int size;
329	char buf[MAXPKTSIZE];
330
331	if (debug&DEBUG_PACKETS)
332		tftp_log(LOG_DEBUG, "Sending ACK for block %d", block);
333
334	DROPPACKETn("send_ack", 0);
335
336	tp = (struct tftphdr *)buf;
337	size = sizeof(buf) - 2;
338	tp->th_opcode = htons((u_short)ACK);
339	tp->th_block = htons((u_short)block);
340	size = 4;
341
342	if (sendto(fp, buf, size, 0,
343	    (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
344		tftp_log(LOG_INFO, "send_ack: %s", strerror(errno));
345		return (1);
346	}
347
348	return (0);
349}
350
351/*
352 * Send a DATA packet
353 */
354int
355send_data(int peer, uint16_t block, char *data, int size)
356{
357	char buf[MAXPKTSIZE];
358	struct tftphdr *pkt;
359	int n;
360
361	if (debug&DEBUG_PACKETS)
362		tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes",
363			block, size);
364
365	DROPPACKETn("send_data", 0);
366
367	pkt = (struct tftphdr *)buf;
368
369	pkt->th_opcode = htons((u_short)DATA);
370	pkt->th_block = htons((u_short)block);
371	memcpy(pkt->th_data, data, size);
372
373	n = send_packet(peer, block, (char *)pkt, size + 4);
374	return (n);
375}
376
377
378/*
379 * Receive a packet
380 */
381static jmp_buf timeoutbuf;
382
383static void
384timeout(int sig __unused)
385{
386
387	/* tftp_log(LOG_DEBUG, "Timeout\n");	Inside a signal handler... */
388	longjmp(timeoutbuf, 1);
389}
390
391int
392receive_packet(int peer, char *data, int size, struct sockaddr_storage *from,
393    int thistimeout)
394{
395	struct tftphdr *pkt;
396	struct sockaddr_storage from_local;
397	struct sockaddr_storage *pfrom;
398	socklen_t fromlen;
399	int n;
400	static int timed_out;
401
402	if (debug&DEBUG_PACKETS)
403		tftp_log(LOG_DEBUG,
404		    "Waiting %d seconds for packet", timeoutpacket);
405
406	pkt = (struct tftphdr *)data;
407
408	signal(SIGALRM, timeout);
409	timed_out = setjmp(timeoutbuf);
410	alarm(thistimeout);
411
412	if (timed_out != 0) {
413		tftp_log(LOG_ERR, "receive_packet: timeout");
414		alarm(0);
415		return (RP_TIMEOUT);
416	}
417
418	pfrom = (from == NULL) ? &from_local : from;
419	fromlen = sizeof(*pfrom);
420	n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen);
421
422	alarm(0);
423
424	DROPPACKETn("receive_packet", RP_TIMEOUT);
425
426	if (n < 0) {
427		tftp_log(LOG_ERR, "receive_packet: timeout");
428		return (RP_TIMEOUT);
429	}
430
431	if (n < 0) {
432		/* No idea what could have happened if it isn't a timeout */
433		tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno));
434		return (RP_RECVFROM);
435	}
436	if (n < 4) {
437		tftp_log(LOG_ERR,
438		    "receive_packet: packet too small (%d bytes)", n);
439		return (RP_TOOSMALL);
440	}
441
442	pkt->th_opcode = ntohs((u_short)pkt->th_opcode);
443	if (pkt->th_opcode == DATA ||
444	    pkt->th_opcode == ACK)
445		pkt->th_block = ntohs((u_short)pkt->th_block);
446
447	if (pkt->th_opcode == DATA && n > pktsize) {
448		tftp_log(LOG_ERR, "receive_packet: packet too big");
449		return (RP_TOOBIG);
450	}
451
452	if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr !=
453	    ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) {
454		tftp_log(LOG_ERR,
455			"receive_packet: received packet from wrong source");
456		return (RP_WRONGSOURCE);
457	}
458
459	if (pkt->th_opcode == ERROR) {
460		tftp_log(pkt->th_code == EUNDEF ? LOG_DEBUG : LOG_ERR,
461		    "Got ERROR packet: %s", pkt->th_msg);
462		return (RP_ERROR);
463	}
464
465	if (debug&DEBUG_PACKETS)
466		tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet",
467			n, packettype(pkt->th_opcode));
468
469	return n - 4;
470}
471