[BACK]Return to tftp.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tftp

Annotation of src/usr.bin/tftp/tftp.c, Revision 1.16

1.16    ! claudio     1: /*     $OpenBSD: tftp.c,v 1.15 2003/09/24 20:21:40 deraadt Exp $       */
1.1       deraadt     2: /*     $NetBSD: tftp.c,v 1.5 1995/04/29 05:55:25 cgd Exp $     */
                      3:
                      4: /*
                      5:  * Copyright (c) 1983, 1993
                      6:  *     The Regents of the University of California.  All rights reserved.
                      7:  *
                      8:  * Redistribution and use in source and binary forms, with or without
                      9:  * modification, are permitted provided that the following conditions
                     10:  * are met:
                     11:  * 1. Redistributions of source code must retain the above copyright
                     12:  *    notice, this list of conditions and the following disclaimer.
                     13:  * 2. Redistributions in binary form must reproduce the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer in the
                     15:  *    documentation and/or other materials provided with the distribution.
1.12      millert    16:  * 3. Neither the name of the University nor the names of its contributors
1.1       deraadt    17:  *    may be used to endorse or promote products derived from this software
                     18:  *    without specific prior written permission.
                     19:  *
                     20:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     21:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     22:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     23:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     24:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     25:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     26:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     27:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     28:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     29:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     30:  * SUCH DAMAGE.
                     31:  */
                     32:
                     33: #ifndef lint
                     34: #if 0
                     35: static char sccsid[] = "@(#)tftp.c     8.1 (Berkeley) 6/6/93";
                     36: #endif
1.16    ! claudio    37: static const char rcsid[] = "$OpenBSD: tftp.c,v 1.15 2003/09/24 20:21:40 deraadt Exp $";
1.1       deraadt    38: #endif /* not lint */
                     39:
                     40: /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
                     41:
                     42: /*
                     43:  * TFTP User Program -- Protocol Machines
                     44:  */
1.16    ! claudio    45:
1.1       deraadt    46: #include <sys/types.h>
                     47: #include <sys/socket.h>
                     48: #include <sys/time.h>
                     49:
                     50: #include <netinet/in.h>
                     51: #include <arpa/tftp.h>
                     52:
                     53: #include <errno.h>
1.16    ! claudio    54: #include <poll.h>
1.1       deraadt    55: #include <signal.h>
                     56: #include <stdio.h>
1.11      henning    57: #include <stddef.h>
1.1       deraadt    58: #include <string.h>
                     59: #include <unistd.h>
1.7       mickey     60: #include <err.h>
1.1       deraadt    61:
                     62: #include "extern.h"
                     63: #include "tftpsubs.h"
                     64:
1.16    ! claudio    65: #define        PKTSIZE SEGSIZE + 4
        !            66:
        !            67: extern struct sockaddr_in      peeraddr;       /* filled in by main */
        !            68: extern int                     f;              /* the opened socket */
        !            69: extern int                     trace;
        !            70: extern int                     verbose;
        !            71: extern int                     rexmtval;
        !            72: extern int                     maxtimeout;
        !            73: extern FILE                    *file;
        !            74: extern volatile sig_atomic_t   intrflag;
1.1       deraadt    75:
1.16    ! claudio    76: char   ackbuf[PKTSIZE];
        !            77:
        !            78: struct timeval tstart;
        !            79: struct timeval tstop;
        !            80:
        !            81: struct errmsg {
        !            82:        int     e_code;
        !            83:        char    *e_msg;
        !            84: } errmsgs[] = {
        !            85:        { EUNDEF,       "Undefined error code" },
        !            86:        { ENOTFOUND,    "File not found" },
        !            87:        { EACCESS,      "Access violation" },
        !            88:        { ENOSPACE,     "Disk full or allocation exceeded" },
        !            89:        { EBADOP,       "Illegal TFTP operation" },
        !            90:        { EBADID,       "Unknown transfer ID" },
        !            91:        { EEXISTS,      "File already exists" },
        !            92:        { ENOUSER,      "No such user" },
        !            93:        { -1,           NULL }
        !            94: };
        !            95:
        !            96: static int     makerequest(int, const char *, struct tftphdr *, const char *);
        !            97: static void    nak(int);
        !            98: static void    tpacket(const char *, struct tftphdr *, int);
        !            99: static void    startclock(void);
        !           100: static void    stopclock(void);
        !           101: static void    printstats(const char *, unsigned long);
        !           102: static void    printtimeout(void);
1.1       deraadt   103:
                    104: /*
                    105:  * Send the requested file.
                    106:  */
                    107: void
1.14      deraadt   108: sendfile(int fd, char *name, char *mode)
1.1       deraadt   109: {
1.16    ! claudio   110:        struct tftphdr *dp, *ap;        /* data and ack packets */
1.1       deraadt   111:        struct sockaddr_in from;
1.16    ! claudio   112:        struct pollfd pfd[1];
        !           113:        unsigned long amount;
        !           114:        int convert;                    /* true if converting crlf -> lf */
        !           115:        int n, nfds, error, fromlen, timeouts, block, size;
1.1       deraadt   116:
                    117:        startclock();           /* start stat's clock */
                    118:        dp = r_init();          /* reset fillbuf/read-ahead code */
                    119:        ap = (struct tftphdr *)ackbuf;
                    120:        file = fdopen(fd, "r");
                    121:        convert = !strcmp(mode, "netascii");
                    122:        block = 0;
                    123:        amount = 0;
                    124:
                    125:        do {
1.16    ! claudio   126:                /* read data from file */
        !           127:                if (!block)
1.1       deraadt   128:                        size = makerequest(WRQ, name, dp, mode) - 4;
                    129:                else {
                    130:                        size = readit(file, &dp, convert);
                    131:                        if (size < 0) {
                    132:                                nak(errno + 100);
                    133:                                break;
                    134:                        }
                    135:                        dp->th_opcode = htons((u_short)DATA);
                    136:                        dp->th_block = htons((u_short)block);
                    137:                }
1.16    ! claudio   138:
        !           139:                /* send data to server and wait for server ACK */
        !           140:                for (timeouts = 0, error = 0; !intrflag;) {
        !           141:                        if (timeouts == maxtimeout) {
        !           142:                                printtimeout();
        !           143:                                goto abort;
        !           144:                        }
        !           145:
        !           146:                        if (!error) {
        !           147:                                if (trace)
        !           148:                                        tpacket("sent", dp, size + 4);
        !           149:                                if (sendto(f, dp, size + 4, 0,
        !           150:                                    (struct sockaddr *)&peeraddr,
        !           151:                                    sizeof(peeraddr)) != size + 4) {
        !           152:                                        warn("sendto");
        !           153:                                        goto abort;
        !           154:                                }
        !           155:                                read_ahead(file, convert);
        !           156:                        }
        !           157:                        error = 0;
        !           158:
        !           159:                        pfd[0].fd = f;
        !           160:                        pfd[0].events = POLLIN;
        !           161:                        nfds = poll(pfd, 1, rexmtval * 1000);
        !           162:                        if (nfds == 0) {
        !           163:                                timeouts++;
        !           164:                                continue;
        !           165:                        }
        !           166:                        if (nfds == -1) {
        !           167:                                error = 1;
        !           168:                                if (errno == EINTR)
        !           169:                                        continue;
        !           170:                                warn("poll");
        !           171:                                goto abort;
        !           172:                        }
        !           173:                        fromlen = sizeof(from);
        !           174:                        n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
        !           175:                            (struct sockaddr *)&from, &fromlen);
        !           176:                        if (n == 0) {
        !           177:                                warn("recvfrom");
        !           178:                                goto abort;
        !           179:                        }
        !           180:                        if (n == -1) {
        !           181:                                error = 1;
        !           182:                                if (errno == EINTR)
        !           183:                                        continue;
1.7       mickey    184:                                warn("recvfrom");
1.1       deraadt   185:                                goto abort;
                    186:                        }
                    187:                        peeraddr.sin_port = from.sin_port;      /* added */
                    188:                        if (trace)
                    189:                                tpacket("received", ap, n);
                    190:                        ap->th_opcode = ntohs(ap->th_opcode);
                    191:                        ap->th_block = ntohs(ap->th_block);
1.16    ! claudio   192:
1.1       deraadt   193:                        if (ap->th_opcode == ERROR) {
1.16    ! claudio   194:                                printf("Error code %d: %s\n",
        !           195:                                    ap->th_code, ap->th_msg);
1.1       deraadt   196:                                goto abort;
                    197:                        }
                    198:                        if (ap->th_opcode == ACK) {
                    199:                                int j;
1.16    ! claudio   200:                                if (ap->th_block == block)
1.1       deraadt   201:                                        break;
1.16    ! claudio   202:                                /* re-synchronize with other side */
1.1       deraadt   203:                                j = synchnet(f);
1.15      deraadt   204:                                if (j && trace)
                    205:                                        printf("discarded %d packets\n", j);
1.16    ! claudio   206:                                if (ap->th_block == (block - 1))
        !           207:                                        continue;
1.1       deraadt   208:                        }
1.16    ! claudio   209:                        error = 1;      /* received packet does not match */
1.1       deraadt   210:                }
1.16    ! claudio   211:
1.1       deraadt   212:                if (block > 0)
                    213:                        amount += size;
                    214:                block++;
1.16    ! claudio   215:        } while ((size == SEGSIZE || block == 1) && !intrflag);
        !           216:
1.1       deraadt   217: abort:
                    218:        fclose(file);
                    219:        stopclock();
1.16    ! claudio   220:        if (amount > 0) {
        !           221:                if (intrflag)
        !           222:                        putchar('\n');
1.1       deraadt   223:                printstats("Sent", amount);
1.16    ! claudio   224:        }
1.1       deraadt   225: }
                    226:
                    227: /*
                    228:  * Receive a file.
                    229:  */
                    230: void
1.14      deraadt   231: recvfile(int fd, char *name, char *mode)
1.1       deraadt   232: {
1.16    ! claudio   233:        struct tftphdr *dp, *ap;        /* data and ack packets */
1.1       deraadt   234:        struct sockaddr_in from;
1.16    ! claudio   235:        struct pollfd pfd[1];
        !           236:        unsigned long amount;
        !           237:        int convert;                    /* true if converting crlf -> lf */
        !           238:        int n, nfds, error, fromlen, timeouts, block, size, firsttrip;
1.1       deraadt   239:
1.16    ! claudio   240:        startclock();           /* start stat's clock */
        !           241:        dp = w_init();          /* reset fillbuf/read-ahead code */
1.1       deraadt   242:        ap = (struct tftphdr *)ackbuf;
                    243:        file = fdopen(fd, "w");
                    244:        convert = !strcmp(mode, "netascii");
1.16    ! claudio   245:        n = 0;
1.1       deraadt   246:        block = 1;
1.16    ! claudio   247:        amount = 0;
1.1       deraadt   248:        firsttrip = 1;
                    249:
                    250:        do {
1.16    ! claudio   251:                /* create new ACK packet */
1.1       deraadt   252:                if (firsttrip) {
                    253:                        size = makerequest(RRQ, name, ap, mode);
                    254:                        firsttrip = 0;
                    255:                } else {
                    256:                        ap->th_opcode = htons((u_short)ACK);
                    257:                        ap->th_block = htons((u_short)(block));
                    258:                        size = 4;
                    259:                        block++;
                    260:                }
1.16    ! claudio   261:
        !           262:                /* send ACK to server and wait for server data */
        !           263:                for (timeouts = 0, error = 0; !intrflag;) {
        !           264:                        if (timeouts == maxtimeout) {
        !           265:                                printtimeout();
        !           266:                                goto abort;
        !           267:                        }
        !           268:
        !           269:                        if (!error) {
        !           270:                                if (trace)
        !           271:                                        tpacket("sent", ap, size);
        !           272:                                if (sendto(f, ackbuf, size, 0,
        !           273:                                    (struct sockaddr *)&peeraddr,
        !           274:                                    sizeof(peeraddr)) != size) {
        !           275:                                        warn("sendto");
        !           276:                                        goto abort;
        !           277:                                }
        !           278:                                write_behind(file, convert);
        !           279:                        }
        !           280:                        error = 0;
        !           281:
        !           282:                        pfd[0].fd = f;
        !           283:                        pfd[0].events = POLLIN;
        !           284:                        nfds = poll(pfd, 1, rexmtval * 1000);
        !           285:                        if (nfds == 0) {
        !           286:                                timeouts++;
        !           287:                                continue;
        !           288:                        }
        !           289:                        if (nfds == -1) {
        !           290:                                error = 1;
        !           291:                                if (errno == EINTR)
        !           292:                                        continue;
        !           293:                                warn("poll");
        !           294:                                goto abort;
        !           295:                        }
        !           296:                        fromlen = sizeof(from);
        !           297:                        n = recvfrom(f, dp, PKTSIZE, 0,
        !           298:                            (struct sockaddr *)&from, &fromlen);
        !           299:                        if (n == 0) {
        !           300:                                warn("recvfrom");
        !           301:                                goto abort;
        !           302:                        }
        !           303:                        if (n == -1) {
        !           304:                                error = 1;
        !           305:                                if (errno == EINTR)
        !           306:                                        continue;
1.7       mickey    307:                                warn("recvfrom");
1.1       deraadt   308:                                goto abort;
                    309:                        }
                    310:                        peeraddr.sin_port = from.sin_port;      /* added */
                    311:                        if (trace)
                    312:                                tpacket("received", dp, n);
                    313:                        dp->th_opcode = ntohs(dp->th_opcode);
                    314:                        dp->th_block = ntohs(dp->th_block);
1.16    ! claudio   315:
1.1       deraadt   316:                        if (dp->th_opcode == ERROR) {
1.16    ! claudio   317:                                printf("Error code %d: %s\n",
        !           318:                                    dp->th_code, dp->th_msg);
1.1       deraadt   319:                                goto abort;
                    320:                        }
                    321:                        if (dp->th_opcode == DATA) {
                    322:                                int j;
1.16    ! claudio   323:                                if (dp->th_block == block)
        !           324:                                        break;
        !           325:                                /* re-synchronize with other side */
1.1       deraadt   326:                                j = synchnet(f);
1.15      deraadt   327:                                if (j && trace)
1.1       deraadt   328:                                        printf("discarded %d packets\n", j);
1.16    ! claudio   329:                                if (dp->th_block == (block - 1))
        !           330:                                        continue;
1.1       deraadt   331:                        }
1.16    ! claudio   332:                        error = 1;      /* received packet does not match */
1.1       deraadt   333:                }
1.16    ! claudio   334:
        !           335:                /* write data to file */
1.1       deraadt   336:                size = writeit(file, &dp, n - 4, convert);
                    337:                if (size < 0) {
                    338:                        nak(errno + 100);
                    339:                        break;
                    340:                }
                    341:                amount += size;
1.16    ! claudio   342:        } while (size == SEGSIZE && !intrflag);
        !           343:
        !           344: abort:
        !           345:        /* ok to ack, since user has seen err msg */
        !           346:        ap->th_opcode = htons((u_short)ACK);
1.1       deraadt   347:        ap->th_block = htons((u_short)block);
                    348:        (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
                    349:            sizeof(peeraddr));
1.16    ! claudio   350:        write_behind(file, convert);    /* flush last buffer */
        !           351:
1.1       deraadt   352:        fclose(file);
                    353:        stopclock();
1.16    ! claudio   354:        if (amount > 0) {
        !           355:                if (intrflag)
        !           356:                        putchar('\n');
1.1       deraadt   357:                printstats("Received", amount);
1.16    ! claudio   358:        }
1.1       deraadt   359: }
                    360:
                    361: static int
1.14      deraadt   362: makerequest(int request, const char *name, struct tftphdr *tp,
                    363:     const char *mode)
1.1       deraadt   364: {
1.9       mpech     365:        char *cp;
1.11      henning   366:        int len, pktlen;
1.1       deraadt   367:
                    368:        tp->th_opcode = htons((u_short)request);
                    369:        cp = tp->th_stuff;
1.11      henning   370:        pktlen = PKTSIZE - offsetof(struct tftphdr, th_stuff);
                    371:        len = strlen(name) + 1;
                    372:        strlcpy(cp, name, pktlen);
                    373:        strlcpy(cp + len, mode, pktlen - len);
                    374:        len += strlen(mode) + 1;
                    375:        return (cp + len - (char *)tp);
1.1       deraadt   376: }
                    377:
                    378: /*
                    379:  * Send a nak packet (error message).
                    380:  * Error code passed in is one of the
                    381:  * standard TFTP codes, or a UNIX errno
                    382:  * offset by 100.
                    383:  */
                    384: static void
1.14      deraadt   385: nak(int error)
1.1       deraadt   386: {
1.9       mpech     387:        struct errmsg *pe;
                    388:        struct tftphdr *tp;
1.1       deraadt   389:        int length;
                    390:
                    391:        tp = (struct tftphdr *)ackbuf;
                    392:        tp->th_opcode = htons((u_short)ERROR);
                    393:        tp->th_code = htons((u_short)error);
                    394:        for (pe = errmsgs; pe->e_code >= 0; pe++)
                    395:                if (pe->e_code == error)
                    396:                        break;
                    397:        if (pe->e_code < 0) {
                    398:                pe->e_msg = strerror(error - 100);
                    399:                tp->th_code = EUNDEF;
                    400:        }
1.8       mpech     401:        length = strlcpy(tp->th_msg, pe->e_msg, sizeof(ackbuf)) + 5;
                    402:        if (length > sizeof(ackbuf))
                    403:                length = sizeof(ackbuf);
1.1       deraadt   404:        if (trace)
                    405:                tpacket("sent", tp, length);
                    406:        if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
                    407:            sizeof(peeraddr)) != length)
1.7       mickey    408:                warn("nak");
1.1       deraadt   409: }
                    410:
                    411: static void
1.14      deraadt   412: tpacket(const char *s, struct tftphdr *tp, int n)
1.1       deraadt   413: {
                    414:        static char *opcodes[] =
1.16    ! claudio   415:            { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
1.9       mpech     416:        char *cp, *file;
1.1       deraadt   417:        u_short op = ntohs(tp->th_opcode);
                    418:
                    419:        if (op < RRQ || op > ERROR)
                    420:                printf("%s opcode=%x ", s, op);
                    421:        else
                    422:                printf("%s %s ", s, opcodes[op]);
1.16    ! claudio   423:
1.1       deraadt   424:        switch (op) {
                    425:        case RRQ:
                    426:        case WRQ:
                    427:                n -= 2;
                    428:                file = cp = tp->th_stuff;
1.3       millert   429:                cp = strchr(cp, '\0');
1.1       deraadt   430:                printf("<file=%s, mode=%s>\n", file, cp + 1);
                    431:                break;
                    432:        case DATA:
                    433:                printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
                    434:                break;
                    435:        case ACK:
                    436:                printf("<block=%d>\n", ntohs(tp->th_block));
                    437:                break;
                    438:        case ERROR:
                    439:                printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
                    440:                break;
                    441:        }
                    442: }
                    443:
                    444: static void
1.13      deraadt   445: startclock(void)
1.1       deraadt   446: {
1.16    ! claudio   447:        (void) gettimeofday(&tstart, NULL);
1.1       deraadt   448: }
                    449:
                    450: static void
1.13      deraadt   451: stopclock(void)
1.1       deraadt   452: {
1.16    ! claudio   453:        (void) gettimeofday(&tstop, NULL);
1.1       deraadt   454: }
                    455:
                    456: static void
1.14      deraadt   457: printstats(const char *direction, unsigned long amount)
1.1       deraadt   458: {
                    459:        double delta;
1.15      deraadt   460:
                    461:        /* compute delta in 1/10's second units */
1.16    ! claudio   462:        delta = ((tstop.tv_sec * 10.) + (tstop.tv_usec / 100000)) -
        !           463:            ((tstart.tv_sec * 10.) + (tstart.tv_usec / 100000));
        !           464:        delta = delta / 10.;    /* back to seconds */
1.6       deraadt   465:        printf("%s %lu bytes in %.1f seconds", direction, amount, delta);
1.1       deraadt   466:        if (verbose)
1.16    ! claudio   467:                printf(" [%.0f bits/sec]", (amount * 8.) / delta);
1.1       deraadt   468:        putchar('\n');
                    469: }
                    470:
                    471: static void
1.16    ! claudio   472: printtimeout(void)
1.1       deraadt   473: {
1.16    ! claudio   474:        printf("Transfer timed out.\n");
1.1       deraadt   475: }