Annotation of cci/usr/src/etc/tftpd.c, revision 1.1.1.1

1.1       root        1: #ifndef lint
                      2: static char sccsid[] = "@(#)tftpd.c    4.11 (Berkeley) 7/2/83";
                      3: #endif
                      4: 
                      5: /*
                      6:  * Trivial file transfer protocol server.
                      7:  */
                      8: #include <sys/types.h>
                      9: #include <sys/socket.h>
                     10: #include <sys/ioctl.h>
                     11: #include <sys/wait.h>
                     12: #include <sys/stat.h>
                     13: 
                     14: #include <netinet/in.h>
                     15: 
                     16: #include <arpa/tftp.h>
                     17: 
                     18: #include <signal.h>
                     19: #include <stdio.h>
                     20: #include <errno.h>
                     21: #include <ctype.h>
                     22: #include <netdb.h>
                     23: #include <setjmp.h>
                     24: 
                     25: #define        TIMEOUT         5
                     26: 
                     27: extern int errno;
                     28: extern int sys_nerr;
                     29: extern char *sys_errlist[];
                     30: 
                     31: struct in_addr my_machine_addr;
                     32: struct sockaddr_in sin = { AF_INET };
                     33: int    f;
                     34: int    rexmtval = TIMEOUT;
                     35: int    maxtimeout = 5*TIMEOUT;
                     36: char   buf[BUFSIZ];
                     37: int    reapchild();
                     38: 
                     39: char   *inet_ntoa();
                     40: /*---------------------------------------------------------
                     41:  * Changes
                     42:  *
                     43:  *     . change declaration of tftp header for compiler independence.
                     44:  *
                     45:  *      . truncate the file after file transfer completes.
                     46:  *
                     47:  *      . the daemon keeps its master socket; it creates a new socket
                     48:  *       and hands this socket to its children. In the old implementation,
                     49:  *       the daemon hands the socket to its child, and creates a new socket;
                     50:  *       therefore, requests queued at old socket will be discarded, and
                     51:  *       requests arriving before the daemon successfully creates a new
                     52:  *       socket are also discarded.
                     53:  *
                     54:  *     . change userid and groupid to -2 before verifying the access
                     55:  *       rights. Temporary Fix Until An Official Policy Is Established.
                     56:  *
                     57:  *     . change perror() to macro PERROR() to ensure that error messages
                     58:  *       come to the console.
                     59:  *
                     60:  *     . let the system assign address to the child daemon.
                     61:  *--------------------------------------------------------*/
                     62: #define        PERROR(X) {                                                     \
                     63:                        int     cfd;                                    \
                     64:                        if ((cfd=open("/dev/console",2)) >=0) {         \
                     65:                                write(cfd,X,strlen(X));                 \
                     66:                                write(cfd,": ", 2);                     \
                     67:                                if (errno >=0 && errno <sys_nerr)       \
                     68:                                        write(cfd,sys_errlist[errno],   \
                     69:                                                strlen(sys_errlist[errno]));\
                     70:                                write(cfd,"\n",1);                      \
                     71:                                close(cfd);                             \
                     72:                        }                                               \
                     73:                }
                     74:        
                     75: main(argc, argv)
                     76:        char *argv[];
                     77: {
                     78:        struct sockaddr_in from;
                     79:        register struct tftphdr *tp;
                     80:        register int n;
                     81:        int     bindstat = -1;
                     82:        struct servent *sp;
                     83: 
                     84:        get_myaddrs();
                     85: 
                     86:        sp = getservbyname("tftp", "udp");
                     87:        if (sp == 0) {
                     88:                fprintf(stderr, "tftpd: udp/tftp: unknown service\n");
                     89:                exit(1);
                     90:        }
                     91:        sin.sin_port = sp->s_port;
                     92: #ifndef DEBUG
                     93:        if (fork())
                     94:                exit(0);
                     95:        for (f = 0; f < 10; f++)
                     96:                (void) close(f);
                     97:        (void) open("/", 0);
                     98:        (void) dup2(0, 1);
                     99:        (void) dup2(0, 2);
                    100:        { int t = open("/dev/tty", 2);
                    101:          if (t >= 0) {
                    102:                ioctl(t, TIOCNOTTY, (char *)0);
                    103:                (void) close(t);
                    104:          }
                    105:        }
                    106: #endif
                    107:        signal(SIGCHLD, reapchild);
                    108:        do {
                    109:                f = socket(AF_INET, SOCK_DGRAM, 0);
                    110:                if (f < 0) {
                    111:                        PERROR("tftpd: socket");
                    112:                        sleep(5);
                    113:                }
                    114:        } while (f<0);
                    115:        if (setsockopt(f, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0)
                    116:                PERROR("tftpd: setsockopt (SO_REUSEADDR)");
                    117:        for (n = 0; n<25; n++) {
                    118:                if ((bindstat = bind(f, (caddr_t)&sin, sizeof (sin), 0))>= 0) {
                    119:                        break;
                    120:                }
                    121:                else {
                    122:                        PERROR("tftpd: bind");
                    123:                        sleep(5);
                    124:                }
                    125:        }
                    126:        if (bindstat <0) {
                    127:                PERROR("tftpd: unable to bind. Terminated.\n");
                    128:                exit(1);
                    129:        }
                    130: 
                    131:        for (;;) {
                    132:                int fromlen;
                    133: 
                    134:                do {
                    135:                        
                    136:                        fromlen = sizeof (from);
                    137:                        from.sin_family = AF_INET;
                    138:                        n = recvfrom(f, buf, sizeof (buf), 0,
                    139:                            (caddr_t)&from, &fromlen);
                    140: #ifdef DEBUG
                    141:                        fprintf(stderr,"tftpd: got request fr %s %d.\n",
                    142:                                inet_ntoa(from.sin_addr), from.sin_port);
                    143: #endif
                    144:                } while (n <= 0);
                    145: 
                    146:                tp = (struct tftphdr *)buf;
                    147:                tp->th_opcode = ntohs(tp->th_opcode);
                    148:                if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) {
                    149: #ifdef DEBUG
                    150:                        fprintf(stderr,"tftpd: fork a child.\n");
                    151: #endif
                    152:                        if (fork() == 0) {
                    153:                                close(f);
                    154:                                do {
                    155:                                        f = socket(AF_INET, SOCK_DGRAM, 0);
                    156:                                        if (f < 0) {
                    157:                                                PERROR("tftpd: socket(child)");
                    158:                                                sleep(5);
                    159:                                        }
                    160:                                } while (f<0);
                    161: 
                    162:                                if (setsockopt(f, SOL_SOCKET, 
                    163:                                                SO_REUSEADDR, 0, 0) < 0)
                    164:                                        PERROR("tftpd: setsockopt(REUSEADDR)");
                    165: 
                    166:                                /* let the system assign the address */
                    167:                                sin.sin_addr.s_addr = 0;
                    168:                                sin.sin_port = 0;
                    169:                                while (bind(f, 
                    170:                                        (caddr_t)&sin, sizeof (sin), 0) < 0) {
                    171:                                        PERROR("tftpd: bind (child)");
                    172:                                        sleep(5);
                    173:                                }
                    174:                                tftp(&from, tp, n);
                    175:                        }
                    176:                }
                    177:        }
                    178: }
                    179: 
                    180: reapchild()
                    181: {
                    182:        union wait status;
                    183: 
                    184:        while (wait3(&status, WNOHANG, 0) > 0)
                    185:                ;
                    186: }
                    187: 
                    188: int    validate_access();
                    189: int    sendfile(), recvfile();
                    190: 
                    191: struct formats {
                    192:        char    *f_mode;
                    193:        int     (*f_validate)();
                    194:        int     (*f_send)();
                    195:        int     (*f_recv)();
                    196: } formats[] = {
                    197:        { "netascii",   validate_access,        sendfile,       recvfile },
                    198:        { "octet",      validate_access,        sendfile,       recvfile },
                    199: #ifdef notdef
                    200:        { "mail",       validate_user,          sendmail,       recvmail },
                    201: #endif
                    202:        { 0 }
                    203: };
                    204: 
                    205: FILE   *fd;                    /* file being transferred */
                    206: 
                    207: /*
                    208:  * Handle initial connection protocol.
                    209:  */
                    210: tftp(client, tp, size)
                    211:        struct sockaddr_in *client;
                    212:        struct tftphdr *tp;
                    213:        int size;
                    214: {
                    215:        register char *cp;
                    216:        int first = 1, ecode;
                    217:        register struct formats *pf;
                    218:        char *filename, *mode;
                    219: 
                    220: #ifdef DEBUG
                    221:        fprintf(stderr,"tftpd:sockfd %d, connect to %s %d\n",
                    222:                f, inet_ntoa(client->sin_addr), client->sin_port);
                    223: #endif
                    224:        if (connect(f, (caddr_t)client, sizeof (*client), 0) < 0) {
                    225:                PERROR("tftpd:connect");
                    226:                exit(1);
                    227:        }
                    228: #ifdef DEBUG
                    229:        {
                    230:        struct sockaddr_in myname;
                    231:        struct sockaddr_in peername;
                    232:        int     t;
                    233:        int     err = 0;
                    234: 
                    235:        myname.sin_family = AF_INET;
                    236:        t = sizeof(myname);
                    237:        if (getsockname(f, (caddr_t)&myname, &t) < 0) {
                    238:                PERROR("tftpd:getsockname");
                    239:                err = 1;
                    240:        }
                    241:        if (err!= 1)
                    242:                fprintf(stderr,"tftpd:myname is %s %d\n",
                    243:                        inet_ntoa(myname.sin_addr),
                    244:                        myname.sin_port);
                    245: 
                    246:        peername.sin_family = AF_INET;
                    247:        t = sizeof(peername);
                    248:        if (getpeername(f, (caddr_t)&peername, &t) < 0) {
                    249:                PERROR("tftpd:getpeername");
                    250:                err = 2;
                    251:        }
                    252:        if (err!=2)
                    253:                fprintf(stderr,"tftpd:peername is %s %d\n",
                    254:                        inet_ntoa(peername.sin_addr),
                    255:                        peername.sin_port);
                    256:        if (err)
                    257:                exit(1);
                    258:        }
                    259: #endif
                    260:        filename = cp = tp->th_stuff;
                    261: again:
                    262:        while (cp < buf + size) {
                    263:                if (*cp == '\0')
                    264:                        break;
                    265:                cp++;
                    266:        }
                    267:        if (*cp != '\0') {
                    268:                nak(EBADOP);
                    269:                exit(1);
                    270:        }
                    271:        if (first) {
                    272:                mode = ++cp;
                    273:                first = 0;
                    274:                goto again;
                    275:        }
                    276:        for (cp = mode; *cp; cp++)
                    277:                if (isupper(*cp))
                    278:                        *cp = tolower(*cp);
                    279:        for (pf = formats; pf->f_mode; pf++)
                    280:                if (strcmp(pf->f_mode, mode) == 0)
                    281:                        break;
                    282:        if (pf->f_mode == 0) {
                    283:                nak(EBADOP);
                    284:                exit(1);
                    285:        }
                    286:        ecode = (*pf->f_validate)(filename, client, tp->th_opcode);
                    287:        if (ecode) {
                    288:                nak(ecode);
                    289:                exit(1);
                    290:        }
                    291:        if (tp->th_opcode == WRQ)
                    292:                (*pf->f_recv)(pf);
                    293:        else
                    294:                (*pf->f_send)(pf);
                    295:        exit(0);
                    296: }
                    297: 
                    298: /*
                    299:  * Validate file access.  Since we
                    300:  * have no uid or gid, for now require
                    301:  * file to exist and be publicly
                    302:  * readable/writable.
                    303:  * Note also, full path name must be
                    304:  * given as we have no login directory.
                    305:  */
                    306: validate_access(file, client, mode)
                    307:        char *file;
                    308:        struct sockaddr_in *client;
                    309:        int mode;
                    310: {
                    311:        struct stat stbuf;
                    312: 
                    313:        if (*file != '/')
                    314:                return (EACCESS);
                    315: 
                    316:        /* temporary fix */
                    317:        if (setuid(-2) <0 ||
                    318:            setgid(-2) <0) {
                    319:                PERROR("tftpd: cannot setuid or setpid");
                    320:        }
                    321: 
                    322:        if (stat(file, &stbuf) < 0)
                    323:                return (errno == ENOENT ? ENOTFOUND : EACCESS);
                    324:        if (mode == RRQ) {
                    325:                if ((stbuf.st_mode&(S_IREAD >> 6)) == 0)
                    326:                        return (EACCESS);
                    327:        } else {
                    328:                if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0)
                    329:                        return (EACCESS);
                    330:        }
                    331:        fd = fopen(file, mode == RRQ ? "r" : "w");
                    332:        if (!fd)
                    333:                return (errno + 100);
                    334:        if (mode == WRQ) {
                    335:                if ((ftruncate(fileno(fd),0))== -1) {
                    336:                        PERROR("tftpd : truncate ");
                    337:                        return(errno + 100);
                    338:                        }
                    339:        }
                    340:        return (0);
                    341: }
                    342: 
                    343: int    timeout;
                    344: jmp_buf        timeoutbuf;
                    345: 
                    346: timer()
                    347: {
                    348: 
                    349:        timeout += rexmtval;
                    350:        if (timeout >= maxtimeout)
                    351:                exit(1);
                    352:        longjmp(timeoutbuf, 1);
                    353: }
                    354: 
                    355: /*
                    356:  * Send the requested file.
                    357:  */
                    358: sendfile(pf)
                    359:        struct format *pf;
                    360: {
                    361:        register struct tftphdr *tp;
                    362:        register int block = 1, size, n;
                    363:        int     fsize;
                    364: 
                    365:        signal(SIGALRM, timer);
                    366:        tp = (struct tftphdr *)buf;
                    367:        do {
                    368:                size = tftpread(fd, tp->th_data, SEGSIZE, &fsize);
                    369:                if (size < 0) {
                    370:                        nak(errno + 100);
                    371:                        goto abort;
                    372:                }
                    373:                tp->th_opcode = htons((u_short)DATA);
                    374:                tp->th_block = htons((u_short)block);
                    375:                timeout = 0;
                    376:                (void) setjmp(timeoutbuf);
                    377:                if (write(f, buf, size + 4) != size + 4) {
                    378:                        PERROR("tftpd: write");
                    379:                        goto abort;
                    380:                }
                    381:                do {
                    382:                        alarm(rexmtval);
                    383:                        n = read(f, buf, sizeof (buf));
                    384:                        alarm(0);
                    385:                        if (n < 0) {
                    386:                                PERROR("tftpd: read");
                    387:                                goto abort;
                    388:                        }
                    389:                        tp->th_opcode = ntohs((u_short)tp->th_opcode);
                    390:                        tp->th_block = ntohs((u_short)tp->th_block);
                    391:                        if (tp->th_opcode == ERROR)
                    392:                                goto abort;
                    393:                } while (tp->th_opcode != ACK || tp->th_block != block);
                    394:                block++;
                    395:        } while (size == SEGSIZE);
                    396: abort:
                    397:        (void) fclose(fd);
                    398: }
                    399: 
                    400: /*
                    401:  * Receive a file.
                    402:  */
                    403: recvfile(pf)
                    404:        struct format *pf;
                    405: {
                    406:        register struct tftphdr *tp;
                    407:        register int block = 0, n, size;
                    408:        int     fsize;
                    409: 
                    410:        signal(SIGALRM, timer);
                    411:        tp = (struct tftphdr *)buf;
                    412:        do {
                    413:                timeout = 0;
                    414:                tp->th_opcode = htons((u_short)ACK);
                    415:                tp->th_block = htons((u_short)block);
                    416:                block++;
                    417:                (void) setjmp(timeoutbuf);
                    418:                if (write(f, buf, 4) != 4) {
                    419:                        PERROR("tftpd: write");
                    420:                        goto abort;
                    421:                }
                    422:                do {
                    423:                        alarm(rexmtval);
                    424:                        n = read(f, buf, sizeof (buf));
                    425:                        alarm(0);
                    426:                        if (n < 0) {
                    427:                                PERROR("tftpd: read");
                    428:                                goto abort;
                    429:                        }
                    430:                        tp->th_opcode = ntohs((u_short)tp->th_opcode);
                    431:                        tp->th_block = ntohs((u_short)tp->th_block);
                    432:                        if (tp->th_opcode == ERROR)
                    433:                                goto abort;
                    434:                } while (tp->th_opcode != DATA || block != tp->th_block);
                    435:                size = n -4;
                    436:                n = tftpwrite(fd, tp->th_data, size, &fsize);
                    437:                if (n < 0) {
                    438:                        nak(errno + 100);
                    439:                        goto abort;
                    440:                }
                    441:        } while (size == SEGSIZE);
                    442: abort:
                    443:        tp->th_opcode = htons((u_short)ACK);
                    444:        tp->th_block = htons((u_short)(block));
                    445:        (void) write(f, buf, 4);
                    446:        (void) fclose(fd);
                    447: }
                    448: 
                    449: struct errmsg {
                    450:        int     e_code;
                    451:        char    *e_msg;
                    452: } errmsgs[] = {
                    453:        { EUNDEF,       "Undefined error code" },
                    454:        { ENOTFOUND,    "File not found" },
                    455:        { EACCESS,      "Access violation" },
                    456:        { ENOSPACE,     "Disk full or allocation exceeded" },
                    457:        { EBADOP,       "Illegal TFTP operation" },
                    458:        { EBADID,       "Unknown transfer ID" },
                    459:        { EEXISTS,      "File already exists" },
                    460:        { ENOUSER,      "No such user" },
                    461:        { -1,           0 }
                    462: };
                    463: 
                    464: /*
                    465:  * Send a nak packet (error message).
                    466:  * Error code passed in is one of the
                    467:  * standard TFTP codes, or a UNIX errno
                    468:  * offset by 100.
                    469:  */
                    470: nak(error)
                    471:        int error;
                    472: {
                    473:        register struct tftphdr *tp;
                    474:        int length;
                    475:        register struct errmsg *pe;
                    476: 
                    477:        tp = (struct tftphdr *)buf;
                    478:        tp->th_opcode = htons((u_short)ERROR);
                    479:        tp->th_code = htons((u_short)error);
                    480:        for (pe = errmsgs; pe->e_code >= 0; pe++)
                    481:                if (pe->e_code == error)
                    482:                        break;
                    483:        if (pe->e_code < 0)
                    484:                pe->e_msg = sys_errlist[error - 100];
                    485:        strcpy(tp->th_msg, pe->e_msg);
                    486:        length = strlen(pe->e_msg);
                    487:        tp->th_msg[length] = '\0';
                    488:        length += 5;
                    489:        if (write(f, buf, length) != length)
                    490:                PERROR("tftpd:nak");
                    491: }
                    492: get_myaddrs()
                    493: {
                    494:     struct hostent *hp;
                    495:     char       my_machine_name[BUFSIZ];
                    496: 
                    497:     /* look up the address of the local host */
                    498:     if (gethostname(my_machine_name, sizeof(my_machine_name)) <0) {
                    499:        perror("tftpd:cannot get this host name");
                    500:        exit(-1);
                    501:     }
                    502: 
                    503:     hp = gethostbyname(my_machine_name);
                    504:     if (hp == (struct hostent *) 0) {
                    505:        fprintf(stderr,"tftpd:cannot get host address");
                    506:        exit(-2);
                    507:     }
                    508: 
                    509:     if (hp->h_addrtype != AF_INET) {
                    510:        fprintf(stderr,"Protocal mix up with local machine address\n");
                    511:        exit(-1);
                    512:     }
                    513: 
                    514:     bcopy(hp->h_addr, (char *)&my_machine_addr, hp->h_length);
                    515: }
                    516: 
                    517: bcopy(fr, to, size)
                    518: register char  *fr;
                    519: register char  *to;
                    520: register int   size;
                    521: {
                    522:        while (size-->0)
                    523:                *to++ = *fr++;
                    524: }
                    525: 
                    526: #include <stdio.h>
                    527: 
                    528: /*-----------------------------------------
                    529:   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                    530:   >>>>>>>>>>>> NOTE >>>>>>>>>>>>> Must be identical to those in "tftp"
                    531:   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                    532:   read from local file and transform lines ending with '\n' to lines
                    533:   ending with '\r' and '\n'; also transform '\r' to '\r' and '\0'
                    534:   ----------------------------------------*/
                    535: tftpread(fd, buf, bufsize, fsize)
                    536: FILE   *fd;
                    537: register char  *buf;
                    538: int    bufsize;
                    539: int    *fsize;                 /* count # bytes read from file */
                    540: {
                    541: 
                    542:        register char   *p;
                    543:        int     i;
                    544: 
                    545:        /* oldc is to indicate if the transformation is unfinished in the
                    546:           last block */
                    547:        static  char    oldc;
                    548: 
                    549:        *fsize = 0;
                    550:        for (p=buf; p < buf+bufsize;) {
                    551:                switch(oldc) {
                    552:                case '\n':
                    553:                        *p++ = '\n';            /* already output '\r' */
                    554:                        oldc = '\0';
                    555:                        break;
                    556:                case '\r':
                    557:                        *p++ = '\0';            /* already output '\r' */
                    558:                        oldc = '\0';
                    559:                        break;
                    560:                default:
                    561:                        i = fread(&oldc, sizeof(oldc), 1, fd);
                    562:                        if (i<0)
                    563:                                return(i);
                    564:                        if (i==0)
                    565:                                goto endit;
                    566: 
                    567:                        ++(*fsize);
                    568:                        switch(oldc) {
                    569:                        case '\n':
                    570:                        case '\r':
                    571:                                *p++ = '\r';
                    572:                                /* will complete the transformation in next
                    573:                                   ... loop iteration */
                    574:                                break;
                    575:                        default:
                    576:                                *p++ = oldc;
                    577:                                break;
                    578:                        }
                    579:                }
                    580: 
                    581:        }
                    582: endit:
                    583:        return(p-buf);
                    584: }
                    585: /*-----------------------------------------
                    586:   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                    587:   >>>>>>>>>>>>>>>> NOTE >>>>>>>>> Must be identical to those "tftp"
                    588:   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                    589:   write to local file and transform lines ending with '\r''\n' to lines
                    590:   ending with and '\n'; also transform '\r' '\0' to '\r'.
                    591:   ----------------------------------------*/
                    592: tftpwrite(fd, buf, bufsize, fsize)
                    593: FILE   *fd;
                    594: register char  *buf;
                    595: int    bufsize;
                    596: int    *fsize;                 /* count # of bytes write to file */
                    597: {
                    598:        register char   *p;
                    599:        char    c;
                    600:        int     i;
                    601: 
                    602:        /* oldc is to indicate if the transformation is unfinished in the
                    603:           last block */
                    604:        static  char    oldc;
                    605: 
                    606:        *fsize = 0;
                    607:        for  (p=buf; p<buf + bufsize; p++) {
                    608:                c = *p;
                    609:                if (oldc == '\r') {
                    610:                        char    writethisc;
                    611: 
                    612:                        switch(c) {
                    613:                        case '\0':
                    614:                                writethisc = oldc;
                    615:                                break;
                    616:                        case '\n':
                    617:                                writethisc = '\n';
                    618:                                break;
                    619:                        default:
                    620:                                writethisc = oldc;
                    621:                                break;
                    622:                        }
                    623:                        i = fwrite(&writethisc, sizeof(writethisc), 1, fd);
                    624:                        if (i!=1)
                    625:                                return(-1);
                    626:                        ++(*fsize);
                    627:                        if (c=='\0' ||
                    628:                            c=='\n') {
                    629:                                oldc = '\0';
                    630:                                continue;
                    631:                        }
                    632:                }
                    633:                if ((oldc = c) == '\r')
                    634:                        continue;
                    635:                i = fwrite(&c, sizeof(c), 1, fd);
                    636:                if (i!=1)
                    637:                        return(-1);
                    638:                ++(*fsize);
                    639:        }
                    640:        return(*fsize);
                    641: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.