[BACK]Return to tftp.c CVS log [TXT][DIR] Up to [local] / sys / lib / libsa

Annotation of sys/lib/libsa/tftp.c, Revision 1.1.1.1

1.1       nbrk        1: /*     $OpenBSD: tftp.c,v 1.2 2004/04/02 04:39:51 deraadt Exp $        */
                      2: /*     $NetBSD: tftp.c,v 1.15 2003/08/18 15:45:29 dsl Exp $     */
                      3:
                      4: /*
                      5:  * Copyright (c) 1996
                      6:  *     Matthias Drochner.  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.
                     16:  *
                     17:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
                     18:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
                     19:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
                     20:  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
                     21:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
                     22:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
                     23:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
                     24:  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     25:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
                     26:  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     27:  */
                     28:
                     29: /*
                     30:  * Simple TFTP implementation for libsa.
                     31:  * Assumes:
                     32:  *  - socket descriptor (int) at open_file->f_devdata
                     33:  *  - server host IP in global servip
                     34:  * Restrictions:
                     35:  *  - read only
                     36:  *  - lseek only with SEEK_SET or SEEK_CUR
                     37:  *  - no big time differences between transfers (<tftp timeout)
                     38:  */
                     39:
                     40: /*
                     41:  * XXX Does not currently implement:
                     42:  * XXX
                     43:  * XXX LIBSA_NO_FS_CLOSE
                     44:  * XXX LIBSA_NO_FS_SEEK
                     45:  * XXX LIBSA_NO_FS_WRITE
                     46:  * XXX LIBSA_NO_FS_SYMLINK (does this even make sense?)
                     47:  * XXX LIBSA_FS_SINGLECOMPONENT (does this even make sense?)
                     48:  */
                     49:
                     50: #include <sys/types.h>
                     51: #include <sys/stat.h>
                     52: #include <netinet/in.h>
                     53: #include <netinet/udp.h>
                     54: #include <netinet/in_systm.h>
                     55: #include <lib/libkern/libkern.h>
                     56:
                     57: #include "stand.h"
                     58: #include "net.h"
                     59: #include "netif.h"
                     60:
                     61: #include "tftp.h"
                     62:
                     63: extern struct in_addr servip;
                     64:
                     65: static int      tftpport = 2000;
                     66:
                     67: #define RSPACE 520             /* max data packet, rounded up */
                     68:
                     69: struct tftp_handle {
                     70:        struct iodesc  *iodesc;
                     71:        int             currblock;      /* contents of lastdata */
                     72:        int             islastblock;    /* flag */
                     73:        int             validsize;
                     74:        int             off;
                     75:        const char     *path;   /* saved for re-requests */
                     76:        struct {
                     77:                u_char header[HEADER_SIZE];
                     78:                struct tftphdr t;
                     79:                u_char space[RSPACE];
                     80:        } lastdata;
                     81: };
                     82:
                     83: static const int tftperrors[8] = {
                     84:        0,                      /* ??? */
                     85:        ENOENT,
                     86:        EPERM,
                     87:        ENOSPC,
                     88:        EINVAL,                 /* ??? */
                     89:        EINVAL,                 /* ??? */
                     90:        EEXIST,
                     91:        EINVAL                  /* ??? */
                     92: };
                     93:
                     94: ssize_t recvtftp(struct iodesc *, void *, size_t, time_t);
                     95: int tftp_makereq(struct tftp_handle *);
                     96: int tftp_getnextblock(struct tftp_handle *);
                     97: #ifndef TFTP_NOTERMINATE
                     98: void tftp_terminate(struct tftp_handle *);
                     99: #endif
                    100:
                    101: ssize_t
                    102: recvtftp(struct iodesc *d, void *pkt, size_t len, time_t tleft)
                    103: {
                    104:        ssize_t n;
                    105:        struct tftphdr *t;
                    106:
                    107:        errno = 0;
                    108:
                    109:        n = readudp(d, pkt, len, tleft);
                    110:
                    111:        if (n < 4)
                    112:                return -1;
                    113:
                    114:        t = (struct tftphdr *) pkt;
                    115:        switch (ntohs(t->th_opcode)) {
                    116:        case DATA:
                    117:                if (htons(t->th_block) != d->xid) {
                    118:                        /*
                    119:                         * Expected block?
                    120:                         */
                    121:                        return -1;
                    122:                }
                    123:                if (d->xid == 1) {
                    124:                        /*
                    125:                         * First data packet from new port.
                    126:                         */
                    127:                        struct udphdr *uh;
                    128:                        uh = (struct udphdr *) pkt - 1;
                    129:                        d->destport = uh->uh_sport;
                    130:                } /* else check uh_sport has not changed??? */
                    131:                return (n - (t->th_data - (char *)t));
                    132:        case ERROR:
                    133:                if ((unsigned) ntohs(t->th_code) >= 8) {
                    134:                        printf("illegal tftp error %d\n", ntohs(t->th_code));
                    135:                        errno = EIO;
                    136:                } else {
                    137: #ifdef DEBUG
                    138:                        printf("tftp-error %d\n", ntohs(t->th_code));
                    139: #endif
                    140:                        errno = tftperrors[ntohs(t->th_code)];
                    141:                }
                    142:                return -1;
                    143:        default:
                    144: #ifdef DEBUG
                    145:                printf("tftp type %d not handled\n", ntohs(t->th_opcode));
                    146: #endif
                    147:                return -1;
                    148:        }
                    149: }
                    150:
                    151: /* send request, expect first block (or error) */
                    152: int
                    153: tftp_makereq(struct tftp_handle *h)
                    154: {
                    155:        struct {
                    156:                u_char header[HEADER_SIZE];
                    157:                struct tftphdr  t;
                    158:                u_char space[FNAME_SIZE + 6];
                    159:        } wbuf;
                    160:        char           *wtail;
                    161:        int             l;
                    162:        ssize_t         res;
                    163:        struct tftphdr *t;
                    164:
                    165:        bzero(&wbuf, sizeof(wbuf));
                    166:
                    167:        wbuf.t.th_opcode = htons((u_short) RRQ);
                    168:        wtail = wbuf.t.th_stuff;
                    169:        l = strlen(h->path);
                    170:        bcopy(h->path, wtail, l + 1);
                    171:        wtail += l + 1;
                    172:        bcopy("octet", wtail, 6);
                    173:        wtail += 6;
                    174:
                    175:        t = &h->lastdata.t;
                    176:
                    177:        /* h->iodesc->myport = htons(--tftpport); */
                    178:        h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff));
                    179:        h->iodesc->destport = htons(IPPORT_TFTP);
                    180:        h->iodesc->xid = 1;     /* expected block */
                    181:
                    182:        res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
                    183:            recvtftp, t, sizeof(*t) + RSPACE);
                    184:
                    185:        if (res == -1)
                    186:                return errno;
                    187:
                    188:        h->currblock = 1;
                    189:        h->validsize = res;
                    190:        h->islastblock = 0;
                    191:        if (res < SEGSIZE)
                    192:                h->islastblock = 1;     /* very short file */
                    193:        return 0;
                    194: }
                    195:
                    196: /* ack block, expect next */
                    197: int
                    198: tftp_getnextblock(struct tftp_handle *h)
                    199: {
                    200:        struct {
                    201:                u_char header[HEADER_SIZE];
                    202:                struct tftphdr t;
                    203:        } wbuf;
                    204:        char           *wtail;
                    205:        int             res;
                    206:        struct tftphdr *t;
                    207:
                    208:        bzero(&wbuf, sizeof(wbuf));
                    209:
                    210:        wbuf.t.th_opcode = htons((u_short) ACK);
                    211:        wbuf.t.th_block = htons((u_short) h->currblock);
                    212:        wtail = (char *) &wbuf.t.th_data;
                    213:
                    214:        t = &h->lastdata.t;
                    215:
                    216:        h->iodesc->xid = h->currblock + 1;      /* expected block */
                    217:
                    218:        res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
                    219:            recvtftp, t, sizeof(*t) + RSPACE);
                    220:
                    221:        if (res == -1)          /* 0 is OK! */
                    222:                return errno;
                    223:
                    224:        h->currblock++;
                    225:        h->validsize = res;
                    226:        if (res < SEGSIZE)
                    227:                h->islastblock = 1;     /* EOF */
                    228:        return 0;
                    229: }
                    230:
                    231: #ifndef TFTP_NOTERMINATE
                    232: void
                    233: tftp_terminate(struct tftp_handle *h)
                    234: {
                    235:        struct {
                    236:                u_char header[HEADER_SIZE];
                    237:                struct tftphdr t;
                    238:        } wbuf;
                    239:        char           *wtail;
                    240:
                    241:        bzero(&wbuf, sizeof(wbuf));
                    242:
                    243:        if (h->islastblock) {
                    244:                wbuf.t.th_opcode = htons((u_short) ACK);
                    245:                wbuf.t.th_block = htons((u_short) h->currblock);
                    246:        } else {
                    247:                wbuf.t.th_opcode = htons((u_short) ERROR);
                    248:                wbuf.t.th_code = htons((u_short) ENOSPACE); /* ??? */
                    249:        }
                    250:        wtail = (char *) &wbuf.t.th_data;
                    251:
                    252:        (void) sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
                    253: }
                    254: #endif
                    255:
                    256: int
                    257: tftp_open(char *path, struct open_file *f)
                    258: {
                    259:        struct tftp_handle *tftpfile;
                    260:        struct iodesc  *io;
                    261:        int             res;
                    262:
                    263:        tftpfile = (struct tftp_handle *) alloc(sizeof(*tftpfile));
                    264:        if (tftpfile == NULL)
                    265:                return ENOMEM;
                    266:
                    267:        tftpfile->iodesc = io = socktodesc(*(int *) (f->f_devdata));
                    268:        io->destip = servip;
                    269:        tftpfile->off = 0;
                    270:        tftpfile->path = path;  /* XXXXXXX we hope it's static */
                    271:
                    272:        res = tftp_makereq(tftpfile);
                    273:
                    274:        if (res) {
                    275:                free(tftpfile, sizeof(*tftpfile));
                    276:                return res;
                    277:        }
                    278:        f->f_fsdata = (void *) tftpfile;
                    279:        return 0;
                    280: }
                    281:
                    282: int
                    283: tftp_read(struct open_file *f, void *addr, size_t size, size_t *resid)
                    284: {
                    285:        struct tftp_handle *tftpfile;
                    286: #if !defined(LIBSA_NO_TWIDDLE)
                    287:        static int tc = 0;
                    288: #endif
                    289:        tftpfile = (struct tftp_handle *) f->f_fsdata;
                    290:
                    291:        while (size > 0) {
                    292:                int needblock;
                    293:                size_t count;
                    294:
                    295:                needblock = tftpfile->off / SEGSIZE + 1;
                    296:
                    297:                if (tftpfile->currblock > needblock) {  /* seek backwards */
                    298: #ifndef TFTP_NOTERMINATE
                    299:                        tftp_terminate(tftpfile);
                    300: #endif
                    301:                        /* Don't bother to check retval: it worked for open() */
                    302:                        tftp_makereq(tftpfile);
                    303:                }
                    304:
                    305:                while (tftpfile->currblock < needblock) {
                    306:                        int res;
                    307:
                    308: #if !defined(LIBSA_NO_TWIDDLE)
                    309:                        if ((tc++ % 16) == 0)
                    310:                                twiddle();
                    311: #endif
                    312:                        res = tftp_getnextblock(tftpfile);
                    313:                        if (res) {      /* no answer */
                    314: #ifdef DEBUG
                    315:                                printf("tftp: read error (block %d->%d)\n",
                    316:                                    tftpfile->currblock, needblock);
                    317: #endif
                    318:                                return res;
                    319:                        }
                    320:                        if (tftpfile->islastblock)
                    321:                                break;
                    322:                }
                    323:
                    324:                if (tftpfile->currblock == needblock) {
                    325:                        size_t offinblock, inbuffer;
                    326:
                    327:                        offinblock = tftpfile->off % SEGSIZE;
                    328:
                    329:                        inbuffer = tftpfile->validsize - offinblock;
                    330:                        if (inbuffer < 0) {
                    331: #ifdef DEBUG
                    332:                                printf("tftp: invalid offset %d\n",
                    333:                                    tftpfile->off);
                    334: #endif
                    335:                                return EINVAL;
                    336:                        }
                    337:                        count = (size < inbuffer ? size : inbuffer);
                    338:                        bcopy(tftpfile->lastdata.t.th_data + offinblock,
                    339:                            addr, count);
                    340:
                    341:                        addr = (caddr_t)addr + count;
                    342:                        tftpfile->off += count;
                    343:                        size -= count;
                    344:
                    345:                        if ((tftpfile->islastblock) && (count == inbuffer))
                    346:                                break;  /* EOF */
                    347:                } else {
                    348: #ifdef DEBUG
                    349:                        printf("tftp: block %d not found\n", needblock);
                    350: #endif
                    351:                        return EINVAL;
                    352:                }
                    353:
                    354:        }
                    355:
                    356:        if (resid != NULL)
                    357:                *resid = size;
                    358:        return 0;
                    359: }
                    360:
                    361: int
                    362: tftp_close(struct open_file *f)
                    363: {
                    364:        struct tftp_handle *tftpfile;
                    365:        tftpfile = (struct tftp_handle *) f->f_fsdata;
                    366:
                    367: #ifdef TFTP_NOTERMINATE
                    368:        /* let it time out ... */
                    369: #else
                    370:        tftp_terminate(tftpfile);
                    371: #endif
                    372:
                    373:        free(tftpfile, sizeof(*tftpfile));
                    374:        return 0;
                    375: }
                    376:
                    377: int
                    378: tftp_write(struct open_file *f, void *start, size_t size, size_t *resid)
                    379: {
                    380:        return EROFS;
                    381: }
                    382:
                    383: int
                    384: tftp_stat(struct open_file *f, struct stat *sb)
                    385: {
                    386:        struct tftp_handle *tftpfile;
                    387:        tftpfile = (struct tftp_handle *) f->f_fsdata;
                    388:
                    389:        sb->st_mode = 0444;
                    390:        sb->st_nlink = 1;
                    391:        sb->st_uid = 0;
                    392:        sb->st_gid = 0;
                    393:        sb->st_size = -1;
                    394:
                    395:        return 0;
                    396: }
                    397:
                    398: off_t
                    399: tftp_seek(struct open_file *f, off_t offset, int where)
                    400: {
                    401:        struct tftp_handle *tftpfile;
                    402:        tftpfile = (struct tftp_handle *) f->f_fsdata;
                    403:
                    404:        switch (where) {
                    405:        case SEEK_SET:
                    406:                tftpfile->off = offset;
                    407:                break;
                    408:        case SEEK_CUR:
                    409:                tftpfile->off += offset;
                    410:                break;
                    411:        default:
                    412:                errno = EOFFSET;
                    413:                return -1;
                    414:        }
                    415:
                    416:        return (tftpfile->off);
                    417: }
                    418:
                    419: /*
                    420:  * Not implemented.
                    421:  */
                    422: #ifndef NO_READDIR
                    423: int
                    424: tftp_readdir(struct open_file *f, char *name)
                    425: {
                    426:        return EROFS;
                    427: }
                    428: #endif

CVSweb