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