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