tftp-io.c revision 212665
1207614Simp/*
2207614Simp * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
3207614Simp *
4207614Simp * Redistribution and use in source and binary forms, with or without
5207614Simp * modification, are permitted provided that the following conditions
6207614Simp * are met:
7207614Simp * 1. Redistributions of source code must retain the above copyright
8207614Simp *    notice, this list of conditions and the following disclaimer.
9207614Simp * 2. Redistributions in binary form must reproduce the above copyright
10207614Simp *    notice, this list of conditions and the following disclaimer in the
11207614Simp *    documentation and/or other materials provided with the distribution.
12207614Simp *
13207614Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14207614Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15207614Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16207614Simp * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17207614Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18207614Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19207614Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20207614Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21207614Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22207614Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23207614Simp * SUCH DAMAGE.
24207614Simp */
25207614Simp
26207614Simp#include <sys/cdefs.h>
27207614Simp__FBSDID("$FreeBSD: head/libexec/tftpd/tftp-io.c 212665 2010-09-15 15:38:47Z imp $");
28207614Simp
29207614Simp#include <sys/stat.h>
30207614Simp#include <sys/types.h>
31207614Simp#include <sys/socket.h>
32207614Simp
33207614Simp#include <netinet/in.h>
34207614Simp#include <arpa/tftp.h>
35207614Simp#include <arpa/inet.h>
36207614Simp
37207614Simp#include <errno.h>
38207614Simp#include <setjmp.h>
39207614Simp#include <signal.h>
40207614Simp#include <stdio.h>
41207614Simp#include <stdlib.h>
42207614Simp#include <string.h>
43207614Simp#include <syslog.h>
44207614Simp#include <unistd.h>
45207614Simp
46207614Simp#include "tftp-file.h"
47207614Simp#include "tftp-io.h"
48207614Simp#include "tftp-utils.h"
49207614Simp#include "tftp-options.h"
50207614Simp
51207614Simpstruct sockaddr_storage peer_sock;
52207614Simpstruct sockaddr_storage me_sock;
53207614Simp
54207614Simpstatic int send_packet(int peer, uint16_t block, char *pkt, int size);
55207614Simp
56207614Simpstruct errmsg {
57207614Simp	int	e_code;
58207614Simp	const char	*e_msg;
59207614Simp} errmsgs[] = {
60207614Simp	{ EUNDEF,	"Undefined error code" },
61207614Simp	{ ENOTFOUND,	"File not found" },
62207614Simp	{ EACCESS,	"Access violation" },
63207614Simp	{ ENOSPACE,	"Disk full or allocation exceeded" },
64207614Simp	{ EBADOP,	"Illegal TFTP operation" },
65207614Simp	{ EBADID,	"Unknown transfer ID" },
66207614Simp	{ EEXISTS,	"File already exists" },
67207614Simp	{ ENOUSER,	"No such user" },
68207614Simp	{ EOPTNEG,	"Option negotiation" },
69207614Simp	{ -1,		NULL }
70207614Simp};
71207614Simp
72207614Simp#define DROPPACKET(s)							\
73207614Simp	if (packetdroppercentage != 0 &&				\
74207614Simp	    random()%100 < packetdroppercentage) {			\
75207614Simp		tftp_log(LOG_DEBUG, "Artifical packet drop in %s", s);	\
76207614Simp		return;							\
77207614Simp	}
78207614Simp#define DROPPACKETn(s,n)						\
79207614Simp	if (packetdroppercentage != 0 &&				\
80207614Simp	    random()%100 < packetdroppercentage) {			\
81207614Simp		tftp_log(LOG_DEBUG, "Artifical packet drop in %s", s);	\
82207614Simp		return (n);						\
83207614Simp	}
84207614Simp
85207614Simpconst char *
86207614Simperrtomsg(int error)
87207614Simp{
88207614Simp	static char ebuf[40];
89207614Simp	struct errmsg *pe;
90207614Simp	char buf[MAXPKTSIZE];
91207614Simp
92207614Simp	if (error == 0)
93207614Simp		return ("success");
94207614Simp	for (pe = errmsgs; pe->e_code >= 0; pe++)
95207614Simp		if (pe->e_code == error)
96207614Simp			return (pe->e_msg);
97207614Simp	snprintf(ebuf, sizeof(buf), "error %d", error);
98207614Simp	return (ebuf);
99207614Simp}
100207614Simp
101207614Simpstatic int
102207614Simpsend_packet(int peer, uint16_t block, char *pkt, int size)
103207614Simp{
104207614Simp	int i;
105207614Simp	int t = 1;
106207614Simp
107207614Simp	for (i = 0; i < 12 ; i++) {
108207614Simp		DROPPACKETn("send_packet", 0);
109207614Simp
110207614Simp		if (sendto(peer, pkt, size, 0,
111207614Simp			(struct sockaddr *)&peer_sock, peer_sock.ss_len)
112207614Simp			== size) {
113207614Simp			if (i)
114207614Simp				tftp_log(LOG_ERR,
115207614Simp				    "%s block %d, attempt %d successful",
116207614Simp				    block, i);
117207614Simp			return (0);
118207614Simp		}
119207614Simp		tftp_log(LOG_ERR,
120207614Simp		    "%s block %d, attempt %d failed (Error %d: %s)",
121207614Simp		    packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)),
122207614Simp		    block, i, errno, strerror(errno));
123207614Simp		sleep(t);
124207614Simp		if (t < 32)
125207614Simp			t <<= 1;
126207614Simp	}
127207614Simp	tftp_log(LOG_ERR, "send_packet: %s", strerror(errno));
128207614Simp	return (1);
129207614Simp}
130207614Simp
131207614Simp/*
132207614Simp * Send an ERROR packet (error message).
133207614Simp * Error code passed in is one of the
134207614Simp * standard TFTP codes, or a UNIX errno
135207614Simp * offset by 100.
136207614Simp */
137207614Simpvoid
138207614Simpsend_error(int peer, int error)
139207614Simp{
140207614Simp	struct tftphdr *tp;
141207614Simp	int length;
142207614Simp	struct errmsg *pe;
143207614Simp	char buf[MAXPKTSIZE];
144207614Simp
145207614Simp	if (debug&DEBUG_PACKETS)
146207614Simp		tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error);
147207614Simp
148207614Simp	DROPPACKET("send_error");
149207614Simp
150207614Simp	tp = (struct tftphdr *)buf;
151207614Simp	tp->th_opcode = htons((u_short)ERROR);
152207614Simp	tp->th_code = htons((u_short)error);
153207614Simp	for (pe = errmsgs; pe->e_code >= 0; pe++)
154207614Simp		if (pe->e_code == error)
155207614Simp			break;
156207614Simp	if (pe->e_code < 0) {
157207614Simp		pe->e_msg = strerror(error - 100);
158207614Simp		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
159207614Simp	}
160207614Simp	strcpy(tp->th_msg, pe->e_msg);
161207614Simp	length = strlen(pe->e_msg);
162207614Simp	tp->th_msg[length] = '\0';
163207614Simp	length += 5;
164207614Simp
165207614Simp	if (debug&DEBUG_PACKETS)
166207614Simp		tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg);
167207614Simp
168207614Simp	if (sendto(peer, buf, length, 0,
169207614Simp		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != length)
170207614Simp		tftp_log(LOG_ERR, "send_error: %s", strerror(errno));
171207614Simp}
172207614Simp
173207614Simp/*
174207614Simp * Send an WRQ packet (write request).
175207614Simp */
176207614Simpint
177207614Simpsend_wrq(int peer, char *filename, char *mode)
178207614Simp{
179207614Simp	int n;
180207614Simp	struct tftphdr *tp;
181207614Simp	char *bp;
182207614Simp	char buf[MAXPKTSIZE];
183207614Simp	int size;
184207614Simp
185207614Simp	if (debug&DEBUG_PACKETS)
186207614Simp		tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'",
187207614Simp			filename, mode
188207614Simp		);
189207614Simp
190207614Simp	DROPPACKETn("send_wrq", 1);
191207614Simp
192207614Simp	tp = (struct tftphdr *)buf;
193207614Simp	tp->th_opcode = htons((u_short)WRQ);
194207614Simp	size = 2;
195207614Simp
196207614Simp	bp = tp->th_stuff;
197207614Simp	strcpy(bp, filename);
198207614Simp	bp += strlen(filename);
199207614Simp	*bp = 0;
200207614Simp	bp++;
201207614Simp	size += strlen(filename) + 1;
202207614Simp
203207614Simp	strcpy(bp, mode);
204207614Simp	bp += strlen(mode);
205207614Simp	*bp = 0;
206207614Simp	bp++;
207207614Simp	size += strlen(mode) + 1;
208207614Simp
209207614Simp	if (options_rfc_enabled)
210207614Simp		size += make_options(peer, bp, sizeof(buf) - size);
211207614Simp
212207614Simp	n = sendto(peer, buf, size, 0,
213207614Simp	    (struct sockaddr *)&peer_sock, peer_sock.ss_len);
214207614Simp	if (n != size) {
215207614Simp		tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno));
216207614Simp		return (1);
217207614Simp	}
218207614Simp	return (0);
219207614Simp}
220207614Simp
221207614Simp/*
222207614Simp * Send an RRQ packet (write request).
223207614Simp */
224207614Simpint
225207614Simpsend_rrq(int peer, char *filename, char *mode)
226207614Simp{
227207614Simp	int n;
228207614Simp	struct tftphdr *tp;
229207614Simp	char *bp;
230207614Simp	char buf[MAXPKTSIZE];
231207614Simp	int size;
232207614Simp
233207614Simp	if (debug&DEBUG_PACKETS)
234207614Simp		tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'",
235207614Simp			filename, mode
236207614Simp		);
237207614Simp
238207614Simp	DROPPACKETn("send_rrq", 1);
239207614Simp
240207614Simp	tp = (struct tftphdr *)buf;
241207614Simp	tp->th_opcode = htons((u_short)RRQ);
242207614Simp	size = 2;
243207614Simp
244207614Simp	bp = tp->th_stuff;
245207614Simp	strcpy(bp, filename);
246207614Simp	bp += strlen(filename);
247207614Simp	*bp = 0;
248207614Simp	bp++;
249207614Simp	size += strlen(filename) + 1;
250207614Simp
251207614Simp	strcpy(bp, mode);
252207614Simp	bp += strlen(mode);
253207614Simp	*bp = 0;
254207614Simp	bp++;
255207614Simp	size += strlen(mode) + 1;
256207614Simp
257207614Simp	if (options_rfc_enabled) {
258207614Simp		options[OPT_TSIZE].o_request = strdup("0");
259207614Simp		size += make_options(peer, bp, sizeof(buf) - size);
260207614Simp	}
261207614Simp
262207614Simp	n = sendto(peer, buf, size, 0,
263207614Simp	    (struct sockaddr *)&peer_sock, peer_sock.ss_len);
264207614Simp	if (n != size) {
265207614Simp		tftp_log(LOG_ERR, "send_rrq: %s", n, strerror(errno));
266207614Simp		return (1);
267207614Simp	}
268207614Simp	return (0);
269207614Simp}
270207614Simp
271207614Simp/*
272207614Simp * Send an OACK packet (option acknowledgement).
273207614Simp */
274207614Simpint
275207614Simpsend_oack(int peer)
276207614Simp{
277207614Simp	struct tftphdr *tp;
278207614Simp	int size, i, n;
279207614Simp	char *bp;
280207614Simp	char buf[MAXPKTSIZE];
281207614Simp
282207614Simp	if (debug&DEBUG_PACKETS)
283207614Simp		tftp_log(LOG_DEBUG, "Sending OACK");
284207614Simp
285207614Simp	DROPPACKETn("send_oack", 0);
286207614Simp
287207614Simp	/*
288207614Simp	 * Send back an options acknowledgement (only the ones with
289207614Simp	 * a reply for)
290207614Simp	 */
291207614Simp	tp = (struct tftphdr *)buf;
292207614Simp	bp = buf + 2;
293207614Simp	size = sizeof(buf) - 2;
294207614Simp	tp->th_opcode = htons((u_short)OACK);
295207614Simp	for (i = 0; options[i].o_type != NULL; i++) {
296207614Simp		if (options[i].o_reply != NULL) {
297207614Simp			n = snprintf(bp, size, "%s%c%s", options[i].o_type,
298207614Simp				     0, options[i].o_reply);
299207614Simp			bp += n+1;
300207614Simp			size -= n+1;
301207614Simp			if (size < 0) {
302207614Simp				tftp_log(LOG_ERR, "oack: buffer overflow");
303207614Simp				exit(1);
304207614Simp			}
305207614Simp		}
306207614Simp	}
307207614Simp	size = bp - buf;
308207614Simp
309207614Simp	if (sendto(peer, buf, size, 0,
310207614Simp		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
311207614Simp		tftp_log(LOG_INFO, "send_oack: %s", strerror(errno));
312207614Simp		return (1);
313207614Simp	}
314207614Simp
315207614Simp	return (0);
316207614Simp}
317207614Simp
318207614Simp/*
319207614Simp * Send an ACK packet (acknowledgement).
320207614Simp */
321207614Simpint
322207614Simpsend_ack(int fp, uint16_t block)
323207614Simp{
324207614Simp	struct tftphdr *tp;
325207614Simp	int size;
326207614Simp	char *bp;
327207614Simp	char buf[MAXPKTSIZE];
328207614Simp
329207614Simp	if (debug&DEBUG_PACKETS)
330207614Simp		tftp_log(LOG_DEBUG, "Sending ACK for block %d", block);
331207614Simp
332207614Simp	DROPPACKETn("send_ack", 0);
333207614Simp
334207614Simp	tp = (struct tftphdr *)buf;
335207614Simp	bp = buf + 2;
336207614Simp	size = sizeof(buf) - 2;
337207614Simp	tp->th_opcode = htons((u_short)ACK);
338207614Simp	tp->th_block = htons((u_short)block);
339207614Simp	size = 4;
340207614Simp
341207614Simp	if (sendto(fp, buf, size, 0,
342207614Simp	    (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
343207614Simp		tftp_log(LOG_INFO, "send_ack: %s", strerror(errno));
344207614Simp		return (1);
345207614Simp	}
346207614Simp
347207614Simp	return (0);
348207614Simp}
349207614Simp
350207614Simp/*
351207614Simp * Send a DATA packet
352207614Simp */
353207614Simpint
354207614Simpsend_data(int peer, uint16_t block, char *data, int size)
355207614Simp{
356207614Simp	char buf[MAXPKTSIZE];
357207614Simp	struct tftphdr *pkt;
358207614Simp	int n;
359207614Simp
360207614Simp	if (debug&DEBUG_PACKETS)
361207614Simp		tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes",
362207614Simp			block, size);
363207614Simp
364207614Simp	DROPPACKETn("send_data", 0);
365207614Simp
366207614Simp	pkt = (struct tftphdr *)buf;
367207614Simp
368207614Simp	pkt->th_opcode = htons((u_short)DATA);
369207614Simp	pkt->th_block = htons((u_short)block);
370207614Simp	memcpy(pkt->th_data, data, size);
371207614Simp
372207614Simp	n = send_packet(peer, block, (char *)pkt, size + 4);
373207614Simp	return (n);
374207614Simp}
375207614Simp
376207614Simp
377207614Simp/*
378207614Simp * Receive a packet
379207614Simp */
380207614Simpjmp_buf	timeoutbuf;
381207614Simp
382207614Simpstatic void
383207614Simptimeout(int sig __unused)
384207614Simp{
385207614Simp
386207614Simp	/* tftp_log(LOG_DEBUG, "Timeout\n");	Inside a signal handler... */
387207614Simp	longjmp(timeoutbuf, 1);
388207614Simp}
389207614Simp
390207614Simpint
391207614Simpreceive_packet(int peer, char *data, int size, struct sockaddr_storage *from,
392207614Simp    int thistimeout)
393207614Simp{
394207614Simp	struct tftphdr *pkt;
395207614Simp	struct sockaddr_storage from_local;
396207614Simp	struct sockaddr_storage *pfrom;
397207614Simp	socklen_t fromlen;
398207614Simp	int n;
399207614Simp	static int waiting;
400207614Simp
401207614Simp	if (debug&DEBUG_PACKETS)
402207614Simp		tftp_log(LOG_DEBUG,
403207614Simp		    "Waiting %d seconds for packet", timeoutpacket);
404207614Simp
405207614Simp	pkt = (struct tftphdr *)data;
406207614Simp
407207614Simp	waiting = 0;
408207614Simp	signal(SIGALRM, timeout);
409207614Simp	setjmp(timeoutbuf);
410207614Simp	alarm(thistimeout);
411207614Simp
412207614Simp	if (waiting > 0) {
413207614Simp		alarm(0);
414207614Simp		return (RP_TIMEOUT);
415207614Simp	}
416207614Simp
417207614Simp	if (waiting > 0) {
418207614Simp		tftp_log(LOG_ERR, "receive_packet: timeout");
419207614Simp		alarm(0);
420207614Simp		return (RP_TIMEOUT);
421207614Simp	}
422207614Simp
423207614Simp	waiting++;
424212665Simp	pfrom = (from == NULL) ? &from_local : from;
425207614Simp	fromlen = sizeof(*pfrom);
426207614Simp	n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen);
427207614Simp
428207614Simp	alarm(0);
429207614Simp
430207614Simp	DROPPACKETn("receive_packet", RP_TIMEOUT);
431207614Simp
432207614Simp	if (n < 0) {
433207614Simp		tftp_log(LOG_ERR, "receive_packet: timeout");
434207614Simp		return (RP_TIMEOUT);
435207614Simp	}
436207614Simp
437207614Simp	alarm(0);
438207614Simp
439207614Simp	if (n < 0) {
440207614Simp		/* No idea what could have happened if it isn't a timeout */
441207614Simp		tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno));
442207614Simp		return (RP_RECVFROM);
443207614Simp	}
444207614Simp	if (n < 4) {
445207614Simp		tftp_log(LOG_ERR,
446207614Simp		    "receive_packet: packet too small (%d bytes)", n);
447207614Simp		return (RP_TOOSMALL);
448207614Simp	}
449207614Simp
450207614Simp	pkt->th_opcode = ntohs((u_short)pkt->th_opcode);
451207614Simp	if (pkt->th_opcode == DATA ||
452207614Simp	    pkt->th_opcode == ACK)
453207614Simp		pkt->th_block = ntohs((u_short)pkt->th_block);
454207614Simp
455207614Simp	if (pkt->th_opcode == DATA && n > pktsize) {
456207614Simp		tftp_log(LOG_ERR, "receive_packet: packet too big");
457207614Simp		return (RP_TOOBIG);
458207614Simp	}
459207614Simp
460207614Simp	if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr !=
461207614Simp	    ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) {
462207614Simp		tftp_log(LOG_ERR,
463207614Simp			"receive_packet: received packet from wrong source");
464207614Simp		return (RP_WRONGSOURCE);
465207614Simp	}
466207614Simp
467207614Simp	if (pkt->th_opcode == ERROR) {
468207614Simp		tftp_log(LOG_ERR, "Got ERROR packet: %s", pkt->th_msg);
469207614Simp		return (RP_ERROR);
470207614Simp	}
471207614Simp
472207614Simp	if (debug&DEBUG_PACKETS)
473207614Simp		tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet",
474207614Simp			n, packettype(pkt->th_opcode));
475207614Simp
476207614Simp	return n - 4;
477207614Simp}
478