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