root/lib/libsa/tftp.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. recvtftp
  2. tftp_makereq
  3. tftp_getnextblock
  4. tftp_terminate
  5. tftp_open
  6. tftp_read
  7. tftp_close
  8. tftp_write
  9. tftp_stat
  10. tftp_seek
  11. tftp_readdir

    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

/* [<][>][^][v][top][bottom][index][help] */