xref: /freebsd/libexec/tftpd/tftp-io.c (revision 4d09eb87c5d5bec2e2832f50537e2ce6f75f4117)
1e6209940SPedro F. Giffuni /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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/types.h>
29e7ff5475SWarner Losh #include <sys/socket.h>
30eb0292d9SDag-Erling Smørgrav #include <sys/stat.h>
31e7ff5475SWarner Losh 
32e7ff5475SWarner Losh #include <netinet/in.h>
33e7ff5475SWarner Losh #include <arpa/inet.h>
34eb0292d9SDag-Erling Smørgrav #include <arpa/tftp.h>
35e7ff5475SWarner Losh 
363c0fa265SAlan Somers #include <assert.h>
37e7ff5475SWarner Losh #include <errno.h>
38e3b4cb1bSDag-Erling Smørgrav #include <poll.h>
39ca2d3691SAlan Somers #include <stddef.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 &&				\
74*4d09eb87SDag-Erling Smørgrav 	    arc4random()%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 &&				\
80*4d09eb87SDag-Erling Smørgrav 	    arc4random()%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 *
errtomsg(int error)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
send_packet(int peer,uint16_t block,char * pkt,int size)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 
109b713097aSMarius Strobl 		if (sendto(peer, pkt, size, 0, (struct sockaddr *)&peer_sock,
110b713097aSMarius Strobl 		    peer_sock.ss_len) == size) {
111e7ff5475SWarner Losh 			if (i)
112e7ff5475SWarner Losh 				tftp_log(LOG_ERR,
113e7ff5475SWarner Losh 				    "%s block %d, attempt %d successful",
114b713097aSMarius Strobl 		    		    packettype(ntohs(((struct tftphdr *)
115b713097aSMarius 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
send_error(int peer,int error)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 	}
159*4d09eb87SDag-Erling Smørgrav 	snprintf(tp->th_msg, MAXPKTSIZE - 4, "%s%n", pe->e_msg, &length);
160*4d09eb87SDag-Erling Smørgrav 	length += 5; /* header and terminator */
161e7ff5475SWarner Losh 
162e7ff5475SWarner Losh 	if (debug & DEBUG_PACKETS)
163e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg);
164e7ff5475SWarner Losh 
165e7ff5475SWarner Losh 	if (sendto(peer, buf, length, 0,
166e7ff5475SWarner Losh 		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != length)
167e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "send_error: %s", strerror(errno));
168e7ff5475SWarner Losh }
169e7ff5475SWarner Losh 
170e7ff5475SWarner Losh /*
171e7ff5475SWarner Losh  * Send an WRQ packet (write request).
172e7ff5475SWarner Losh  */
173e7ff5475SWarner Losh int
send_wrq(int peer,char * filename,char * mode)174e7ff5475SWarner Losh send_wrq(int peer, char *filename, char *mode)
175e7ff5475SWarner Losh {
176e7ff5475SWarner Losh 	int n;
177e7ff5475SWarner Losh 	struct tftphdr *tp;
178e7ff5475SWarner Losh 	char *bp;
179e7ff5475SWarner Losh 	char buf[MAXPKTSIZE];
180e7ff5475SWarner Losh 	int size;
181e7ff5475SWarner Losh 
182e7ff5475SWarner Losh 	if (debug & DEBUG_PACKETS)
183e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'",
184e7ff5475SWarner Losh 			filename, mode
185e7ff5475SWarner Losh 		);
186e7ff5475SWarner Losh 
187a1c4a3eaSMichael Tuexen 	DROPPACKETn("send_wrq", 0);
188e7ff5475SWarner Losh 
189e7ff5475SWarner Losh 	tp = (struct tftphdr *)buf;
190e7ff5475SWarner Losh 	tp->th_opcode = htons((u_short)WRQ);
191ca2d3691SAlan Somers 	size = offsetof(struct tftphdr, th_stuff);
192e7ff5475SWarner Losh 
193e7ff5475SWarner Losh 	bp = tp->th_stuff;
194ca2d3691SAlan Somers 	strlcpy(bp, filename, sizeof(buf) - size);
195e7ff5475SWarner Losh 	bp += strlen(filename);
196e7ff5475SWarner Losh 	*bp = 0;
197e7ff5475SWarner Losh 	bp++;
198e7ff5475SWarner Losh 	size += strlen(filename) + 1;
199e7ff5475SWarner Losh 
200ca2d3691SAlan Somers 	strlcpy(bp, mode, sizeof(buf) - size);
201e7ff5475SWarner Losh 	bp += strlen(mode);
202e7ff5475SWarner Losh 	*bp = 0;
203e7ff5475SWarner Losh 	bp++;
204e7ff5475SWarner Losh 	size += strlen(mode) + 1;
205e7ff5475SWarner Losh 
206e7ff5475SWarner Losh 	if (options_rfc_enabled)
207e7ff5475SWarner Losh 		size += make_options(peer, bp, sizeof(buf) - size);
208e7ff5475SWarner Losh 
209e7ff5475SWarner Losh 	n = sendto(peer, buf, size, 0,
210e7ff5475SWarner Losh 	    (struct sockaddr *)&peer_sock, peer_sock.ss_len);
211e7ff5475SWarner Losh 	if (n != size) {
212e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno));
213e7ff5475SWarner Losh 		return (1);
214e7ff5475SWarner Losh 	}
215e7ff5475SWarner Losh 	return (0);
216e7ff5475SWarner Losh }
217e7ff5475SWarner Losh 
218e7ff5475SWarner Losh /*
219e7ff5475SWarner Losh  * Send an RRQ packet (write request).
220e7ff5475SWarner Losh  */
221e7ff5475SWarner Losh int
send_rrq(int peer,char * filename,char * mode)222e7ff5475SWarner Losh send_rrq(int peer, char *filename, char *mode)
223e7ff5475SWarner Losh {
224e7ff5475SWarner Losh 	int n;
225e7ff5475SWarner Losh 	struct tftphdr *tp;
226e7ff5475SWarner Losh 	char *bp;
227e7ff5475SWarner Losh 	char buf[MAXPKTSIZE];
228e7ff5475SWarner Losh 	int size;
229e7ff5475SWarner Losh 
230e7ff5475SWarner Losh 	if (debug & DEBUG_PACKETS)
231e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'",
232e7ff5475SWarner Losh 			filename, mode
233e7ff5475SWarner Losh 		);
234e7ff5475SWarner Losh 
235a1c4a3eaSMichael Tuexen 	DROPPACKETn("send_rrq", 0);
236e7ff5475SWarner Losh 
237e7ff5475SWarner Losh 	tp = (struct tftphdr *)buf;
238e7ff5475SWarner Losh 	tp->th_opcode = htons((u_short)RRQ);
239ca2d3691SAlan Somers 	size = offsetof(struct tftphdr, th_stuff);
240e7ff5475SWarner Losh 
241e7ff5475SWarner Losh 	bp = tp->th_stuff;
242ca2d3691SAlan Somers 	strlcpy(bp, filename, sizeof(buf) - size);
243e7ff5475SWarner Losh 	bp += strlen(filename);
244e7ff5475SWarner Losh 	*bp = 0;
245e7ff5475SWarner Losh 	bp++;
246e7ff5475SWarner Losh 	size += strlen(filename) + 1;
247e7ff5475SWarner Losh 
248ca2d3691SAlan Somers 	strlcpy(bp, mode, sizeof(buf) - size);
249e7ff5475SWarner Losh 	bp += strlen(mode);
250e7ff5475SWarner Losh 	*bp = 0;
251e7ff5475SWarner Losh 	bp++;
252e7ff5475SWarner Losh 	size += strlen(mode) + 1;
253e7ff5475SWarner Losh 
254e7ff5475SWarner Losh 	if (options_rfc_enabled) {
255b15e052eSDag-Erling Smørgrav 		options_set_request(OPT_TSIZE, "0");
256e7ff5475SWarner Losh 		size += make_options(peer, bp, sizeof(buf) - size);
257e7ff5475SWarner Losh 	}
258e7ff5475SWarner Losh 
259e7ff5475SWarner Losh 	n = sendto(peer, buf, size, 0,
260e7ff5475SWarner Losh 	    (struct sockaddr *)&peer_sock, peer_sock.ss_len);
261e7ff5475SWarner Losh 	if (n != size) {
2623b6bd978SCraig Rodrigues 		tftp_log(LOG_ERR, "send_rrq: %d %s", n, strerror(errno));
263e7ff5475SWarner Losh 		return (1);
264e7ff5475SWarner Losh 	}
265e7ff5475SWarner Losh 	return (0);
266e7ff5475SWarner Losh }
267e7ff5475SWarner Losh 
268e7ff5475SWarner Losh /*
269e7ff5475SWarner Losh  * Send an OACK packet (option acknowledgement).
270e7ff5475SWarner Losh  */
271e7ff5475SWarner Losh int
send_oack(int peer)272e7ff5475SWarner Losh send_oack(int peer)
273e7ff5475SWarner Losh {
274e7ff5475SWarner Losh 	struct tftphdr *tp;
275e7ff5475SWarner Losh 	int size, i, n;
276e7ff5475SWarner Losh 	char *bp;
277e7ff5475SWarner Losh 	char buf[MAXPKTSIZE];
278e7ff5475SWarner Losh 
279e7ff5475SWarner Losh 	if (debug & DEBUG_PACKETS)
280e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending OACK");
281e7ff5475SWarner Losh 
282e7ff5475SWarner Losh 	DROPPACKETn("send_oack", 0);
283e7ff5475SWarner Losh 
284e7ff5475SWarner Losh 	/*
285e7ff5475SWarner Losh 	 * Send back an options acknowledgement (only the ones with
286e7ff5475SWarner Losh 	 * a reply for)
287e7ff5475SWarner Losh 	 */
288e7ff5475SWarner Losh 	tp = (struct tftphdr *)buf;
289e7ff5475SWarner Losh 	bp = buf + 2;
290e7ff5475SWarner Losh 	size = sizeof(buf) - 2;
291e7ff5475SWarner Losh 	tp->th_opcode = htons((u_short)OACK);
292e7ff5475SWarner Losh 	for (i = 0; options[i].o_type != NULL; i++) {
293e7ff5475SWarner Losh 		if (options[i].o_reply != NULL) {
294e7ff5475SWarner Losh 			n = snprintf(bp, size, "%s%c%s", options[i].o_type,
295e7ff5475SWarner Losh 				     0, options[i].o_reply);
296e7ff5475SWarner Losh 			bp += n+1;
297e7ff5475SWarner Losh 			size -= n+1;
298e7ff5475SWarner Losh 			if (size < 0) {
299e7ff5475SWarner Losh 				tftp_log(LOG_ERR, "oack: buffer overflow");
300e7ff5475SWarner Losh 				exit(1);
301e7ff5475SWarner Losh 			}
302e7ff5475SWarner Losh 		}
303e7ff5475SWarner Losh 	}
304e7ff5475SWarner Losh 	size = bp - buf;
305e7ff5475SWarner Losh 
306e7ff5475SWarner Losh 	if (sendto(peer, buf, size, 0,
307e7ff5475SWarner Losh 		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
308e7ff5475SWarner Losh 		tftp_log(LOG_INFO, "send_oack: %s", strerror(errno));
309e7ff5475SWarner Losh 		return (1);
310e7ff5475SWarner Losh 	}
311e7ff5475SWarner Losh 
312e7ff5475SWarner Losh 	return (0);
313e7ff5475SWarner Losh }
314e7ff5475SWarner Losh 
315e7ff5475SWarner Losh /*
316e7ff5475SWarner Losh  * Send an ACK packet (acknowledgement).
317e7ff5475SWarner Losh  */
318e7ff5475SWarner Losh int
send_ack(int fp,uint16_t block)319e7ff5475SWarner Losh send_ack(int fp, uint16_t block)
320e7ff5475SWarner Losh {
321e7ff5475SWarner Losh 	struct tftphdr *tp;
322e7ff5475SWarner Losh 	int size;
323e7ff5475SWarner Losh 	char buf[MAXPKTSIZE];
324e7ff5475SWarner Losh 
325e7ff5475SWarner Losh 	if (debug & DEBUG_PACKETS)
326e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending ACK for block %d", block);
327e7ff5475SWarner Losh 
328e7ff5475SWarner Losh 	DROPPACKETn("send_ack", 0);
329e7ff5475SWarner Losh 
330e7ff5475SWarner Losh 	tp = (struct tftphdr *)buf;
331e7ff5475SWarner Losh 	tp->th_opcode = htons((u_short)ACK);
332e7ff5475SWarner Losh 	tp->th_block = htons((u_short)block);
333e7ff5475SWarner Losh 	size = 4;
334e7ff5475SWarner Losh 
335e7ff5475SWarner Losh 	if (sendto(fp, buf, size, 0,
336e7ff5475SWarner Losh 	    (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
337e7ff5475SWarner Losh 		tftp_log(LOG_INFO, "send_ack: %s", strerror(errno));
338e7ff5475SWarner Losh 		return (1);
339e7ff5475SWarner Losh 	}
340e7ff5475SWarner Losh 
341e7ff5475SWarner Losh 	return (0);
342e7ff5475SWarner Losh }
343e7ff5475SWarner Losh 
344e7ff5475SWarner Losh /*
345e7ff5475SWarner Losh  * Send a DATA packet
346e7ff5475SWarner Losh  */
347e7ff5475SWarner Losh int
send_data(int peer,uint16_t block,char * data,int size)348e7ff5475SWarner Losh send_data(int peer, uint16_t block, char *data, int size)
349e7ff5475SWarner Losh {
350e7ff5475SWarner Losh 	char buf[MAXPKTSIZE];
351e7ff5475SWarner Losh 	struct tftphdr *pkt;
352e7ff5475SWarner Losh 	int n;
353e7ff5475SWarner Losh 
354e7ff5475SWarner Losh 	if (debug & DEBUG_PACKETS)
355e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes",
356e7ff5475SWarner Losh 			block, size);
357e7ff5475SWarner Losh 
358e7ff5475SWarner Losh 	DROPPACKETn("send_data", 0);
359e7ff5475SWarner Losh 
360e7ff5475SWarner Losh 	pkt = (struct tftphdr *)buf;
361e7ff5475SWarner Losh 
362e7ff5475SWarner Losh 	pkt->th_opcode = htons((u_short)DATA);
363e7ff5475SWarner Losh 	pkt->th_block = htons((u_short)block);
364e7ff5475SWarner Losh 	memcpy(pkt->th_data, data, size);
365e7ff5475SWarner Losh 
366e7ff5475SWarner Losh 	n = send_packet(peer, block, (char *)pkt, size + 4);
367e7ff5475SWarner Losh 	return (n);
368e7ff5475SWarner Losh }
369e7ff5475SWarner Losh 
370e7ff5475SWarner Losh 
371e7ff5475SWarner Losh /*
372e7ff5475SWarner Losh  * Receive a packet
373e3b4cb1bSDag-Erling Smørgrav  *
374e3b4cb1bSDag-Erling Smørgrav  * If timeout is negative, no error will be logged on timeout.
375e7ff5475SWarner Losh  */
376e7ff5475SWarner Losh int
receive_packet(int peer,char * data,int size,struct sockaddr_storage * from,int timeout)377e7ff5475SWarner Losh receive_packet(int peer, char *data, int size, struct sockaddr_storage *from,
378e3b4cb1bSDag-Erling Smørgrav     int timeout)
379e7ff5475SWarner Losh {
380e3b4cb1bSDag-Erling Smørgrav 	struct pollfd pfd;
381e7ff5475SWarner Losh 	struct tftphdr *pkt;
382e7ff5475SWarner Losh 	struct sockaddr_storage from_local;
383e7ff5475SWarner Losh 	struct sockaddr_storage *pfrom;
384e7ff5475SWarner Losh 	socklen_t fromlen;
385e7ff5475SWarner Losh 	int n;
386e7ff5475SWarner Losh 
387e7ff5475SWarner Losh 	if (debug & DEBUG_PACKETS)
388e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG,
389e7ff5475SWarner Losh 		    "Waiting %d seconds for packet", timeoutpacket);
390e7ff5475SWarner Losh 
391e7ff5475SWarner Losh 	pkt = (struct tftphdr *)data;
392e7ff5475SWarner Losh 
393e3b4cb1bSDag-Erling Smørgrav 	pfd.fd = peer;
394e3b4cb1bSDag-Erling Smørgrav 	pfd.events = POLLIN;
395e3b4cb1bSDag-Erling Smørgrav 	if (poll(&pfd, 1, 1000 * (timeout < 0 ? -timeout : timeout)) < 1) {
396e3b4cb1bSDag-Erling Smørgrav 		if (timeout > 0)
397e7ff5475SWarner Losh 			tftp_log(LOG_ERR, "receive_packet: timeout");
398e7ff5475SWarner Losh 		return (RP_TIMEOUT);
399e7ff5475SWarner Losh 	}
400e7ff5475SWarner Losh 
4013dc2fd3fSWarner Losh 	pfrom = (from == NULL) ? &from_local : from;
402e7ff5475SWarner Losh 	fromlen = sizeof(*pfrom);
403e7ff5475SWarner Losh 	n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen);
404e7ff5475SWarner Losh 
405e7ff5475SWarner Losh 	DROPPACKETn("receive_packet", RP_TIMEOUT);
406e7ff5475SWarner Losh 
407e7ff5475SWarner Losh 	if (n < 0) {
408e7ff5475SWarner Losh 		/* No idea what could have happened if it isn't a timeout */
409e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno));
410e7ff5475SWarner Losh 		return (RP_RECVFROM);
411e7ff5475SWarner Losh 	}
412e7ff5475SWarner Losh 	if (n < 4) {
413e7ff5475SWarner Losh 		tftp_log(LOG_ERR,
414e7ff5475SWarner Losh 		    "receive_packet: packet too small (%d bytes)", n);
415e7ff5475SWarner Losh 		return (RP_TOOSMALL);
416e7ff5475SWarner Losh 	}
417e7ff5475SWarner Losh 
418e7ff5475SWarner Losh 	pkt->th_opcode = ntohs((u_short)pkt->th_opcode);
419e7ff5475SWarner Losh 	if (pkt->th_opcode == DATA ||
420e7ff5475SWarner Losh 	    pkt->th_opcode == ACK)
421e7ff5475SWarner Losh 		pkt->th_block = ntohs((u_short)pkt->th_block);
422e7ff5475SWarner Losh 
423e7ff5475SWarner Losh 	if (pkt->th_opcode == DATA && n > pktsize) {
424e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "receive_packet: packet too big");
425e7ff5475SWarner Losh 		return (RP_TOOBIG);
426e7ff5475SWarner Losh 	}
427e7ff5475SWarner Losh 
428e7ff5475SWarner Losh 	if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr !=
429e7ff5475SWarner Losh 	    ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) {
430e7ff5475SWarner Losh 		tftp_log(LOG_ERR,
431e7ff5475SWarner Losh 			"receive_packet: received packet from wrong source");
432e7ff5475SWarner Losh 		return (RP_WRONGSOURCE);
433e7ff5475SWarner Losh 	}
434e7ff5475SWarner Losh 
435e7ff5475SWarner Losh 	if (pkt->th_opcode == ERROR) {
4369a2856b4SEd Maste 		tftp_log(pkt->th_code == EUNDEF ? LOG_DEBUG : LOG_ERR,
4379a2856b4SEd Maste 		    "Got ERROR packet: %s", pkt->th_msg);
438e7ff5475SWarner Losh 		return (RP_ERROR);
439e7ff5475SWarner Losh 	}
440e7ff5475SWarner Losh 
441e7ff5475SWarner Losh 	if (debug & DEBUG_PACKETS)
442e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet",
443e7ff5475SWarner Losh 			n, packettype(pkt->th_opcode));
444e7ff5475SWarner Losh 
445e7ff5475SWarner Losh 	return n - 4;
446e7ff5475SWarner Losh }
447