xref: /freebsd/libexec/tftpd/tftp-io.c (revision 3c0fa265346ec04fc8277fba9c3fe28d1e26068a)
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/stat.h>
32e7ff5475SWarner Losh #include <sys/types.h>
33e7ff5475SWarner Losh #include <sys/socket.h>
34e7ff5475SWarner Losh 
35e7ff5475SWarner Losh #include <netinet/in.h>
36e7ff5475SWarner Losh #include <arpa/tftp.h>
37e7ff5475SWarner Losh #include <arpa/inet.h>
38e7ff5475SWarner Losh 
39*3c0fa265SAlan Somers #include <assert.h>
40e7ff5475SWarner Losh #include <errno.h>
41e7ff5475SWarner Losh #include <setjmp.h>
42e7ff5475SWarner Losh #include <signal.h>
43e7ff5475SWarner Losh #include <stdio.h>
44e7ff5475SWarner Losh #include <stdlib.h>
45e7ff5475SWarner Losh #include <string.h>
46e7ff5475SWarner Losh #include <syslog.h>
47e7ff5475SWarner Losh #include <unistd.h>
48e7ff5475SWarner Losh 
49e7ff5475SWarner Losh #include "tftp-file.h"
50e7ff5475SWarner Losh #include "tftp-io.h"
51e7ff5475SWarner Losh #include "tftp-utils.h"
52e7ff5475SWarner Losh #include "tftp-options.h"
53e7ff5475SWarner Losh 
54e7ff5475SWarner Losh struct sockaddr_storage peer_sock;
55e7ff5475SWarner Losh struct sockaddr_storage me_sock;
56e7ff5475SWarner Losh 
57e7ff5475SWarner Losh static int send_packet(int peer, uint16_t block, char *pkt, int size);
58e7ff5475SWarner Losh 
59ae824d80SEd Schouten static struct errmsg {
60e7ff5475SWarner Losh 	int	e_code;
61e7ff5475SWarner Losh 	const char	*e_msg;
62e7ff5475SWarner Losh } errmsgs[] = {
63e7ff5475SWarner Losh 	{ EUNDEF,	"Undefined error code" },
64e7ff5475SWarner Losh 	{ ENOTFOUND,	"File not found" },
65e7ff5475SWarner Losh 	{ EACCESS,	"Access violation" },
66e7ff5475SWarner Losh 	{ ENOSPACE,	"Disk full or allocation exceeded" },
67e7ff5475SWarner Losh 	{ EBADOP,	"Illegal TFTP operation" },
68e7ff5475SWarner Losh 	{ EBADID,	"Unknown transfer ID" },
69e7ff5475SWarner Losh 	{ EEXISTS,	"File already exists" },
70e7ff5475SWarner Losh 	{ ENOUSER,	"No such user" },
71e7ff5475SWarner Losh 	{ EOPTNEG,	"Option negotiation" },
72e7ff5475SWarner Losh 	{ -1,		NULL }
73e7ff5475SWarner Losh };
74e7ff5475SWarner Losh 
75e7ff5475SWarner Losh #define DROPPACKET(s)							\
76e7ff5475SWarner Losh 	if (packetdroppercentage != 0 &&				\
77e7ff5475SWarner Losh 	    random()%100 < packetdroppercentage) {			\
781acf0dbaSUlrich Spörlein 		tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s);	\
79e7ff5475SWarner Losh 		return;							\
80e7ff5475SWarner Losh 	}
81e7ff5475SWarner Losh #define DROPPACKETn(s,n)						\
82e7ff5475SWarner Losh 	if (packetdroppercentage != 0 &&				\
83e7ff5475SWarner Losh 	    random()%100 < packetdroppercentage) {			\
841acf0dbaSUlrich Spörlein 		tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s);	\
85e7ff5475SWarner Losh 		return (n);						\
86e7ff5475SWarner Losh 	}
87e7ff5475SWarner Losh 
88e7ff5475SWarner Losh const char *
89e7ff5475SWarner Losh errtomsg(int error)
90e7ff5475SWarner Losh {
91e7ff5475SWarner Losh 	static char ebuf[40];
92e7ff5475SWarner Losh 	struct errmsg *pe;
93e7ff5475SWarner Losh 
94e7ff5475SWarner Losh 	if (error == 0)
95e7ff5475SWarner Losh 		return ("success");
96e7ff5475SWarner Losh 	for (pe = errmsgs; pe->e_code >= 0; pe++)
97e7ff5475SWarner Losh 		if (pe->e_code == error)
98e7ff5475SWarner Losh 			return (pe->e_msg);
993496d72cSAntoine Brodin 	snprintf(ebuf, sizeof(ebuf), "error %d", error);
100e7ff5475SWarner Losh 	return (ebuf);
101e7ff5475SWarner Losh }
102e7ff5475SWarner Losh 
103e7ff5475SWarner Losh static int
104e7ff5475SWarner Losh send_packet(int peer, uint16_t block, char *pkt, int size)
105e7ff5475SWarner Losh {
106e7ff5475SWarner Losh 	int i;
107e7ff5475SWarner Losh 	int t = 1;
108e7ff5475SWarner Losh 
109e7ff5475SWarner Losh 	for (i = 0; i < 12 ; i++) {
110e7ff5475SWarner Losh 		DROPPACKETn("send_packet", 0);
111e7ff5475SWarner Losh 
112b713097aSMarius Strobl 		if (sendto(peer, pkt, size, 0, (struct sockaddr *)&peer_sock,
113b713097aSMarius Strobl 		    peer_sock.ss_len) == size) {
114e7ff5475SWarner Losh 			if (i)
115e7ff5475SWarner Losh 				tftp_log(LOG_ERR,
116e7ff5475SWarner Losh 				    "%s block %d, attempt %d successful",
117b713097aSMarius Strobl 		    		    packettype(ntohs(((struct tftphdr *)
118b713097aSMarius Strobl 				    (pkt))->th_opcode)), block, i);
119e7ff5475SWarner Losh 			return (0);
120e7ff5475SWarner Losh 		}
121e7ff5475SWarner Losh 		tftp_log(LOG_ERR,
122e7ff5475SWarner Losh 		    "%s block %d, attempt %d failed (Error %d: %s)",
123e7ff5475SWarner Losh 		    packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)),
124e7ff5475SWarner Losh 		    block, i, errno, strerror(errno));
125e7ff5475SWarner Losh 		sleep(t);
126e7ff5475SWarner Losh 		if (t < 32)
127e7ff5475SWarner Losh 			t <<= 1;
128e7ff5475SWarner Losh 	}
129e7ff5475SWarner Losh 	tftp_log(LOG_ERR, "send_packet: %s", strerror(errno));
130e7ff5475SWarner Losh 	return (1);
131e7ff5475SWarner Losh }
132e7ff5475SWarner Losh 
133e7ff5475SWarner Losh /*
134e7ff5475SWarner Losh  * Send an ERROR packet (error message).
135e7ff5475SWarner Losh  * Error code passed in is one of the
136e7ff5475SWarner Losh  * standard TFTP codes, or a UNIX errno
137e7ff5475SWarner Losh  * offset by 100.
138e7ff5475SWarner Losh  */
139e7ff5475SWarner Losh void
140e7ff5475SWarner Losh send_error(int peer, int error)
141e7ff5475SWarner Losh {
142e7ff5475SWarner Losh 	struct tftphdr *tp;
143e7ff5475SWarner Losh 	int length;
144e7ff5475SWarner Losh 	struct errmsg *pe;
145e7ff5475SWarner Losh 	char buf[MAXPKTSIZE];
146e7ff5475SWarner Losh 
147e7ff5475SWarner Losh 	if (debug&DEBUG_PACKETS)
148663a6522SSean Bruno 		tftp_log(LOG_DEBUG, "Sending ERROR %d", error);
149e7ff5475SWarner Losh 
150e7ff5475SWarner Losh 	DROPPACKET("send_error");
151e7ff5475SWarner Losh 
152e7ff5475SWarner Losh 	tp = (struct tftphdr *)buf;
153e7ff5475SWarner Losh 	tp->th_opcode = htons((u_short)ERROR);
154e7ff5475SWarner Losh 	tp->th_code = htons((u_short)error);
155e7ff5475SWarner Losh 	for (pe = errmsgs; pe->e_code >= 0; pe++)
156e7ff5475SWarner Losh 		if (pe->e_code == error)
157e7ff5475SWarner Losh 			break;
158e7ff5475SWarner Losh 	if (pe->e_code < 0) {
159e7ff5475SWarner Losh 		pe->e_msg = strerror(error - 100);
160e7ff5475SWarner Losh 		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
161e7ff5475SWarner Losh 	}
162e7ff5475SWarner Losh 	strcpy(tp->th_msg, pe->e_msg);
163e7ff5475SWarner Losh 	length = strlen(pe->e_msg);
164e7ff5475SWarner Losh 	tp->th_msg[length] = '\0';
165e7ff5475SWarner Losh 	length += 5;
166e7ff5475SWarner Losh 
167e7ff5475SWarner Losh 	if (debug&DEBUG_PACKETS)
168e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg);
169e7ff5475SWarner Losh 
170e7ff5475SWarner Losh 	if (sendto(peer, buf, length, 0,
171e7ff5475SWarner Losh 		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != length)
172e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "send_error: %s", strerror(errno));
173e7ff5475SWarner Losh }
174e7ff5475SWarner Losh 
175e7ff5475SWarner Losh /*
176e7ff5475SWarner Losh  * Send an WRQ packet (write request).
177e7ff5475SWarner Losh  */
178e7ff5475SWarner Losh int
179e7ff5475SWarner Losh send_wrq(int peer, char *filename, char *mode)
180e7ff5475SWarner Losh {
181e7ff5475SWarner Losh 	int n;
182e7ff5475SWarner Losh 	struct tftphdr *tp;
183e7ff5475SWarner Losh 	char *bp;
184e7ff5475SWarner Losh 	char buf[MAXPKTSIZE];
185e7ff5475SWarner Losh 	int size;
186e7ff5475SWarner Losh 
187e7ff5475SWarner Losh 	if (debug&DEBUG_PACKETS)
188e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'",
189e7ff5475SWarner Losh 			filename, mode
190e7ff5475SWarner Losh 		);
191e7ff5475SWarner Losh 
192e7ff5475SWarner Losh 	DROPPACKETn("send_wrq", 1);
193e7ff5475SWarner Losh 
194e7ff5475SWarner Losh 	tp = (struct tftphdr *)buf;
195e7ff5475SWarner Losh 	tp->th_opcode = htons((u_short)WRQ);
196e7ff5475SWarner Losh 	size = 2;
197e7ff5475SWarner Losh 
198e7ff5475SWarner Losh 	bp = tp->th_stuff;
199e7ff5475SWarner Losh 	strcpy(bp, filename);
200e7ff5475SWarner Losh 	bp += strlen(filename);
201e7ff5475SWarner Losh 	*bp = 0;
202e7ff5475SWarner Losh 	bp++;
203e7ff5475SWarner Losh 	size += strlen(filename) + 1;
204e7ff5475SWarner Losh 
205e7ff5475SWarner Losh 	strcpy(bp, mode);
206e7ff5475SWarner Losh 	bp += strlen(mode);
207e7ff5475SWarner Losh 	*bp = 0;
208e7ff5475SWarner Losh 	bp++;
209e7ff5475SWarner Losh 	size += strlen(mode) + 1;
210e7ff5475SWarner Losh 
211e7ff5475SWarner Losh 	if (options_rfc_enabled)
212e7ff5475SWarner Losh 		size += make_options(peer, bp, sizeof(buf) - size);
213e7ff5475SWarner Losh 
214e7ff5475SWarner Losh 	n = sendto(peer, buf, size, 0,
215e7ff5475SWarner Losh 	    (struct sockaddr *)&peer_sock, peer_sock.ss_len);
216e7ff5475SWarner Losh 	if (n != size) {
217e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno));
218e7ff5475SWarner Losh 		return (1);
219e7ff5475SWarner Losh 	}
220e7ff5475SWarner Losh 	return (0);
221e7ff5475SWarner Losh }
222e7ff5475SWarner Losh 
223e7ff5475SWarner Losh /*
224e7ff5475SWarner Losh  * Send an RRQ packet (write request).
225e7ff5475SWarner Losh  */
226e7ff5475SWarner Losh int
227e7ff5475SWarner Losh send_rrq(int peer, char *filename, char *mode)
228e7ff5475SWarner Losh {
229e7ff5475SWarner Losh 	int n;
230e7ff5475SWarner Losh 	struct tftphdr *tp;
231e7ff5475SWarner Losh 	char *bp;
232e7ff5475SWarner Losh 	char buf[MAXPKTSIZE];
233e7ff5475SWarner Losh 	int size;
234e7ff5475SWarner Losh 
235e7ff5475SWarner Losh 	if (debug&DEBUG_PACKETS)
236e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'",
237e7ff5475SWarner Losh 			filename, mode
238e7ff5475SWarner Losh 		);
239e7ff5475SWarner Losh 
240e7ff5475SWarner Losh 	DROPPACKETn("send_rrq", 1);
241e7ff5475SWarner Losh 
242e7ff5475SWarner Losh 	tp = (struct tftphdr *)buf;
243e7ff5475SWarner Losh 	tp->th_opcode = htons((u_short)RRQ);
244e7ff5475SWarner Losh 	size = 2;
245e7ff5475SWarner Losh 
246e7ff5475SWarner Losh 	bp = tp->th_stuff;
247e7ff5475SWarner Losh 	strcpy(bp, filename);
248e7ff5475SWarner Losh 	bp += strlen(filename);
249e7ff5475SWarner Losh 	*bp = 0;
250e7ff5475SWarner Losh 	bp++;
251e7ff5475SWarner Losh 	size += strlen(filename) + 1;
252e7ff5475SWarner Losh 
253e7ff5475SWarner Losh 	strcpy(bp, mode);
254e7ff5475SWarner Losh 	bp += strlen(mode);
255e7ff5475SWarner Losh 	*bp = 0;
256e7ff5475SWarner Losh 	bp++;
257e7ff5475SWarner Losh 	size += strlen(mode) + 1;
258e7ff5475SWarner Losh 
259e7ff5475SWarner Losh 	if (options_rfc_enabled) {
260e7ff5475SWarner Losh 		options[OPT_TSIZE].o_request = strdup("0");
261e7ff5475SWarner Losh 		size += make_options(peer, bp, sizeof(buf) - size);
262e7ff5475SWarner Losh 	}
263e7ff5475SWarner Losh 
264e7ff5475SWarner Losh 	n = sendto(peer, buf, size, 0,
265e7ff5475SWarner Losh 	    (struct sockaddr *)&peer_sock, peer_sock.ss_len);
266e7ff5475SWarner Losh 	if (n != size) {
2673b6bd978SCraig Rodrigues 		tftp_log(LOG_ERR, "send_rrq: %d %s", n, strerror(errno));
268e7ff5475SWarner Losh 		return (1);
269e7ff5475SWarner Losh 	}
270e7ff5475SWarner Losh 	return (0);
271e7ff5475SWarner Losh }
272e7ff5475SWarner Losh 
273e7ff5475SWarner Losh /*
274e7ff5475SWarner Losh  * Send an OACK packet (option acknowledgement).
275e7ff5475SWarner Losh  */
276e7ff5475SWarner Losh int
277e7ff5475SWarner Losh send_oack(int peer)
278e7ff5475SWarner Losh {
279e7ff5475SWarner Losh 	struct tftphdr *tp;
280e7ff5475SWarner Losh 	int size, i, n;
281e7ff5475SWarner Losh 	char *bp;
282e7ff5475SWarner Losh 	char buf[MAXPKTSIZE];
283e7ff5475SWarner Losh 
284e7ff5475SWarner Losh 	if (debug&DEBUG_PACKETS)
285e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending OACK");
286e7ff5475SWarner Losh 
287e7ff5475SWarner Losh 	DROPPACKETn("send_oack", 0);
288e7ff5475SWarner Losh 
289e7ff5475SWarner Losh 	/*
290e7ff5475SWarner Losh 	 * Send back an options acknowledgement (only the ones with
291e7ff5475SWarner Losh 	 * a reply for)
292e7ff5475SWarner Losh 	 */
293e7ff5475SWarner Losh 	tp = (struct tftphdr *)buf;
294e7ff5475SWarner Losh 	bp = buf + 2;
295e7ff5475SWarner Losh 	size = sizeof(buf) - 2;
296e7ff5475SWarner Losh 	tp->th_opcode = htons((u_short)OACK);
297e7ff5475SWarner Losh 	for (i = 0; options[i].o_type != NULL; i++) {
298e7ff5475SWarner Losh 		if (options[i].o_reply != NULL) {
299e7ff5475SWarner Losh 			n = snprintf(bp, size, "%s%c%s", options[i].o_type,
300e7ff5475SWarner Losh 				     0, options[i].o_reply);
301e7ff5475SWarner Losh 			bp += n+1;
302e7ff5475SWarner Losh 			size -= n+1;
303e7ff5475SWarner Losh 			if (size < 0) {
304e7ff5475SWarner Losh 				tftp_log(LOG_ERR, "oack: buffer overflow");
305e7ff5475SWarner Losh 				exit(1);
306e7ff5475SWarner Losh 			}
307e7ff5475SWarner Losh 		}
308e7ff5475SWarner Losh 	}
309e7ff5475SWarner Losh 	size = bp - buf;
310e7ff5475SWarner Losh 
311e7ff5475SWarner Losh 	if (sendto(peer, buf, size, 0,
312e7ff5475SWarner Losh 		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
313e7ff5475SWarner Losh 		tftp_log(LOG_INFO, "send_oack: %s", strerror(errno));
314e7ff5475SWarner Losh 		return (1);
315e7ff5475SWarner Losh 	}
316e7ff5475SWarner Losh 
317e7ff5475SWarner Losh 	return (0);
318e7ff5475SWarner Losh }
319e7ff5475SWarner Losh 
320e7ff5475SWarner Losh /*
321e7ff5475SWarner Losh  * Send an ACK packet (acknowledgement).
322e7ff5475SWarner Losh  */
323e7ff5475SWarner Losh int
324e7ff5475SWarner Losh send_ack(int fp, uint16_t block)
325e7ff5475SWarner Losh {
326e7ff5475SWarner Losh 	struct tftphdr *tp;
327e7ff5475SWarner Losh 	int size;
328e7ff5475SWarner Losh 	char buf[MAXPKTSIZE];
329e7ff5475SWarner Losh 
330e7ff5475SWarner Losh 	if (debug&DEBUG_PACKETS)
331e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending ACK for block %d", block);
332e7ff5475SWarner Losh 
333e7ff5475SWarner Losh 	DROPPACKETn("send_ack", 0);
334e7ff5475SWarner Losh 
335e7ff5475SWarner Losh 	tp = (struct tftphdr *)buf;
336e7ff5475SWarner Losh 	size = sizeof(buf) - 2;
337e7ff5475SWarner Losh 	tp->th_opcode = htons((u_short)ACK);
338e7ff5475SWarner Losh 	tp->th_block = htons((u_short)block);
339e7ff5475SWarner Losh 	size = 4;
340e7ff5475SWarner Losh 
341e7ff5475SWarner Losh 	if (sendto(fp, buf, size, 0,
342e7ff5475SWarner Losh 	    (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
343e7ff5475SWarner Losh 		tftp_log(LOG_INFO, "send_ack: %s", strerror(errno));
344e7ff5475SWarner Losh 		return (1);
345e7ff5475SWarner Losh 	}
346e7ff5475SWarner Losh 
347e7ff5475SWarner Losh 	return (0);
348e7ff5475SWarner Losh }
349e7ff5475SWarner Losh 
350e7ff5475SWarner Losh /*
351e7ff5475SWarner Losh  * Send a DATA packet
352e7ff5475SWarner Losh  */
353e7ff5475SWarner Losh int
354e7ff5475SWarner Losh send_data(int peer, uint16_t block, char *data, int size)
355e7ff5475SWarner Losh {
356e7ff5475SWarner Losh 	char buf[MAXPKTSIZE];
357e7ff5475SWarner Losh 	struct tftphdr *pkt;
358e7ff5475SWarner Losh 	int n;
359e7ff5475SWarner Losh 
360e7ff5475SWarner Losh 	if (debug&DEBUG_PACKETS)
361e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes",
362e7ff5475SWarner Losh 			block, size);
363e7ff5475SWarner Losh 
364e7ff5475SWarner Losh 	DROPPACKETn("send_data", 0);
365e7ff5475SWarner Losh 
366e7ff5475SWarner Losh 	pkt = (struct tftphdr *)buf;
367e7ff5475SWarner Losh 
368e7ff5475SWarner Losh 	pkt->th_opcode = htons((u_short)DATA);
369e7ff5475SWarner Losh 	pkt->th_block = htons((u_short)block);
370e7ff5475SWarner Losh 	memcpy(pkt->th_data, data, size);
371e7ff5475SWarner Losh 
372e7ff5475SWarner Losh 	n = send_packet(peer, block, (char *)pkt, size + 4);
373e7ff5475SWarner Losh 	return (n);
374e7ff5475SWarner Losh }
375e7ff5475SWarner Losh 
376e7ff5475SWarner Losh 
377e7ff5475SWarner Losh /*
378e7ff5475SWarner Losh  * Receive a packet
379e7ff5475SWarner Losh  */
380ae824d80SEd Schouten static jmp_buf timeoutbuf;
381e7ff5475SWarner Losh 
382e7ff5475SWarner Losh static void
383e7ff5475SWarner Losh timeout(int sig __unused)
384e7ff5475SWarner Losh {
385e7ff5475SWarner Losh 
386e7ff5475SWarner Losh 	/* tftp_log(LOG_DEBUG, "Timeout\n");	Inside a signal handler... */
387e7ff5475SWarner Losh 	longjmp(timeoutbuf, 1);
388e7ff5475SWarner Losh }
389e7ff5475SWarner Losh 
390e7ff5475SWarner Losh int
391e7ff5475SWarner Losh receive_packet(int peer, char *data, int size, struct sockaddr_storage *from,
392e7ff5475SWarner Losh     int thistimeout)
393e7ff5475SWarner Losh {
394e7ff5475SWarner Losh 	struct tftphdr *pkt;
395e7ff5475SWarner Losh 	struct sockaddr_storage from_local;
396e7ff5475SWarner Losh 	struct sockaddr_storage *pfrom;
397e7ff5475SWarner Losh 	socklen_t fromlen;
398e7ff5475SWarner Losh 	int n;
399*3c0fa265SAlan Somers 	static int timed_out;
400e7ff5475SWarner Losh 
401e7ff5475SWarner Losh 	if (debug&DEBUG_PACKETS)
402e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG,
403e7ff5475SWarner Losh 		    "Waiting %d seconds for packet", timeoutpacket);
404e7ff5475SWarner Losh 
405e7ff5475SWarner Losh 	pkt = (struct tftphdr *)data;
406e7ff5475SWarner Losh 
407e7ff5475SWarner Losh 	signal(SIGALRM, timeout);
408*3c0fa265SAlan Somers 	timed_out = setjmp(timeoutbuf);
409e7ff5475SWarner Losh 	alarm(thistimeout);
410e7ff5475SWarner Losh 
411*3c0fa265SAlan Somers 	if (timed_out != 0) {
412e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "receive_packet: timeout");
413e7ff5475SWarner Losh 		alarm(0);
414e7ff5475SWarner Losh 		return (RP_TIMEOUT);
415e7ff5475SWarner Losh 	}
416e7ff5475SWarner Losh 
4173dc2fd3fSWarner Losh 	pfrom = (from == NULL) ? &from_local : from;
418e7ff5475SWarner Losh 	fromlen = sizeof(*pfrom);
419e7ff5475SWarner Losh 	n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen);
420e7ff5475SWarner Losh 
421e7ff5475SWarner Losh 	alarm(0);
422e7ff5475SWarner Losh 
423e7ff5475SWarner Losh 	DROPPACKETn("receive_packet", RP_TIMEOUT);
424e7ff5475SWarner Losh 
425e7ff5475SWarner Losh 	if (n < 0) {
426e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "receive_packet: timeout");
427e7ff5475SWarner Losh 		return (RP_TIMEOUT);
428e7ff5475SWarner Losh 	}
429e7ff5475SWarner Losh 
430e7ff5475SWarner Losh 	if (n < 0) {
431e7ff5475SWarner Losh 		/* No idea what could have happened if it isn't a timeout */
432e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno));
433e7ff5475SWarner Losh 		return (RP_RECVFROM);
434e7ff5475SWarner Losh 	}
435e7ff5475SWarner Losh 	if (n < 4) {
436e7ff5475SWarner Losh 		tftp_log(LOG_ERR,
437e7ff5475SWarner Losh 		    "receive_packet: packet too small (%d bytes)", n);
438e7ff5475SWarner Losh 		return (RP_TOOSMALL);
439e7ff5475SWarner Losh 	}
440e7ff5475SWarner Losh 
441e7ff5475SWarner Losh 	pkt->th_opcode = ntohs((u_short)pkt->th_opcode);
442e7ff5475SWarner Losh 	if (pkt->th_opcode == DATA ||
443e7ff5475SWarner Losh 	    pkt->th_opcode == ACK)
444e7ff5475SWarner Losh 		pkt->th_block = ntohs((u_short)pkt->th_block);
445e7ff5475SWarner Losh 
446e7ff5475SWarner Losh 	if (pkt->th_opcode == DATA && n > pktsize) {
447e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "receive_packet: packet too big");
448e7ff5475SWarner Losh 		return (RP_TOOBIG);
449e7ff5475SWarner Losh 	}
450e7ff5475SWarner Losh 
451e7ff5475SWarner Losh 	if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr !=
452e7ff5475SWarner Losh 	    ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) {
453e7ff5475SWarner Losh 		tftp_log(LOG_ERR,
454e7ff5475SWarner Losh 			"receive_packet: received packet from wrong source");
455e7ff5475SWarner Losh 		return (RP_WRONGSOURCE);
456e7ff5475SWarner Losh 	}
457e7ff5475SWarner Losh 
458e7ff5475SWarner Losh 	if (pkt->th_opcode == ERROR) {
4599a2856b4SEd Maste 		tftp_log(pkt->th_code == EUNDEF ? LOG_DEBUG : LOG_ERR,
4609a2856b4SEd Maste 		    "Got ERROR packet: %s", pkt->th_msg);
461e7ff5475SWarner Losh 		return (RP_ERROR);
462e7ff5475SWarner Losh 	}
463e7ff5475SWarner Losh 
464e7ff5475SWarner Losh 	if (debug&DEBUG_PACKETS)
465e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet",
466e7ff5475SWarner Losh 			n, packettype(pkt->th_opcode));
467e7ff5475SWarner Losh 
468e7ff5475SWarner Losh 	return n - 4;
469e7ff5475SWarner Losh }
470