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
   8: char copyright[] =
   9: "@(#) Copyright (c) 1983 Regents of the University of California.\n\
  10:  All rights reserved.\n";
  11: #endif not lint
  12: 
  13: #ifndef lint
  14: static char sccsid[] = "@(#)tftpd.c	5.6 (Berkeley) 5/13/86";
  15: #endif not lint
  16: 
  17: 
  18: /*
  19:  * Trivial file transfer protocol server.
  20:  *
  21:  * This version includes many modifications by Jim Guyton <guyton@rand-unix>
  22:  */
  23: 
  24: #include <sys/types.h>
  25: #include <sys/socket.h>
  26: #include <sys/ioctl.h>
  27: #include <sys/wait.h>
  28: #include <sys/stat.h>
  29: 
  30: #include <netinet/in.h>
  31: 
  32: #include <arpa/tftp.h>
  33: 
  34: #include <signal.h>
  35: #include <stdio.h>
  36: #include <errno.h>
  37: #include <ctype.h>
  38: #include <netdb.h>
  39: #include <setjmp.h>
  40: #include <syslog.h>
  41: 
  42: #define TIMEOUT     5
  43: 
  44: extern  int errno;
  45: struct  sockaddr_in sin = { AF_INET };
  46: int peer;
  47: int rexmtval = TIMEOUT;
  48: int maxtimeout = 5*TIMEOUT;
  49: 
  50: #define PKTSIZE SEGSIZE+4
  51: char    buf[PKTSIZE];
  52: char    ackbuf[PKTSIZE];
  53: struct  sockaddr_in from;
  54: int fromlen;
  55: 
  56: main()
  57: {
  58:     register struct tftphdr *tp;
  59:     register int n;
  60:     int on = 1;
  61: 
  62:     openlog("tftpd", LOG_PID, LOG_DAEMON);
  63:     if (ioctl(0, FIONBIO, &on) < 0) {
  64:         syslog(LOG_ERR, "ioctl(FIONBIO): %m\n");
  65:         exit(1);
  66:     }
  67:     fromlen = sizeof (from);
  68:     n = recvfrom(0, buf, sizeof (buf), 0,
  69:         (caddr_t)&from, &fromlen);
  70:     if (n < 0) {
  71:         syslog(LOG_ERR, "recvfrom: %m\n");
  72:         exit(1);
  73:     }
  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:     }
 124:     from.sin_family = AF_INET;
 125:     alarm(0);
 126:     close(0);
 127:     close(1);
 128:     peer = socket(AF_INET, SOCK_DGRAM, 0);
 129:     if (peer < 0) {
 130:         syslog(LOG_ERR, "socket: %m\n");
 131:         exit(1);
 132:     }
 133:     if (bind(peer, (caddr_t)&sin, sizeof (sin)) < 0) {
 134:         syslog(LOG_ERR, "bind: %m\n");
 135:         exit(1);
 136:     }
 137:     if (connect(peer, (caddr_t)&from, sizeof(from)) < 0) {
 138:         syslog(LOG_ERR, "connect: %m\n");
 139:         exit(1);
 140:     }
 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);
 146: }
 147: 
 148: int validate_access();
 149: int sendfile(), recvfile();
 150: 
 151: struct formats {
 152:     char    *f_mode;
 153:     int (*f_validate)();
 154:     int (*f_send)();
 155:     int (*f_recv)();
 156:     int f_convert;
 157: } formats[] = {
 158:     { "netascii",   validate_access,    sendfile,   recvfile, 1 },
 159:     { "octet",  validate_access,    sendfile,   recvfile, 0 },
 160: #ifdef notdef
 161:     { "mail",   validate_user,      sendmail,   recvmail, 1 },
 162: #endif
 163:     { 0 }
 164: };
 165: 
 166: /*
 167:  * Handle initial connection protocol.
 168:  */
 169: tftp(tp, size)
 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: 
 178:     filename = cp = tp->th_stuff;
 179: again:
 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:     }
 204:     ecode = (*pf->f_validate)(filename, tp->th_opcode);
 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: 
 216: 
 217: FILE *file;
 218: 
 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:  */
 227: validate_access(filename, mode)
 228:     char *filename;
 229:     int mode;
 230: {
 231:     struct stat stbuf;
 232:     int fd;
 233: 
 234:     if (*filename != '/')
 235:         return (EACCESS);
 236:     if (stat(filename, &stbuf) < 0)
 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:     }
 245:     fd = open(filename, mode == RRQ ? 0 : 1);
 246:     if (fd < 0)
 247:         return (errno + 100);
 248:     file = fdopen(fd, (mode == RRQ)? "r":"w");
 249:     if (file == NULL) {
 250:         return errno+100;
 251:     }
 252:     return (0);
 253: }
 254: 
 255: int timeout;
 256: jmp_buf timeoutbuf;
 257: 
 258: timer()
 259: {
 260: 
 261:     timeout += rexmtval;
 262:     if (timeout >= maxtimeout)
 263:         exit(1);
 264:     longjmp(timeoutbuf, 1);
 265: }
 266: 
 267: /*
 268:  * Send the requested file.
 269:  */
 270: sendfile(pf)
 271:     struct formats *pf;
 272: {
 273:     struct tftphdr *dp, *r_init();
 274:     register struct tftphdr *ap;    /* ack packet */
 275:     register int block = 1, size, n;
 276: 
 277:     signal(SIGALRM, timer);
 278:     dp = r_init();
 279:     ap = (struct tftphdr *)ackbuf;
 280:     do {
 281:         size = readit(file, &dp, pf->f_convert);
 282:         if (size < 0) {
 283:             nak(errno + 100);
 284:             goto abort;
 285:         }
 286:         dp->th_opcode = htons((u_short)DATA);
 287:         dp->th_block = htons((u_short)block);
 288:         timeout = 0;
 289:         (void) setjmp(timeoutbuf);
 290: 
 291: send_data:
 292:         if (send(peer, dp, size + 4, 0) != size + 4) {
 293:             syslog(LOG_ERR, "tftpd: write: %m\n");
 294:             goto abort;
 295:         }
 296:         read_ahead(file, pf->f_convert);
 297:         for ( ; ; ) {
 298:             alarm(rexmtval);        /* read the ack */
 299:             n = recv(peer, ackbuf, sizeof (ackbuf), 0);
 300:             alarm(0);
 301:             if (n < 0) {
 302:                 syslog(LOG_ERR, "tftpd: read: %m\n");
 303:                 goto abort;
 304:             }
 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;
 310: 
 311:             if (ap->th_opcode == ACK) {
 312:                 if (ap->th_block == block) {
 313:                     break;
 314:                 }
 315:                 /* Re-synchronize with the other side */
 316:                 (void) synchnet(peer);
 317:                 if (ap->th_block == (block -1)) {
 318:                     goto send_data;
 319:                 }
 320:             }
 321: 
 322:         }
 323:         block++;
 324:     } while (size == SEGSIZE);
 325: abort:
 326:     (void) fclose(file);
 327: }
 328: 
 329: justquit()
 330: {
 331:     exit(0);
 332: }
 333: 
 334: 
 335: /*
 336:  * Receive a file.
 337:  */
 338: recvfile(pf)
 339:     struct formats *pf;
 340: {
 341:     struct tftphdr *dp, *w_init();
 342:     register struct tftphdr *ap;    /* ack buffer */
 343:     register int block = 0, n, size;
 344: 
 345:     signal(SIGALRM, timer);
 346:     dp = w_init();
 347:     ap = (struct tftphdr *)ackbuf;
 348:     do {
 349:         timeout = 0;
 350:         ap->th_opcode = htons((u_short)ACK);
 351:         ap->th_block = htons((u_short)block);
 352:         block++;
 353:         (void) setjmp(timeoutbuf);
 354: send_ack:
 355:         if (send(peer, ackbuf, 4, 0) != 4) {
 356:             syslog(LOG_ERR, "tftpd: write: %m\n");
 357:             goto abort;
 358:         }
 359:         write_behind(file, pf->f_convert);
 360:         for ( ; ; ) {
 361:             alarm(rexmtval);
 362:             n = recv(peer, dp, PKTSIZE, 0);
 363:             alarm(0);
 364:             if (n < 0) {            /* really? */
 365:                 syslog(LOG_ERR, "tftpd: read: %m\n");
 366:                 goto abort;
 367:             }
 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)
 371:                 goto abort;
 372:             if (dp->th_opcode == DATA) {
 373:                 if (dp->th_block == block) {
 374:                     break;   /* normal */
 375:                 }
 376:                 /* Re-synchronize with the other side */
 377:                 (void) synchnet(peer);
 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);
 387:             goto abort;
 388:         }
 389:     } while (size == SEGSIZE);
 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:     }
 406: abort:
 407:     return;
 408: }
 409: 
 410: struct 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:  */
 431: nak(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;
 445:     if (pe->e_code < 0) {
 446:         pe->e_msg = sys_errlist[error - 100];
 447:         tp->th_code = EUNDEF;   /* set 'undef' errorcode */
 448:     }
 449:     strcpy(tp->th_msg, pe->e_msg);
 450:     length = strlen(pe->e_msg);
 451:     tp->th_msg[length] = '\0';
 452:     length += 5;
 453:     if (send(peer, buf, length, 0) != length)
 454:         syslog(LOG_ERR, "nak: %m\n");
 455: }

Defined functions

justquit defined in line 329; used 1 times
main defined in line 56; never used
nak defined in line 431; used 6 times
recvfile defined in line 338; used 3 times
sendfile defined in line 270; used 3 times
tftp defined in line 169; used 1 times
timer defined in line 258; used 2 times
validate_access defined in line 227; used 3 times

Defined variables

ackbuf defined in line 52; used 7 times
buf defined in line 51; used 10 times
copyright defined in line 8; never used
errmsgs defined in line 413; used 1 times
formats defined in line 157; used 1 times
from defined in line 53; used 7 times
fromlen defined in line 54; used 3 times
maxtimeout defined in line 48; used 1 times
peer defined in line 46; used 14 times
rexmtval defined in line 47; used 4 times
sccsid defined in line 14; never used
sin defined in line 45; used 2 times
  • in line 133(2)
timeout defined in line 255; used 4 times
timeoutbuf defined in line 256; used 3 times

Defined struct's

errmsg defined in line 410; used 2 times
  • in line 436(2)
formats defined in line 151; used 6 times

Defined macros

PKTSIZE defined in line 50; used 3 times
TIMEOUT defined in line 42; used 2 times
Last modified: 1986-05-13
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 2126
Valid CSS Valid XHTML 1.0 Strict