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