xref: /freebsd/libexec/tftpd/tftp-io.c (revision 389e4940069316fe667ffa263fa7d6390d0a960f)
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 <errno.h>
40 #include <setjmp.h>
41 #include <signal.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 
53 struct sockaddr_storage peer_sock;
54 struct sockaddr_storage me_sock;
55 
56 static int send_packet(int peer, uint16_t block, char *pkt, int size);
57 
58 static 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 
87 const char *
88 errtomsg(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 
102 static int
103 send_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  */
138 void
139 send_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  */
177 int
178 send_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 = 2;
196 
197 	bp = tp->th_stuff;
198 	strcpy(bp, filename);
199 	bp += strlen(filename);
200 	*bp = 0;
201 	bp++;
202 	size += strlen(filename) + 1;
203 
204 	strcpy(bp, mode);
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  */
225 int
226 send_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 = 2;
244 
245 	bp = tp->th_stuff;
246 	strcpy(bp, filename);
247 	bp += strlen(filename);
248 	*bp = 0;
249 	bp++;
250 	size += strlen(filename) + 1;
251 
252 	strcpy(bp, mode);
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  */
275 int
276 send_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  */
322 int
323 send_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  */
352 int
353 send_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  */
379 static jmp_buf timeoutbuf;
380 
381 static void
382 timeout(int sig __unused)
383 {
384 
385 	/* tftp_log(LOG_DEBUG, "Timeout\n");	Inside a signal handler... */
386 	longjmp(timeoutbuf, 1);
387 }
388 
389 int
390 receive_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 waiting;
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 	waiting = 0;
407 	signal(SIGALRM, timeout);
408 	setjmp(timeoutbuf);
409 	alarm(thistimeout);
410 
411 	if (waiting > 0) {
412 		alarm(0);
413 		return (RP_TIMEOUT);
414 	}
415 
416 	if (waiting > 0) {
417 		tftp_log(LOG_ERR, "receive_packet: timeout");
418 		alarm(0);
419 		return (RP_TIMEOUT);
420 	}
421 
422 	waiting++;
423 	pfrom = (from == NULL) ? &from_local : from;
424 	fromlen = sizeof(*pfrom);
425 	n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen);
426 
427 	alarm(0);
428 
429 	DROPPACKETn("receive_packet", RP_TIMEOUT);
430 
431 	if (n < 0) {
432 		tftp_log(LOG_ERR, "receive_packet: timeout");
433 		return (RP_TIMEOUT);
434 	}
435 
436 	alarm(0);
437 
438 	if (n < 0) {
439 		/* No idea what could have happened if it isn't a timeout */
440 		tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno));
441 		return (RP_RECVFROM);
442 	}
443 	if (n < 4) {
444 		tftp_log(LOG_ERR,
445 		    "receive_packet: packet too small (%d bytes)", n);
446 		return (RP_TOOSMALL);
447 	}
448 
449 	pkt->th_opcode = ntohs((u_short)pkt->th_opcode);
450 	if (pkt->th_opcode == DATA ||
451 	    pkt->th_opcode == ACK)
452 		pkt->th_block = ntohs((u_short)pkt->th_block);
453 
454 	if (pkt->th_opcode == DATA && n > pktsize) {
455 		tftp_log(LOG_ERR, "receive_packet: packet too big");
456 		return (RP_TOOBIG);
457 	}
458 
459 	if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr !=
460 	    ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) {
461 		tftp_log(LOG_ERR,
462 			"receive_packet: received packet from wrong source");
463 		return (RP_WRONGSOURCE);
464 	}
465 
466 	if (pkt->th_opcode == ERROR) {
467 		tftp_log(pkt->th_code == EUNDEF ? LOG_DEBUG : LOG_ERR,
468 		    "Got ERROR packet: %s", pkt->th_msg);
469 		return (RP_ERROR);
470 	}
471 
472 	if (debug&DEBUG_PACKETS)
473 		tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet",
474 			n, packettype(pkt->th_opcode));
475 
476 	return n - 4;
477 }
478