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