allow cg's to start at frag instead of block boundries
[unix-history] / usr / src / libexec / tftpd / tftpd.c
CommitLineData
8c5eec2f
DF
1/*
2 * Copyright (c) 1983 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
8char copyright[] =
9"@(#) Copyright (c) 1983 Regents of the University of California.\n\
10 All rights reserved.\n";
11#endif not lint
12
ce4fd43b 13#ifndef lint
854c42ad 14static char sccsid[] = "@(#)tftpd.c 5.6 (Berkeley) %G%";
8c5eec2f 15#endif not lint
f8675e8e 16
7a859218 17
f8675e8e
SL
18/*
19 * Trivial file transfer protocol server.
7a859218
GM
20 *
21 * This version includes many modifications by Jim Guyton <guyton@rand-unix>
f8675e8e 22 */
7a859218 23
f8675e8e 24#include <sys/types.h>
f8675e8e 25#include <sys/socket.h>
f8675e8e 26#include <sys/ioctl.h>
ce4fd43b
SL
27#include <sys/wait.h>
28#include <sys/stat.h>
de3b21e8
SL
29
30#include <netinet/in.h>
31
533343f1
SL
32#include <arpa/tftp.h>
33
de3b21e8 34#include <signal.h>
f8675e8e 35#include <stdio.h>
f8675e8e
SL
36#include <errno.h>
37#include <ctype.h>
4d5e33bd 38#include <netdb.h>
103499bc 39#include <setjmp.h>
3f99c0f7 40#include <syslog.h>
103499bc 41
103499bc 42#define TIMEOUT 5
de3b21e8 43
f8675e8e 44extern int errno;
4d5e33bd 45struct sockaddr_in sin = { AF_INET };
bb933cc2 46int peer;
103499bc
SL
47int rexmtval = TIMEOUT;
48int maxtimeout = 5*TIMEOUT;
7a859218
GM
49
50#define PKTSIZE SEGSIZE+4
51char buf[PKTSIZE];
52char ackbuf[PKTSIZE];
bb933cc2
MK
53struct sockaddr_in from;
54int fromlen;
f8675e8e 55
bb933cc2 56main()
f8675e8e 57{
f8675e8e
SL
58 register struct tftphdr *tp;
59 register int n;
854c42ad 60 int on = 1;
f8675e8e 61
076ae92c 62 openlog("tftpd", LOG_PID, LOG_DAEMON);
854c42ad
GM
63 if (ioctl(0, FIONBIO, &on) < 0) {
64 syslog(LOG_ERR, "ioctl(FIONBIO): %m\n");
65 exit(1);
66 }
bb933cc2
MK
67 fromlen = sizeof (from);
68 n = recvfrom(0, buf, sizeof (buf), 0,
69 (caddr_t)&from, &fromlen);
70 if (n < 0) {
854c42ad 71 syslog(LOG_ERR, "recvfrom: %m\n");
4d5e33bd
SL
72 exit(1);
73 }
854c42ad
GM
74 /*
75 * Now that we have read the message out of the UDP
76 * socket, we fork and exit. Thus, inetd will go back
77 * to listening to the tftp port, and the next request
78 * to come in will start up a new instance of tftpd.
79 *
80 * We do this so that inetd can run tftpd in "wait" mode.
81 * The problem with tftpd running in "nowait" mode is that
82 * inetd may get one or more successful "selects" on the
83 * tftp port before we do our receive, so more than one
84 * instance of tftpd may be started up. Worse, if tftpd
85 * break before doing the above "recvfrom", inetd would
86 * spawn endless instances, clogging the system.
87 */
88 {
89 int pid;
90 int i, j;
91
92 for (i = 1; i < 20; i++) {
93 pid = fork();
94 if (pid < 0) {
95 sleep(i);
96 /*
97 * flush out to most recently sent request.
98 *
99 * This may drop some request, but those
100 * will be resent by the clients when
101 * they timeout. The positive effect of
102 * this flush is to (try to) prevent more
103 * than one tftpd being started up to service
104 * a single request from a single client.
105 */
106 j = sizeof from;
107 i = recvfrom(0, buf, sizeof (buf), 0,
108 (caddr_t)&from, &j);
109 if (i > 0) {
110 n = i;
111 fromlen = j;
112 }
113 } else {
114 break;
115 }
116 }
117 if (pid < 0) {
118 syslog(LOG_ERR, "fork: %m\n");
119 exit(1);
120 } else if (pid != 0) {
121 exit(0);
122 }
123 }
bb933cc2
MK
124 from.sin_family = AF_INET;
125 alarm(0);
bb933cc2
MK
126 close(0);
127 close(1);
128 peer = socket(AF_INET, SOCK_DGRAM, 0);
129 if (peer < 0) {
854c42ad 130 syslog(LOG_ERR, "socket: %m\n");
bb933cc2
MK
131 exit(1);
132 }
133 if (bind(peer, (caddr_t)&sin, sizeof (sin)) < 0) {
854c42ad 134 syslog(LOG_ERR, "bind: %m\n");
bb933cc2 135 exit(1);
f8675e8e 136 }
bb933cc2 137 if (connect(peer, (caddr_t)&from, sizeof(from)) < 0) {
854c42ad 138 syslog(LOG_ERR, "connect: %m\n");
bb933cc2 139 exit(1);
f8675e8e 140 }
bb933cc2
MK
141 tp = (struct tftphdr *)buf;
142 tp->th_opcode = ntohs(tp->th_opcode);
143 if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
144 tftp(tp, n);
145 exit(1);
103499bc
SL
146}
147
f8675e8e
SL
148int validate_access();
149int sendfile(), recvfile();
150
151struct formats {
152 char *f_mode;
153 int (*f_validate)();
154 int (*f_send)();
155 int (*f_recv)();
7a859218 156 int f_convert;
f8675e8e 157} formats[] = {
7a859218
GM
158 { "netascii", validate_access, sendfile, recvfile, 1 },
159 { "octet", validate_access, sendfile, recvfile, 0 },
f8675e8e 160#ifdef notdef
7a859218 161 { "mail", validate_user, sendmail, recvmail, 1 },
f8675e8e
SL
162#endif
163 { 0 }
164};
165
f8675e8e
SL
166/*
167 * Handle initial connection protocol.
168 */
bb933cc2 169tftp(tp, size)
f8675e8e
SL
170 struct tftphdr *tp;
171 int size;
172{
173 register char *cp;
174 int first = 1, ecode;
175 register struct formats *pf;
176 char *filename, *mode;
177
f8675e8e
SL
178 filename = cp = tp->th_stuff;
179again:
180 while (cp < buf + size) {
181 if (*cp == '\0')
182 break;
183 cp++;
184 }
185 if (*cp != '\0') {
186 nak(EBADOP);
187 exit(1);
188 }
189 if (first) {
190 mode = ++cp;
191 first = 0;
192 goto again;
193 }
194 for (cp = mode; *cp; cp++)
195 if (isupper(*cp))
196 *cp = tolower(*cp);
197 for (pf = formats; pf->f_mode; pf++)
198 if (strcmp(pf->f_mode, mode) == 0)
199 break;
200 if (pf->f_mode == 0) {
201 nak(EBADOP);
202 exit(1);
203 }
bb933cc2 204 ecode = (*pf->f_validate)(filename, tp->th_opcode);
f8675e8e
SL
205 if (ecode) {
206 nak(ecode);
207 exit(1);
208 }
209 if (tp->th_opcode == WRQ)
210 (*pf->f_recv)(pf);
211 else
212 (*pf->f_send)(pf);
213 exit(0);
214}
215
7a859218
GM
216
217FILE *file;
bb933cc2 218
f8675e8e
SL
219/*
220 * Validate file access. Since we
221 * have no uid or gid, for now require
222 * file to exist and be publicly
223 * readable/writable.
224 * Note also, full path name must be
225 * given as we have no login directory.
226 */
7a859218
GM
227validate_access(filename, mode)
228 char *filename;
f8675e8e
SL
229 int mode;
230{
231 struct stat stbuf;
7a859218 232 int fd;
f8675e8e 233
7a859218 234 if (*filename != '/')
f8675e8e 235 return (EACCESS);
7a859218 236 if (stat(filename, &stbuf) < 0)
f8675e8e
SL
237 return (errno == ENOENT ? ENOTFOUND : EACCESS);
238 if (mode == RRQ) {
239 if ((stbuf.st_mode&(S_IREAD >> 6)) == 0)
240 return (EACCESS);
241 } else {
242 if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0)
243 return (EACCESS);
244 }
7a859218 245 fd = open(filename, mode == RRQ ? 0 : 1);
f8675e8e
SL
246 if (fd < 0)
247 return (errno + 100);
7a859218
GM
248 file = fdopen(fd, (mode == RRQ)? "r":"w");
249 if (file == NULL) {
250 return errno+100;
251 }
f8675e8e
SL
252 return (0);
253}
254
103499bc
SL
255int timeout;
256jmp_buf timeoutbuf;
f8675e8e
SL
257
258timer()
259{
103499bc
SL
260
261 timeout += rexmtval;
262 if (timeout >= maxtimeout)
f8675e8e 263 exit(1);
103499bc 264 longjmp(timeoutbuf, 1);
f8675e8e
SL
265}
266
267/*
268 * Send the requested file.
269 */
270sendfile(pf)
7a859218 271 struct formats *pf;
f8675e8e 272{
7a859218
GM
273 struct tftphdr *dp, *r_init();
274 register struct tftphdr *ap; /* ack packet */
f8675e8e
SL
275 register int block = 1, size, n;
276
103499bc 277 signal(SIGALRM, timer);
7a859218
GM
278 dp = r_init();
279 ap = (struct tftphdr *)ackbuf;
f8675e8e 280 do {
7a859218 281 size = readit(file, &dp, pf->f_convert);
f8675e8e
SL
282 if (size < 0) {
283 nak(errno + 100);
7a859218 284 goto abort;
f8675e8e 285 }
7a859218
GM
286 dp->th_opcode = htons((u_short)DATA);
287 dp->th_block = htons((u_short)block);
f8675e8e 288 timeout = 0;
103499bc 289 (void) setjmp(timeoutbuf);
7a859218 290
f9cd9486 291send_data:
7a859218 292 if (send(peer, dp, size + 4, 0) != size + 4) {
854c42ad 293 syslog(LOG_ERR, "tftpd: write: %m\n");
7a859218 294 goto abort;
f8675e8e 295 }
7a859218 296 read_ahead(file, pf->f_convert);
f9cd9486 297 for ( ; ; ) {
7a859218
GM
298 alarm(rexmtval); /* read the ack */
299 n = recv(peer, ackbuf, sizeof (ackbuf), 0);
f8675e8e 300 alarm(0);
103499bc 301 if (n < 0) {
854c42ad 302 syslog(LOG_ERR, "tftpd: read: %m\n");
7a859218 303 goto abort;
103499bc 304 }
7a859218
GM
305 ap->th_opcode = ntohs((u_short)ap->th_opcode);
306 ap->th_block = ntohs((u_short)ap->th_block);
307
308 if (ap->th_opcode == ERROR)
309 goto abort;
f9cd9486
GM
310
311 if (ap->th_opcode == ACK) {
312 if (ap->th_block == block) {
313 break;
314 }
81243fce
GM
315 /* Re-synchronize with the other side */
316 (void) synchnet(peer);
f9cd9486
GM
317 if (ap->th_block == (block -1)) {
318 goto send_data;
319 }
320 }
7a859218 321
f9cd9486 322 }
f8675e8e
SL
323 block++;
324 } while (size == SEGSIZE);
7a859218
GM
325abort:
326 (void) fclose(file);
f8675e8e
SL
327}
328
7a859218
GM
329justquit()
330{
331 exit(0);
332}
333
334
f8675e8e
SL
335/*
336 * Receive a file.
337 */
338recvfile(pf)
7a859218 339 struct formats *pf;
f8675e8e 340{
7a859218
GM
341 struct tftphdr *dp, *w_init();
342 register struct tftphdr *ap; /* ack buffer */
f8675e8e
SL
343 register int block = 0, n, size;
344
103499bc 345 signal(SIGALRM, timer);
7a859218
GM
346 dp = w_init();
347 ap = (struct tftphdr *)ackbuf;
f8675e8e
SL
348 do {
349 timeout = 0;
7a859218
GM
350 ap->th_opcode = htons((u_short)ACK);
351 ap->th_block = htons((u_short)block);
f8675e8e 352 block++;
103499bc 353 (void) setjmp(timeoutbuf);
7a859218
GM
354send_ack:
355 if (send(peer, ackbuf, 4, 0) != 4) {
854c42ad 356 syslog(LOG_ERR, "tftpd: write: %m\n");
103499bc 357 goto abort;
f8675e8e 358 }
7a859218
GM
359 write_behind(file, pf->f_convert);
360 for ( ; ; ) {
103499bc 361 alarm(rexmtval);
7a859218 362 n = recv(peer, dp, PKTSIZE, 0);
f8675e8e 363 alarm(0);
7a859218 364 if (n < 0) { /* really? */
854c42ad 365 syslog(LOG_ERR, "tftpd: read: %m\n");
103499bc
SL
366 goto abort;
367 }
7a859218
GM
368 dp->th_opcode = ntohs((u_short)dp->th_opcode);
369 dp->th_block = ntohs((u_short)dp->th_block);
370 if (dp->th_opcode == ERROR)
103499bc 371 goto abort;
7a859218
GM
372 if (dp->th_opcode == DATA) {
373 if (dp->th_block == block) {
374 break; /* normal */
375 }
81243fce
GM
376 /* Re-synchronize with the other side */
377 (void) synchnet(peer);
7a859218
GM
378 if (dp->th_block == (block-1))
379 goto send_ack; /* rexmit */
380 }
381 }
382 /* size = write(file, dp->th_data, n - 4); */
383 size = writeit(file, &dp, n - 4, pf->f_convert);
384 if (size != (n-4)) { /* ahem */
385 if (size < 0) nak(errno + 100);
386 else nak(ENOSPACE);
103499bc 387 goto abort;
f8675e8e
SL
388 }
389 } while (size == SEGSIZE);
7a859218
GM
390 write_behind(file, pf->f_convert);
391 (void) fclose(file); /* close data file */
392
393 ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */
394 ap->th_block = htons((u_short)(block));
395 (void) send(peer, ackbuf, 4, 0);
396
397 signal(SIGALRM, justquit); /* just quit on timeout */
398 alarm(rexmtval);
399 n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
400 alarm(0);
401 if (n >= 4 && /* if read some data */
402 dp->th_opcode == DATA && /* and got a data block */
403 block == dp->th_block) { /* then my last ack was lost */
404 (void) send(peer, ackbuf, 4, 0); /* resend final ack */
405 }
103499bc 406abort:
7a859218 407 return;
f8675e8e
SL
408}
409
410struct errmsg {
411 int e_code;
412 char *e_msg;
413} errmsgs[] = {
414 { EUNDEF, "Undefined error code" },
415 { ENOTFOUND, "File not found" },
416 { EACCESS, "Access violation" },
417 { ENOSPACE, "Disk full or allocation exceeded" },
418 { EBADOP, "Illegal TFTP operation" },
419 { EBADID, "Unknown transfer ID" },
420 { EEXISTS, "File already exists" },
421 { ENOUSER, "No such user" },
422 { -1, 0 }
423};
424
425/*
426 * Send a nak packet (error message).
427 * Error code passed in is one of the
428 * standard TFTP codes, or a UNIX errno
429 * offset by 100.
430 */
431nak(error)
432 int error;
433{
434 register struct tftphdr *tp;
435 int length;
436 register struct errmsg *pe;
437 extern char *sys_errlist[];
438
439 tp = (struct tftphdr *)buf;
440 tp->th_opcode = htons((u_short)ERROR);
441 tp->th_code = htons((u_short)error);
442 for (pe = errmsgs; pe->e_code >= 0; pe++)
443 if (pe->e_code == error)
444 break;
7a859218 445 if (pe->e_code < 0) {
f8675e8e 446 pe->e_msg = sys_errlist[error - 100];
7a859218
GM
447 tp->th_code = EUNDEF; /* set 'undef' errorcode */
448 }
f8675e8e
SL
449 strcpy(tp->th_msg, pe->e_msg);
450 length = strlen(pe->e_msg);
451 tp->th_msg[length] = '\0';
452 length += 5;
bb933cc2 453 if (send(peer, buf, length, 0) != length)
854c42ad 454 syslog(LOG_ERR, "nak: %m\n");
f8675e8e 455}