xref: /freebsd/libexec/tftpd/tftp-io.c (revision 1acf0dba17d74c59fc87d450664df950ff127795)
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 
56e7ff5475SWarner Losh 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) {			\
75*1acf0dbaSUlrich 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) {			\
81*1acf0dbaSUlrich 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 	char buf[MAXPKTSIZE];
91e7ff5475SWarner Losh 
92e7ff5475SWarner Losh 	if (error == 0)
93e7ff5475SWarner Losh 		return ("success");
94e7ff5475SWarner Losh 	for (pe = errmsgs; pe->e_code >= 0; pe++)
95e7ff5475SWarner Losh 		if (pe->e_code == error)
96e7ff5475SWarner Losh 			return (pe->e_msg);
97e7ff5475SWarner Losh 	snprintf(ebuf, sizeof(buf), "error %d", error);
98e7ff5475SWarner Losh 	return (ebuf);
99e7ff5475SWarner Losh }
100e7ff5475SWarner Losh 
101e7ff5475SWarner Losh static int
102e7ff5475SWarner Losh send_packet(int peer, uint16_t block, char *pkt, int size)
103e7ff5475SWarner Losh {
104e7ff5475SWarner Losh 	int i;
105e7ff5475SWarner Losh 	int t = 1;
106e7ff5475SWarner Losh 
107e7ff5475SWarner Losh 	for (i = 0; i < 12 ; i++) {
108e7ff5475SWarner Losh 		DROPPACKETn("send_packet", 0);
109e7ff5475SWarner Losh 
110e7ff5475SWarner Losh 		if (sendto(peer, pkt, size, 0,
111e7ff5475SWarner Losh 			(struct sockaddr *)&peer_sock, peer_sock.ss_len)
112e7ff5475SWarner Losh 			== size) {
113e7ff5475SWarner Losh 			if (i)
114e7ff5475SWarner Losh 				tftp_log(LOG_ERR,
115e7ff5475SWarner Losh 				    "%s block %d, attempt %d successful",
116e7ff5475SWarner Losh 				    block, i);
117e7ff5475SWarner Losh 			return (0);
118e7ff5475SWarner Losh 		}
119e7ff5475SWarner Losh 		tftp_log(LOG_ERR,
120e7ff5475SWarner Losh 		    "%s block %d, attempt %d failed (Error %d: %s)",
121e7ff5475SWarner Losh 		    packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)),
122e7ff5475SWarner Losh 		    block, i, errno, strerror(errno));
123e7ff5475SWarner Losh 		sleep(t);
124e7ff5475SWarner Losh 		if (t < 32)
125e7ff5475SWarner Losh 			t <<= 1;
126e7ff5475SWarner Losh 	}
127e7ff5475SWarner Losh 	tftp_log(LOG_ERR, "send_packet: %s", strerror(errno));
128e7ff5475SWarner Losh 	return (1);
129e7ff5475SWarner Losh }
130e7ff5475SWarner Losh 
131e7ff5475SWarner Losh /*
132e7ff5475SWarner Losh  * Send an ERROR packet (error message).
133e7ff5475SWarner Losh  * Error code passed in is one of the
134e7ff5475SWarner Losh  * standard TFTP codes, or a UNIX errno
135e7ff5475SWarner Losh  * offset by 100.
136e7ff5475SWarner Losh  */
137e7ff5475SWarner Losh void
138e7ff5475SWarner Losh send_error(int peer, int error)
139e7ff5475SWarner Losh {
140e7ff5475SWarner Losh 	struct tftphdr *tp;
141e7ff5475SWarner Losh 	int length;
142e7ff5475SWarner Losh 	struct errmsg *pe;
143e7ff5475SWarner Losh 	char buf[MAXPKTSIZE];
144e7ff5475SWarner Losh 
145e7ff5475SWarner Losh 	if (debug&DEBUG_PACKETS)
146e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error);
147e7ff5475SWarner Losh 
148e7ff5475SWarner Losh 	DROPPACKET("send_error");
149e7ff5475SWarner Losh 
150e7ff5475SWarner Losh 	tp = (struct tftphdr *)buf;
151e7ff5475SWarner Losh 	tp->th_opcode = htons((u_short)ERROR);
152e7ff5475SWarner Losh 	tp->th_code = htons((u_short)error);
153e7ff5475SWarner Losh 	for (pe = errmsgs; pe->e_code >= 0; pe++)
154e7ff5475SWarner Losh 		if (pe->e_code == error)
155e7ff5475SWarner Losh 			break;
156e7ff5475SWarner Losh 	if (pe->e_code < 0) {
157e7ff5475SWarner Losh 		pe->e_msg = strerror(error - 100);
158e7ff5475SWarner Losh 		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
159e7ff5475SWarner Losh 	}
160e7ff5475SWarner Losh 	strcpy(tp->th_msg, pe->e_msg);
161e7ff5475SWarner Losh 	length = strlen(pe->e_msg);
162e7ff5475SWarner Losh 	tp->th_msg[length] = '\0';
163e7ff5475SWarner Losh 	length += 5;
164e7ff5475SWarner Losh 
165e7ff5475SWarner Losh 	if (debug&DEBUG_PACKETS)
166e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg);
167e7ff5475SWarner Losh 
168e7ff5475SWarner Losh 	if (sendto(peer, buf, length, 0,
169e7ff5475SWarner Losh 		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != length)
170e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "send_error: %s", strerror(errno));
171e7ff5475SWarner Losh }
172e7ff5475SWarner Losh 
173e7ff5475SWarner Losh /*
174e7ff5475SWarner Losh  * Send an WRQ packet (write request).
175e7ff5475SWarner Losh  */
176e7ff5475SWarner Losh int
177e7ff5475SWarner Losh send_wrq(int peer, char *filename, char *mode)
178e7ff5475SWarner Losh {
179e7ff5475SWarner Losh 	int n;
180e7ff5475SWarner Losh 	struct tftphdr *tp;
181e7ff5475SWarner Losh 	char *bp;
182e7ff5475SWarner Losh 	char buf[MAXPKTSIZE];
183e7ff5475SWarner Losh 	int size;
184e7ff5475SWarner Losh 
185e7ff5475SWarner Losh 	if (debug&DEBUG_PACKETS)
186e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'",
187e7ff5475SWarner Losh 			filename, mode
188e7ff5475SWarner Losh 		);
189e7ff5475SWarner Losh 
190e7ff5475SWarner Losh 	DROPPACKETn("send_wrq", 1);
191e7ff5475SWarner Losh 
192e7ff5475SWarner Losh 	tp = (struct tftphdr *)buf;
193e7ff5475SWarner Losh 	tp->th_opcode = htons((u_short)WRQ);
194e7ff5475SWarner Losh 	size = 2;
195e7ff5475SWarner Losh 
196e7ff5475SWarner Losh 	bp = tp->th_stuff;
197e7ff5475SWarner Losh 	strcpy(bp, filename);
198e7ff5475SWarner Losh 	bp += strlen(filename);
199e7ff5475SWarner Losh 	*bp = 0;
200e7ff5475SWarner Losh 	bp++;
201e7ff5475SWarner Losh 	size += strlen(filename) + 1;
202e7ff5475SWarner Losh 
203e7ff5475SWarner Losh 	strcpy(bp, mode);
204e7ff5475SWarner Losh 	bp += strlen(mode);
205e7ff5475SWarner Losh 	*bp = 0;
206e7ff5475SWarner Losh 	bp++;
207e7ff5475SWarner Losh 	size += strlen(mode) + 1;
208e7ff5475SWarner Losh 
209e7ff5475SWarner Losh 	if (options_rfc_enabled)
210e7ff5475SWarner Losh 		size += make_options(peer, bp, sizeof(buf) - size);
211e7ff5475SWarner Losh 
212e7ff5475SWarner Losh 	n = sendto(peer, buf, size, 0,
213e7ff5475SWarner Losh 	    (struct sockaddr *)&peer_sock, peer_sock.ss_len);
214e7ff5475SWarner Losh 	if (n != size) {
215e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno));
216e7ff5475SWarner Losh 		return (1);
217e7ff5475SWarner Losh 	}
218e7ff5475SWarner Losh 	return (0);
219e7ff5475SWarner Losh }
220e7ff5475SWarner Losh 
221e7ff5475SWarner Losh /*
222e7ff5475SWarner Losh  * Send an RRQ packet (write request).
223e7ff5475SWarner Losh  */
224e7ff5475SWarner Losh int
225e7ff5475SWarner Losh send_rrq(int peer, char *filename, char *mode)
226e7ff5475SWarner Losh {
227e7ff5475SWarner Losh 	int n;
228e7ff5475SWarner Losh 	struct tftphdr *tp;
229e7ff5475SWarner Losh 	char *bp;
230e7ff5475SWarner Losh 	char buf[MAXPKTSIZE];
231e7ff5475SWarner Losh 	int size;
232e7ff5475SWarner Losh 
233e7ff5475SWarner Losh 	if (debug&DEBUG_PACKETS)
234e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'",
235e7ff5475SWarner Losh 			filename, mode
236e7ff5475SWarner Losh 		);
237e7ff5475SWarner Losh 
238e7ff5475SWarner Losh 	DROPPACKETn("send_rrq", 1);
239e7ff5475SWarner Losh 
240e7ff5475SWarner Losh 	tp = (struct tftphdr *)buf;
241e7ff5475SWarner Losh 	tp->th_opcode = htons((u_short)RRQ);
242e7ff5475SWarner Losh 	size = 2;
243e7ff5475SWarner Losh 
244e7ff5475SWarner Losh 	bp = tp->th_stuff;
245e7ff5475SWarner Losh 	strcpy(bp, filename);
246e7ff5475SWarner Losh 	bp += strlen(filename);
247e7ff5475SWarner Losh 	*bp = 0;
248e7ff5475SWarner Losh 	bp++;
249e7ff5475SWarner Losh 	size += strlen(filename) + 1;
250e7ff5475SWarner Losh 
251e7ff5475SWarner Losh 	strcpy(bp, mode);
252e7ff5475SWarner Losh 	bp += strlen(mode);
253e7ff5475SWarner Losh 	*bp = 0;
254e7ff5475SWarner Losh 	bp++;
255e7ff5475SWarner Losh 	size += strlen(mode) + 1;
256e7ff5475SWarner Losh 
257e7ff5475SWarner Losh 	if (options_rfc_enabled) {
258e7ff5475SWarner Losh 		options[OPT_TSIZE].o_request = strdup("0");
259e7ff5475SWarner Losh 		size += make_options(peer, bp, sizeof(buf) - size);
260e7ff5475SWarner Losh 	}
261e7ff5475SWarner Losh 
262e7ff5475SWarner Losh 	n = sendto(peer, buf, size, 0,
263e7ff5475SWarner Losh 	    (struct sockaddr *)&peer_sock, peer_sock.ss_len);
264e7ff5475SWarner Losh 	if (n != size) {
2653b6bd978SCraig Rodrigues 		tftp_log(LOG_ERR, "send_rrq: %d %s", n, strerror(errno));
266e7ff5475SWarner Losh 		return (1);
267e7ff5475SWarner Losh 	}
268e7ff5475SWarner Losh 	return (0);
269e7ff5475SWarner Losh }
270e7ff5475SWarner Losh 
271e7ff5475SWarner Losh /*
272e7ff5475SWarner Losh  * Send an OACK packet (option acknowledgement).
273e7ff5475SWarner Losh  */
274e7ff5475SWarner Losh int
275e7ff5475SWarner Losh send_oack(int peer)
276e7ff5475SWarner Losh {
277e7ff5475SWarner Losh 	struct tftphdr *tp;
278e7ff5475SWarner Losh 	int size, i, n;
279e7ff5475SWarner Losh 	char *bp;
280e7ff5475SWarner Losh 	char buf[MAXPKTSIZE];
281e7ff5475SWarner Losh 
282e7ff5475SWarner Losh 	if (debug&DEBUG_PACKETS)
283e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending OACK");
284e7ff5475SWarner Losh 
285e7ff5475SWarner Losh 	DROPPACKETn("send_oack", 0);
286e7ff5475SWarner Losh 
287e7ff5475SWarner Losh 	/*
288e7ff5475SWarner Losh 	 * Send back an options acknowledgement (only the ones with
289e7ff5475SWarner Losh 	 * a reply for)
290e7ff5475SWarner Losh 	 */
291e7ff5475SWarner Losh 	tp = (struct tftphdr *)buf;
292e7ff5475SWarner Losh 	bp = buf + 2;
293e7ff5475SWarner Losh 	size = sizeof(buf) - 2;
294e7ff5475SWarner Losh 	tp->th_opcode = htons((u_short)OACK);
295e7ff5475SWarner Losh 	for (i = 0; options[i].o_type != NULL; i++) {
296e7ff5475SWarner Losh 		if (options[i].o_reply != NULL) {
297e7ff5475SWarner Losh 			n = snprintf(bp, size, "%s%c%s", options[i].o_type,
298e7ff5475SWarner Losh 				     0, options[i].o_reply);
299e7ff5475SWarner Losh 			bp += n+1;
300e7ff5475SWarner Losh 			size -= n+1;
301e7ff5475SWarner Losh 			if (size < 0) {
302e7ff5475SWarner Losh 				tftp_log(LOG_ERR, "oack: buffer overflow");
303e7ff5475SWarner Losh 				exit(1);
304e7ff5475SWarner Losh 			}
305e7ff5475SWarner Losh 		}
306e7ff5475SWarner Losh 	}
307e7ff5475SWarner Losh 	size = bp - buf;
308e7ff5475SWarner Losh 
309e7ff5475SWarner Losh 	if (sendto(peer, buf, size, 0,
310e7ff5475SWarner Losh 		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
311e7ff5475SWarner Losh 		tftp_log(LOG_INFO, "send_oack: %s", strerror(errno));
312e7ff5475SWarner Losh 		return (1);
313e7ff5475SWarner Losh 	}
314e7ff5475SWarner Losh 
315e7ff5475SWarner Losh 	return (0);
316e7ff5475SWarner Losh }
317e7ff5475SWarner Losh 
318e7ff5475SWarner Losh /*
319e7ff5475SWarner Losh  * Send an ACK packet (acknowledgement).
320e7ff5475SWarner Losh  */
321e7ff5475SWarner Losh int
322e7ff5475SWarner Losh send_ack(int fp, uint16_t block)
323e7ff5475SWarner Losh {
324e7ff5475SWarner Losh 	struct tftphdr *tp;
325e7ff5475SWarner Losh 	int size;
326e7ff5475SWarner Losh 	char *bp;
327e7ff5475SWarner Losh 	char buf[MAXPKTSIZE];
328e7ff5475SWarner Losh 
329e7ff5475SWarner Losh 	if (debug&DEBUG_PACKETS)
330e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending ACK for block %d", block);
331e7ff5475SWarner Losh 
332e7ff5475SWarner Losh 	DROPPACKETn("send_ack", 0);
333e7ff5475SWarner Losh 
334e7ff5475SWarner Losh 	tp = (struct tftphdr *)buf;
335e7ff5475SWarner Losh 	bp = buf + 2;
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  */
380e7ff5475SWarner Losh 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;
399e7ff5475SWarner Losh 	static int waiting;
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 	waiting = 0;
408e7ff5475SWarner Losh 	signal(SIGALRM, timeout);
409e7ff5475SWarner Losh 	setjmp(timeoutbuf);
410e7ff5475SWarner Losh 	alarm(thistimeout);
411e7ff5475SWarner Losh 
412e7ff5475SWarner Losh 	if (waiting > 0) {
413e7ff5475SWarner Losh 		alarm(0);
414e7ff5475SWarner Losh 		return (RP_TIMEOUT);
415e7ff5475SWarner Losh 	}
416e7ff5475SWarner Losh 
417e7ff5475SWarner Losh 	if (waiting > 0) {
418e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "receive_packet: timeout");
419e7ff5475SWarner Losh 		alarm(0);
420e7ff5475SWarner Losh 		return (RP_TIMEOUT);
421e7ff5475SWarner Losh 	}
422e7ff5475SWarner Losh 
423e7ff5475SWarner Losh 	waiting++;
4243dc2fd3fSWarner Losh 	pfrom = (from == NULL) ? &from_local : from;
425e7ff5475SWarner Losh 	fromlen = sizeof(*pfrom);
426e7ff5475SWarner Losh 	n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen);
427e7ff5475SWarner Losh 
428e7ff5475SWarner Losh 	alarm(0);
429e7ff5475SWarner Losh 
430e7ff5475SWarner Losh 	DROPPACKETn("receive_packet", RP_TIMEOUT);
431e7ff5475SWarner Losh 
432e7ff5475SWarner Losh 	if (n < 0) {
433e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "receive_packet: timeout");
434e7ff5475SWarner Losh 		return (RP_TIMEOUT);
435e7ff5475SWarner Losh 	}
436e7ff5475SWarner Losh 
437e7ff5475SWarner Losh 	alarm(0);
438e7ff5475SWarner Losh 
439e7ff5475SWarner Losh 	if (n < 0) {
440e7ff5475SWarner Losh 		/* No idea what could have happened if it isn't a timeout */
441e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno));
442e7ff5475SWarner Losh 		return (RP_RECVFROM);
443e7ff5475SWarner Losh 	}
444e7ff5475SWarner Losh 	if (n < 4) {
445e7ff5475SWarner Losh 		tftp_log(LOG_ERR,
446e7ff5475SWarner Losh 		    "receive_packet: packet too small (%d bytes)", n);
447e7ff5475SWarner Losh 		return (RP_TOOSMALL);
448e7ff5475SWarner Losh 	}
449e7ff5475SWarner Losh 
450e7ff5475SWarner Losh 	pkt->th_opcode = ntohs((u_short)pkt->th_opcode);
451e7ff5475SWarner Losh 	if (pkt->th_opcode == DATA ||
452e7ff5475SWarner Losh 	    pkt->th_opcode == ACK)
453e7ff5475SWarner Losh 		pkt->th_block = ntohs((u_short)pkt->th_block);
454e7ff5475SWarner Losh 
455e7ff5475SWarner Losh 	if (pkt->th_opcode == DATA && n > pktsize) {
456e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "receive_packet: packet too big");
457e7ff5475SWarner Losh 		return (RP_TOOBIG);
458e7ff5475SWarner Losh 	}
459e7ff5475SWarner Losh 
460e7ff5475SWarner Losh 	if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr !=
461e7ff5475SWarner Losh 	    ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) {
462e7ff5475SWarner Losh 		tftp_log(LOG_ERR,
463e7ff5475SWarner Losh 			"receive_packet: received packet from wrong source");
464e7ff5475SWarner Losh 		return (RP_WRONGSOURCE);
465e7ff5475SWarner Losh 	}
466e7ff5475SWarner Losh 
467e7ff5475SWarner Losh 	if (pkt->th_opcode == ERROR) {
468e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "Got ERROR packet: %s", pkt->th_msg);
469e7ff5475SWarner Losh 		return (RP_ERROR);
470e7ff5475SWarner Losh 	}
471e7ff5475SWarner Losh 
472e7ff5475SWarner Losh 	if (debug&DEBUG_PACKETS)
473e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet",
474e7ff5475SWarner Losh 			n, packettype(pkt->th_opcode));
475e7ff5475SWarner Losh 
476e7ff5475SWarner Losh 	return n - 4;
477e7ff5475SWarner Losh }
478