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