# 2.11BSD#431 - /usr/src/libexec/tftpd/tftpd.c

##### Functions

    justquit          X
    main              X
    nak               X
    recvfile          X
    sendfile          X
    tftp              X
    timer             X
    validate_access   X

##### Variables

    ackbuf            X
    buf               X
    copyright         X
    dirs              X
    errmsgs           X
    formats           X
    from              X
    fromlen           X
    maxtimeout        X
    peer              X
    rexmtval          X
    sin               X
    timeout           X
    timeoutbuf        X

##### Macros

    MAXARG            X
    PKTSIZE           X
    TIMEOUT           X

##### struct's

    errmsg            X
    formats           X

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

#### Defined functions

justquit defined in line 362; used 1 times

- in line 430

main defined in line 70; never used
nak defined in line 464; used 6 times

- in line 204, 219-224(2), 316, 418-419(2)

recvfile defined in line 371; used 3 times

- in line 167, 176-177(2)

sendfile defined in line 303; used 3 times

- in line 167, 176-177(2)

tftp defined in line 187; used 1 times

- in line 162

timer defined in line 291; used 2 times

- in line 310, 378

validate_access defined in line 248; used 3 times

- in line 166, 176-177(2)

#### Defined variables

ackbuf defined in line 63; used 7 times

- in line 312, 332(2), 380, 388, 428, 437

buf defined in line 62; used 10 times

- in line 86(2), 125(2), 159, 198, 432(2), 471, 485

copyright defined in line 21; never used
dirs defined in line 68; used 3 times

- in line 79, 264-267(2)

errmsgs defined in line 446; used 1 times

- in line 474

formats defined in line 175; used 1 times

- in line 215

from defined in line 64; used 7 times

- in line 85-87(2), 124-126(2), 142, 155(2)

fromlen defined in line 65; used 3 times

- in line 85-87(2), 129

maxtimeout defined in line 59; used 1 times

- in line 295

peer defined in line 57; used 14 times

- in line 146-155(4), 325, 332, 349, 388, 395, 410, 428-437(3), 485

rexmtval defined in line 58; used 4 times

- in line 294, 331, 394, 431

sccsid defined in line 25; never used
sin defined in line 56; used 2 times

- in line 151(2)

timeout defined in line 288; used 4 times

- in line 294-295(2), 321, 382

timeoutbuf defined in line 289; used 3 times

- in line 297, 322, 386

#### Defined struct's

errmsg defined in line 443; used 2 times

- in line 469(2)

formats defined in line 169; used 6 times

- in line 193(2), 304(2), 372(2)

#### Defined macros

MAXARG defined in line 67; used 2 times

- in line 68, 78

PKTSIZE defined in line 61; used 3 times

- in line 62-63(2), 395

TIMEOUT defined in line 53; used 2 times

- in line 58-59(2)

Last modified: 1994-01-11 Generated: 2016-12-26 | Generated by src2html V0.67 page hit count: 2277 |