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