The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/lib/libsa/tftp.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 93d706539d0045c49b310005d9c9f0e1


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.