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