xref: /csrg-svn/libexec/tftpd/tftpd.c (revision 16372)
1 #ifndef lint
2 static	char sccsid[] = "@(#)tftpd.c	4.12 (Berkeley) 04/11/84";
3 #endif
4 
5 /*
6  * Trivial file transfer protocol server.
7  */
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <sys/ioctl.h>
11 #include <sys/wait.h>
12 #include <sys/stat.h>
13 
14 #include <netinet/in.h>
15 
16 #include <arpa/tftp.h>
17 
18 #include <signal.h>
19 #include <stdio.h>
20 #include <errno.h>
21 #include <ctype.h>
22 #include <netdb.h>
23 #include <setjmp.h>
24 
25 #define	TIMEOUT		5
26 
27 extern	int errno;
28 struct	sockaddr_in sin = { AF_INET };
29 int	peer;
30 int	rexmtval = TIMEOUT;
31 int	maxtimeout = 5*TIMEOUT;
32 char	buf[BUFSIZ];
33 struct	sockaddr_in from;
34 int	fromlen;
35 
36 main()
37 {
38 	register struct tftphdr *tp;
39 	register int n;
40 
41 	alarm(10);
42 	fromlen = sizeof (from);
43 	n = recvfrom(0, buf, sizeof (buf), 0,
44 	    (caddr_t)&from, &fromlen);
45 	if (n < 0) {
46 		perror("tftpd: recvfrom");
47 		exit(1);
48 	}
49 	from.sin_family = AF_INET;
50 	alarm(0);
51 #ifdef do_it_right_and_use_a_new_port
52 	if (fork())
53 		exit(0);
54 	close(0);
55 	close(1);
56 	peer = socket(AF_INET, SOCK_DGRAM, 0);
57 	if (peer < 0) {
58 		perror("tftpd: socket");
59 		exit(1);
60 	}
61 	if (bind(peer, (caddr_t)&sin, sizeof (sin)) < 0) {
62 		perror("tftpd: bind");
63 		exit(1);
64 	}
65 #else
66 	/*
67 	 * The current 4.2 tftp client neglects to switch its destination
68 	 * port after the first ACK.
69 	 */
70 	peer = 0;
71 #endif
72 	if (connect(peer, (caddr_t)&from, sizeof(from)) < 0) {
73 		perror("tftpd: connect");
74 		exit(1);
75 	}
76 	tp = (struct tftphdr *)buf;
77 	tp->th_opcode = ntohs(tp->th_opcode);
78 	if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
79 		tftp(tp, n);
80 	exit(1);
81 }
82 
83 int	validate_access();
84 int	sendfile(), recvfile();
85 
86 struct formats {
87 	char	*f_mode;
88 	int	(*f_validate)();
89 	int	(*f_send)();
90 	int	(*f_recv)();
91 } formats[] = {
92 	{ "netascii",	validate_access,	sendfile,	recvfile },
93 	{ "octet",	validate_access,	sendfile,	recvfile },
94 #ifdef notdef
95 	{ "mail",	validate_user,		sendmail,	recvmail },
96 #endif
97 	{ 0 }
98 };
99 
100 /*
101  * Handle initial connection protocol.
102  */
103 tftp(tp, size)
104 	struct tftphdr *tp;
105 	int size;
106 {
107 	register char *cp;
108 	int first = 1, ecode;
109 	register struct formats *pf;
110 	char *filename, *mode;
111 
112 	filename = cp = tp->th_stuff;
113 again:
114 	while (cp < buf + size) {
115 		if (*cp == '\0')
116 			break;
117 		cp++;
118 	}
119 	if (*cp != '\0') {
120 		nak(EBADOP);
121 		exit(1);
122 	}
123 	if (first) {
124 		mode = ++cp;
125 		first = 0;
126 		goto again;
127 	}
128 	for (cp = mode; *cp; cp++)
129 		if (isupper(*cp))
130 			*cp = tolower(*cp);
131 	for (pf = formats; pf->f_mode; pf++)
132 		if (strcmp(pf->f_mode, mode) == 0)
133 			break;
134 	if (pf->f_mode == 0) {
135 		nak(EBADOP);
136 		exit(1);
137 	}
138 	ecode = (*pf->f_validate)(filename, tp->th_opcode);
139 	if (ecode) {
140 		nak(ecode);
141 		exit(1);
142 	}
143 	if (tp->th_opcode == WRQ)
144 		(*pf->f_recv)(pf);
145 	else
146 		(*pf->f_send)(pf);
147 	exit(0);
148 }
149 
150 int	fd;
151 
152 /*
153  * Validate file access.  Since we
154  * have no uid or gid, for now require
155  * file to exist and be publicly
156  * readable/writable.
157  * Note also, full path name must be
158  * given as we have no login directory.
159  */
160 validate_access(file, mode)
161 	char *file;
162 	int mode;
163 {
164 	struct stat stbuf;
165 
166 	if (*file != '/')
167 		return (EACCESS);
168 	if (stat(file, &stbuf) < 0)
169 		return (errno == ENOENT ? ENOTFOUND : EACCESS);
170 	if (mode == RRQ) {
171 		if ((stbuf.st_mode&(S_IREAD >> 6)) == 0)
172 			return (EACCESS);
173 	} else {
174 		if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0)
175 			return (EACCESS);
176 	}
177 	fd = open(file, mode == RRQ ? 0 : 1);
178 	if (fd < 0)
179 		return (errno + 100);
180 	return (0);
181 }
182 
183 int	timeout;
184 jmp_buf	timeoutbuf;
185 
186 timer()
187 {
188 
189 	timeout += rexmtval;
190 	if (timeout >= maxtimeout)
191 		exit(1);
192 	longjmp(timeoutbuf, 1);
193 }
194 
195 /*
196  * Send the requested file.
197  */
198 sendfile(pf)
199 	struct format *pf;
200 {
201 	register struct tftphdr *tp;
202 	register int block = 1, size, n;
203 
204 	signal(SIGALRM, timer);
205 	tp = (struct tftphdr *)buf;
206 	do {
207 		size = read(fd, tp->th_data, SEGSIZE);
208 		if (size < 0) {
209 			nak(errno + 100);
210 			return;
211 		}
212 		tp->th_opcode = htons((u_short)DATA);
213 		tp->th_block = htons((u_short)block);
214 		timeout = 0;
215 		(void) setjmp(timeoutbuf);
216 		if (send(peer, buf, size + 4, 0) != size + 4) {
217 			perror("tftpd: send");
218 			return;
219 		}
220 		do {
221 			alarm(rexmtval);
222 			n = recv(peer, buf, sizeof (buf), 0);
223 			alarm(0);
224 			if (n < 0) {
225 				perror("tftpd: recv");
226 				return;
227 			}
228 			tp->th_opcode = ntohs((u_short)tp->th_opcode);
229 			tp->th_block = ntohs((u_short)tp->th_block);
230 			if (tp->th_opcode == ERROR)
231 				return;
232 		} while (tp->th_opcode != ACK || tp->th_block != block);
233 		block++;
234 	} while (size == SEGSIZE);
235 }
236 
237 /*
238  * Receive a file.
239  */
240 recvfile(pf)
241 	struct format *pf;
242 {
243 	register struct tftphdr *tp;
244 	register int block = 0, n, size;
245 
246 	signal(SIGALRM, timer);
247 	tp = (struct tftphdr *)buf;
248 	do {
249 		timeout = 0;
250 		tp->th_opcode = htons((u_short)ACK);
251 		tp->th_block = htons((u_short)block);
252 		block++;
253 		(void) setjmp(timeoutbuf);
254 		if (send(peer, buf, 4, 0) != 4) {
255 			perror("tftpd: send");
256 			goto abort;
257 		}
258 		do {
259 			alarm(rexmtval);
260 			n = recv(peer, buf, sizeof (buf), 0);
261 			alarm(0);
262 			if (n < 0) {
263 				perror("tftpd: recv");
264 				goto abort;
265 			}
266 			tp->th_opcode = ntohs((u_short)tp->th_opcode);
267 			tp->th_block = ntohs((u_short)tp->th_block);
268 			if (tp->th_opcode == ERROR)
269 				goto abort;
270 		} while (tp->th_opcode != DATA || block != tp->th_block);
271 		size = write(fd, tp->th_data, n - 4);
272 		if (size < 0) {
273 			nak(errno + 100);
274 			goto abort;
275 		}
276 	} while (size == SEGSIZE);
277 abort:
278 	tp->th_opcode = htons((u_short)ACK);
279 	tp->th_block = htons((u_short)(block));
280 	(void) send(peer, buf, 4, 0);
281 }
282 
283 struct errmsg {
284 	int	e_code;
285 	char	*e_msg;
286 } errmsgs[] = {
287 	{ EUNDEF,	"Undefined error code" },
288 	{ ENOTFOUND,	"File not found" },
289 	{ EACCESS,	"Access violation" },
290 	{ ENOSPACE,	"Disk full or allocation exceeded" },
291 	{ EBADOP,	"Illegal TFTP operation" },
292 	{ EBADID,	"Unknown transfer ID" },
293 	{ EEXISTS,	"File already exists" },
294 	{ ENOUSER,	"No such user" },
295 	{ -1,		0 }
296 };
297 
298 /*
299  * Send a nak packet (error message).
300  * Error code passed in is one of the
301  * standard TFTP codes, or a UNIX errno
302  * offset by 100.
303  */
304 nak(error)
305 	int error;
306 {
307 	register struct tftphdr *tp;
308 	int length;
309 	register struct errmsg *pe;
310 	extern char *sys_errlist[];
311 
312 	tp = (struct tftphdr *)buf;
313 	tp->th_opcode = htons((u_short)ERROR);
314 	tp->th_code = htons((u_short)error);
315 	for (pe = errmsgs; pe->e_code >= 0; pe++)
316 		if (pe->e_code == error)
317 			break;
318 	if (pe->e_code < 0)
319 		pe->e_msg = sys_errlist[error - 100];
320 	strcpy(tp->th_msg, pe->e_msg);
321 	length = strlen(pe->e_msg);
322 	tp->th_msg[length] = '\0';
323 	length += 5;
324 	if (send(peer, buf, length, 0) != length)
325 		perror("nak");
326 	exit(1);
327 }
328