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$");
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
56241720Sedstatic struct 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) {			\
75229780Suqs		tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s);	\
76207614Simp		return;							\
77207614Simp	}
78207614Simp#define DROPPACKETn(s,n)						\
79207614Simp	if (packetdroppercentage != 0 &&				\
80207614Simp	    random()%100 < packetdroppercentage) {			\
81229780Suqs		tftp_log(LOG_DEBUG, "Artificial 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
91207614Simp	if (error == 0)
92207614Simp		return ("success");
93207614Simp	for (pe = errmsgs; pe->e_code >= 0; pe++)
94207614Simp		if (pe->e_code == error)
95207614Simp			return (pe->e_msg);
96244686Santoine	snprintf(ebuf, sizeof(ebuf), "error %d", error);
97207614Simp	return (ebuf);
98207614Simp}
99207614Simp
100207614Simpstatic int
101207614Simpsend_packet(int peer, uint16_t block, char *pkt, int size)
102207614Simp{
103207614Simp	int i;
104207614Simp	int t = 1;
105207614Simp
106207614Simp	for (i = 0; i < 12 ; i++) {
107207614Simp		DROPPACKETn("send_packet", 0);
108207614Simp
109246139Smarius		if (sendto(peer, pkt, size, 0, (struct sockaddr *)&peer_sock,
110246139Smarius		    peer_sock.ss_len) == size) {
111207614Simp			if (i)
112207614Simp				tftp_log(LOG_ERR,
113207614Simp				    "%s block %d, attempt %d successful",
114246139Smarius		    		    packettype(ntohs(((struct tftphdr *)
115246139Smarius				    (pkt))->th_opcode)), block, i);
116207614Simp			return (0);
117207614Simp		}
118207614Simp		tftp_log(LOG_ERR,
119207614Simp		    "%s block %d, attempt %d failed (Error %d: %s)",
120207614Simp		    packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)),
121207614Simp		    block, i, errno, strerror(errno));
122207614Simp		sleep(t);
123207614Simp		if (t < 32)
124207614Simp			t <<= 1;
125207614Simp	}
126207614Simp	tftp_log(LOG_ERR, "send_packet: %s", strerror(errno));
127207614Simp	return (1);
128207614Simp}
129207614Simp
130207614Simp/*
131207614Simp * Send an ERROR packet (error message).
132207614Simp * Error code passed in is one of the
133207614Simp * standard TFTP codes, or a UNIX errno
134207614Simp * offset by 100.
135207614Simp */
136207614Simpvoid
137207614Simpsend_error(int peer, int error)
138207614Simp{
139207614Simp	struct tftphdr *tp;
140207614Simp	int length;
141207614Simp	struct errmsg *pe;
142207614Simp	char buf[MAXPKTSIZE];
143207614Simp
144207614Simp	if (debug&DEBUG_PACKETS)
145246106Ssbruno		tftp_log(LOG_DEBUG, "Sending ERROR %d", error);
146207614Simp
147207614Simp	DROPPACKET("send_error");
148207614Simp
149207614Simp	tp = (struct tftphdr *)buf;
150207614Simp	tp->th_opcode = htons((u_short)ERROR);
151207614Simp	tp->th_code = htons((u_short)error);
152207614Simp	for (pe = errmsgs; pe->e_code >= 0; pe++)
153207614Simp		if (pe->e_code == error)
154207614Simp			break;
155207614Simp	if (pe->e_code < 0) {
156207614Simp		pe->e_msg = strerror(error - 100);
157207614Simp		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
158207614Simp	}
159207614Simp	strcpy(tp->th_msg, pe->e_msg);
160207614Simp	length = strlen(pe->e_msg);
161207614Simp	tp->th_msg[length] = '\0';
162207614Simp	length += 5;
163207614Simp
164207614Simp	if (debug&DEBUG_PACKETS)
165207614Simp		tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg);
166207614Simp
167207614Simp	if (sendto(peer, buf, length, 0,
168207614Simp		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != length)
169207614Simp		tftp_log(LOG_ERR, "send_error: %s", strerror(errno));
170207614Simp}
171207614Simp
172207614Simp/*
173207614Simp * Send an WRQ packet (write request).
174207614Simp */
175207614Simpint
176207614Simpsend_wrq(int peer, char *filename, char *mode)
177207614Simp{
178207614Simp	int n;
179207614Simp	struct tftphdr *tp;
180207614Simp	char *bp;
181207614Simp	char buf[MAXPKTSIZE];
182207614Simp	int size;
183207614Simp
184207614Simp	if (debug&DEBUG_PACKETS)
185207614Simp		tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'",
186207614Simp			filename, mode
187207614Simp		);
188207614Simp
189207614Simp	DROPPACKETn("send_wrq", 1);
190207614Simp
191207614Simp	tp = (struct tftphdr *)buf;
192207614Simp	tp->th_opcode = htons((u_short)WRQ);
193207614Simp	size = 2;
194207614Simp
195207614Simp	bp = tp->th_stuff;
196207614Simp	strcpy(bp, filename);
197207614Simp	bp += strlen(filename);
198207614Simp	*bp = 0;
199207614Simp	bp++;
200207614Simp	size += strlen(filename) + 1;
201207614Simp
202207614Simp	strcpy(bp, mode);
203207614Simp	bp += strlen(mode);
204207614Simp	*bp = 0;
205207614Simp	bp++;
206207614Simp	size += strlen(mode) + 1;
207207614Simp
208207614Simp	if (options_rfc_enabled)
209207614Simp		size += make_options(peer, bp, sizeof(buf) - size);
210207614Simp
211207614Simp	n = sendto(peer, buf, size, 0,
212207614Simp	    (struct sockaddr *)&peer_sock, peer_sock.ss_len);
213207614Simp	if (n != size) {
214207614Simp		tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno));
215207614Simp		return (1);
216207614Simp	}
217207614Simp	return (0);
218207614Simp}
219207614Simp
220207614Simp/*
221207614Simp * Send an RRQ packet (write request).
222207614Simp */
223207614Simpint
224207614Simpsend_rrq(int peer, char *filename, char *mode)
225207614Simp{
226207614Simp	int n;
227207614Simp	struct tftphdr *tp;
228207614Simp	char *bp;
229207614Simp	char buf[MAXPKTSIZE];
230207614Simp	int size;
231207614Simp
232207614Simp	if (debug&DEBUG_PACKETS)
233207614Simp		tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'",
234207614Simp			filename, mode
235207614Simp		);
236207614Simp
237207614Simp	DROPPACKETn("send_rrq", 1);
238207614Simp
239207614Simp	tp = (struct tftphdr *)buf;
240207614Simp	tp->th_opcode = htons((u_short)RRQ);
241207614Simp	size = 2;
242207614Simp
243207614Simp	bp = tp->th_stuff;
244207614Simp	strcpy(bp, filename);
245207614Simp	bp += strlen(filename);
246207614Simp	*bp = 0;
247207614Simp	bp++;
248207614Simp	size += strlen(filename) + 1;
249207614Simp
250207614Simp	strcpy(bp, mode);
251207614Simp	bp += strlen(mode);
252207614Simp	*bp = 0;
253207614Simp	bp++;
254207614Simp	size += strlen(mode) + 1;
255207614Simp
256207614Simp	if (options_rfc_enabled) {
257207614Simp		options[OPT_TSIZE].o_request = strdup("0");
258207614Simp		size += make_options(peer, bp, sizeof(buf) - size);
259207614Simp	}
260207614Simp
261207614Simp	n = sendto(peer, buf, size, 0,
262207614Simp	    (struct sockaddr *)&peer_sock, peer_sock.ss_len);
263207614Simp	if (n != size) {
264222326Srodrigc		tftp_log(LOG_ERR, "send_rrq: %d %s", n, strerror(errno));
265207614Simp		return (1);
266207614Simp	}
267207614Simp	return (0);
268207614Simp}
269207614Simp
270207614Simp/*
271207614Simp * Send an OACK packet (option acknowledgement).
272207614Simp */
273207614Simpint
274207614Simpsend_oack(int peer)
275207614Simp{
276207614Simp	struct tftphdr *tp;
277207614Simp	int size, i, n;
278207614Simp	char *bp;
279207614Simp	char buf[MAXPKTSIZE];
280207614Simp
281207614Simp	if (debug&DEBUG_PACKETS)
282207614Simp		tftp_log(LOG_DEBUG, "Sending OACK");
283207614Simp
284207614Simp	DROPPACKETn("send_oack", 0);
285207614Simp
286207614Simp	/*
287207614Simp	 * Send back an options acknowledgement (only the ones with
288207614Simp	 * a reply for)
289207614Simp	 */
290207614Simp	tp = (struct tftphdr *)buf;
291207614Simp	bp = buf + 2;
292207614Simp	size = sizeof(buf) - 2;
293207614Simp	tp->th_opcode = htons((u_short)OACK);
294207614Simp	for (i = 0; options[i].o_type != NULL; i++) {
295207614Simp		if (options[i].o_reply != NULL) {
296207614Simp			n = snprintf(bp, size, "%s%c%s", options[i].o_type,
297207614Simp				     0, options[i].o_reply);
298207614Simp			bp += n+1;
299207614Simp			size -= n+1;
300207614Simp			if (size < 0) {
301207614Simp				tftp_log(LOG_ERR, "oack: buffer overflow");
302207614Simp				exit(1);
303207614Simp			}
304207614Simp		}
305207614Simp	}
306207614Simp	size = bp - buf;
307207614Simp
308207614Simp	if (sendto(peer, buf, size, 0,
309207614Simp		(struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
310207614Simp		tftp_log(LOG_INFO, "send_oack: %s", strerror(errno));
311207614Simp		return (1);
312207614Simp	}
313207614Simp
314207614Simp	return (0);
315207614Simp}
316207614Simp
317207614Simp/*
318207614Simp * Send an ACK packet (acknowledgement).
319207614Simp */
320207614Simpint
321207614Simpsend_ack(int fp, uint16_t block)
322207614Simp{
323207614Simp	struct tftphdr *tp;
324207614Simp	int size;
325207614Simp	char buf[MAXPKTSIZE];
326207614Simp
327207614Simp	if (debug&DEBUG_PACKETS)
328207614Simp		tftp_log(LOG_DEBUG, "Sending ACK for block %d", block);
329207614Simp
330207614Simp	DROPPACKETn("send_ack", 0);
331207614Simp
332207614Simp	tp = (struct tftphdr *)buf;
333207614Simp	size = sizeof(buf) - 2;
334207614Simp	tp->th_opcode = htons((u_short)ACK);
335207614Simp	tp->th_block = htons((u_short)block);
336207614Simp	size = 4;
337207614Simp
338207614Simp	if (sendto(fp, buf, size, 0,
339207614Simp	    (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
340207614Simp		tftp_log(LOG_INFO, "send_ack: %s", strerror(errno));
341207614Simp		return (1);
342207614Simp	}
343207614Simp
344207614Simp	return (0);
345207614Simp}
346207614Simp
347207614Simp/*
348207614Simp * Send a DATA packet
349207614Simp */
350207614Simpint
351207614Simpsend_data(int peer, uint16_t block, char *data, int size)
352207614Simp{
353207614Simp	char buf[MAXPKTSIZE];
354207614Simp	struct tftphdr *pkt;
355207614Simp	int n;
356207614Simp
357207614Simp	if (debug&DEBUG_PACKETS)
358207614Simp		tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes",
359207614Simp			block, size);
360207614Simp
361207614Simp	DROPPACKETn("send_data", 0);
362207614Simp
363207614Simp	pkt = (struct tftphdr *)buf;
364207614Simp
365207614Simp	pkt->th_opcode = htons((u_short)DATA);
366207614Simp	pkt->th_block = htons((u_short)block);
367207614Simp	memcpy(pkt->th_data, data, size);
368207614Simp
369207614Simp	n = send_packet(peer, block, (char *)pkt, size + 4);
370207614Simp	return (n);
371207614Simp}
372207614Simp
373207614Simp
374207614Simp/*
375207614Simp * Receive a packet
376207614Simp */
377241720Sedstatic jmp_buf timeoutbuf;
378207614Simp
379207614Simpstatic void
380207614Simptimeout(int sig __unused)
381207614Simp{
382207614Simp
383207614Simp	/* tftp_log(LOG_DEBUG, "Timeout\n");	Inside a signal handler... */
384207614Simp	longjmp(timeoutbuf, 1);
385207614Simp}
386207614Simp
387207614Simpint
388207614Simpreceive_packet(int peer, char *data, int size, struct sockaddr_storage *from,
389207614Simp    int thistimeout)
390207614Simp{
391207614Simp	struct tftphdr *pkt;
392207614Simp	struct sockaddr_storage from_local;
393207614Simp	struct sockaddr_storage *pfrom;
394207614Simp	socklen_t fromlen;
395207614Simp	int n;
396207614Simp	static int waiting;
397207614Simp
398207614Simp	if (debug&DEBUG_PACKETS)
399207614Simp		tftp_log(LOG_DEBUG,
400207614Simp		    "Waiting %d seconds for packet", timeoutpacket);
401207614Simp
402207614Simp	pkt = (struct tftphdr *)data;
403207614Simp
404207614Simp	waiting = 0;
405207614Simp	signal(SIGALRM, timeout);
406207614Simp	setjmp(timeoutbuf);
407207614Simp	alarm(thistimeout);
408207614Simp
409207614Simp	if (waiting > 0) {
410207614Simp		alarm(0);
411207614Simp		return (RP_TIMEOUT);
412207614Simp	}
413207614Simp
414207614Simp	if (waiting > 0) {
415207614Simp		tftp_log(LOG_ERR, "receive_packet: timeout");
416207614Simp		alarm(0);
417207614Simp		return (RP_TIMEOUT);
418207614Simp	}
419207614Simp
420207614Simp	waiting++;
421212665Simp	pfrom = (from == NULL) ? &from_local : from;
422207614Simp	fromlen = sizeof(*pfrom);
423207614Simp	n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen);
424207614Simp
425207614Simp	alarm(0);
426207614Simp
427207614Simp	DROPPACKETn("receive_packet", RP_TIMEOUT);
428207614Simp
429207614Simp	if (n < 0) {
430207614Simp		tftp_log(LOG_ERR, "receive_packet: timeout");
431207614Simp		return (RP_TIMEOUT);
432207614Simp	}
433207614Simp
434207614Simp	alarm(0);
435207614Simp
436207614Simp	if (n < 0) {
437207614Simp		/* No idea what could have happened if it isn't a timeout */
438207614Simp		tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno));
439207614Simp		return (RP_RECVFROM);
440207614Simp	}
441207614Simp	if (n < 4) {
442207614Simp		tftp_log(LOG_ERR,
443207614Simp		    "receive_packet: packet too small (%d bytes)", n);
444207614Simp		return (RP_TOOSMALL);
445207614Simp	}
446207614Simp
447207614Simp	pkt->th_opcode = ntohs((u_short)pkt->th_opcode);
448207614Simp	if (pkt->th_opcode == DATA ||
449207614Simp	    pkt->th_opcode == ACK)
450207614Simp		pkt->th_block = ntohs((u_short)pkt->th_block);
451207614Simp
452207614Simp	if (pkt->th_opcode == DATA && n > pktsize) {
453207614Simp		tftp_log(LOG_ERR, "receive_packet: packet too big");
454207614Simp		return (RP_TOOBIG);
455207614Simp	}
456207614Simp
457207614Simp	if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr !=
458207614Simp	    ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) {
459207614Simp		tftp_log(LOG_ERR,
460207614Simp			"receive_packet: received packet from wrong source");
461207614Simp		return (RP_WRONGSOURCE);
462207614Simp	}
463207614Simp
464207614Simp	if (pkt->th_opcode == ERROR) {
465231973Semaste		tftp_log(pkt->th_code == EUNDEF ? LOG_DEBUG : LOG_ERR,
466231973Semaste		    "Got ERROR packet: %s", pkt->th_msg);
467207614Simp		return (RP_ERROR);
468207614Simp	}
469207614Simp
470207614Simp	if (debug&DEBUG_PACKETS)
471207614Simp		tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet",
472207614Simp			n, packettype(pkt->th_opcode));
473207614Simp
474207614Simp	return n - 4;
475207614Simp}
476