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