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