home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / N / TCPIP / NETKIT-B.05 / NETKIT-B / NetKit-B-0.05 / tftpd / tftpd.c < prev   
Encoding:
C/C++ Source or Header  |  1993-12-17  |  12.1 KB  |  507 lines

  1. /*
  2.  * Copyright (c) 1983 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. #ifndef lint
  35. char copyright[] =
  36. "@(#) Copyright (c) 1983 Regents of the University of California.\n\
  37.  All rights reserved.\n";
  38. #endif /* not lint */
  39.  
  40. #ifndef lint
  41. /*static char sccsid[] = "from: @(#)tftpd.c    5.13 (Berkeley) 2/26/91";*/
  42. static char rcsid[] = "$Id: tftpd.c,v 1.3 1993/08/01 18:28:53 mycroft Exp $";
  43. #endif /* not lint */
  44.  
  45. /*
  46.  * Trivial file transfer protocol server.
  47.  *
  48.  * This version includes many modifications by Jim Guyton <guyton@rand-unix>
  49.  */
  50.  
  51. #include <sys/types.h>
  52. #include <sys/ioctl.h>
  53. #include <sys/stat.h>
  54. #include <signal.h>
  55. #include <fcntl.h>
  56.  
  57. #include <sys/socket.h>
  58. #include <netinet/in.h>
  59. #include <arpa/tftp.h>
  60. #include <netdb.h>
  61.  
  62. #include <setjmp.h>
  63. #include <syslog.h>
  64. #include <stdio.h>
  65. #include <errno.h>
  66. #include <ctype.h>
  67. #include <string.h>
  68. #include <stdlib.h>
  69.  
  70. #define    TIMEOUT        5
  71.  
  72. extern    int errno;
  73. struct    sockaddr_in s_in = { AF_INET };
  74. int    peer;
  75. int    rexmtval = TIMEOUT;
  76. int    maxtimeout = 5*TIMEOUT;
  77.  
  78. #define    PKTSIZE    SEGSIZE+4
  79. char    buf[PKTSIZE];
  80. char    ackbuf[PKTSIZE];
  81. struct    sockaddr_in from;
  82. int    fromlen;
  83.  
  84. #define MAXARG    4
  85. char    *dirs[MAXARG+1];
  86.  
  87. main(ac, av)
  88.     char **av;
  89. {
  90.     register struct tftphdr *tp;
  91.     register int n = 0;
  92.     int on = 1;
  93.  
  94.     ac--; av++;
  95.     while (ac-- > 0 && n < MAXARG)
  96.         dirs[n++] = *av++;
  97.     openlog("tftpd", LOG_PID, LOG_DAEMON);
  98.     if (ioctl(0, FIONBIO, &on) < 0) {
  99.         syslog(LOG_ERR, "ioctl(FIONBIO): %m\n");
  100.         exit(1);
  101.     }
  102.     fromlen = sizeof (from);
  103.     n = recvfrom(0, buf, sizeof (buf), 0,
  104.         (struct sockaddr *)&from, &fromlen);
  105.     if (n < 0) {
  106.         syslog(LOG_ERR, "recvfrom: %m\n");
  107.         exit(1);
  108.     }
  109.     /*
  110.      * Now that we have read the message out of the UDP
  111.      * socket, we fork and exit.  Thus, inetd will go back
  112.      * to listening to the tftp port, and the next request
  113.      * to come in will start up a new instance of tftpd.
  114.      *
  115.      * We do this so that inetd can run tftpd in "wait" mode.
  116.      * The problem with tftpd running in "nowait" mode is that
  117.      * inetd may get one or more successful "selects" on the
  118.      * tftp port before we do our receive, so more than one
  119.      * instance of tftpd may be started up.  Worse, if tftpd
  120.      * break before doing the above "recvfrom", inetd would
  121.      * spawn endless instances, clogging the system.
  122.      */
  123.     {
  124.         int pid;
  125.         int i, j;
  126.  
  127.         for (i = 1; i < 20; i++) {
  128.             pid = fork();
  129.             if (pid < 0) {
  130.                 sleep(i);
  131.                 /*
  132.                  * flush out to most recently sent request.
  133.                  *
  134.                  * This may drop some request, but those
  135.                  * will be resent by the clients when
  136.                  * they timeout.  The positive effect of
  137.                  * this flush is to (try to) prevent more
  138.                  * than one tftpd being started up to service
  139.                  * a single request from a single client.
  140.                  */
  141.                 j = sizeof from;
  142.                 i = recvfrom(0, buf, sizeof (buf), 0,
  143.                     (struct sockaddr *)&from, &j);
  144.                 if (i > 0) {
  145.                     n = i;
  146.                     fromlen = j;
  147.                 }
  148.             } else {
  149.                 break;
  150.             }
  151.         }
  152.         if (pid < 0) {
  153.             syslog(LOG_ERR, "fork: %m\n");
  154.             exit(1);
  155.         } else if (pid != 0) {
  156.             exit(0);
  157.         }
  158.     }
  159.     from.sin_family = AF_INET;
  160.     alarm(0);
  161.     close(0);
  162.     close(1);
  163.     peer = socket(AF_INET, SOCK_DGRAM, 0);
  164.     if (peer < 0) {
  165.         syslog(LOG_ERR, "socket: %m\n");
  166.         exit(1);
  167.     }
  168.     if (bind(peer, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) {
  169.         syslog(LOG_ERR, "bind: %m\n");
  170.         exit(1);
  171.     }
  172.     if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
  173.         syslog(LOG_ERR, "connect: %m\n");
  174.         exit(1);
  175.     }
  176.     tp = (struct tftphdr *)buf;
  177.     tp->th_opcode = ntohs(tp->th_opcode);
  178.     if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
  179.         tftp(tp, n);
  180.     exit(1);
  181. }
  182.  
  183. int    validate_access();
  184. int    sendfile(), recvfile();
  185.  
  186. struct formats {
  187.     char    *f_mode;
  188.     int    (*f_validate)();
  189.     int    (*f_send)();
  190.     int    (*f_recv)();
  191.     int    f_convert;
  192. } formats[] = {
  193.     { "netascii",    validate_access,    sendfile,    recvfile, 1 },
  194.     { "octet",    validate_access,    sendfile,    recvfile, 0 },
  195. #ifdef notdef
  196.     { "mail",    validate_user,        sendmail,    recvmail, 1 },
  197. #endif
  198.     { 0 }
  199. };
  200.  
  201. /*
  202.  * Handle initial connection protocol.
  203.  */
  204. tftp(tp, size)
  205.     struct tftphdr *tp;
  206.     int size;
  207. {
  208.     register char *cp;
  209.     int first = 1, ecode;
  210.     register struct formats *pf;
  211.     char *filename, *mode;
  212.  
  213.     filename = cp = tp->th_stuff;
  214. again:
  215.     while (cp < buf + size) {
  216.         if (*cp == '\0')
  217.             break;
  218.         cp++;
  219.     }
  220.     if (*cp != '\0') {
  221.         nak(EBADOP);
  222.         exit(1);
  223.     }
  224.     if (first) {
  225.         mode = ++cp;
  226.         first = 0;
  227.         goto again;
  228.     }
  229.     for (cp = mode; *cp; cp++)
  230.         if (isupper(*cp))
  231.             *cp = tolower(*cp);
  232.     for (pf = formats; pf->f_mode; pf++)
  233.         if (strcmp(pf->f_mode, mode) == 0)
  234.             break;
  235.     if (pf->f_mode == 0) {
  236.         nak(EBADOP);
  237.         exit(1);
  238.     }
  239.     ecode = (*pf->f_validate)(filename, tp->th_opcode);
  240.     if (ecode) {
  241.         nak(ecode);
  242.         exit(1);
  243.     }
  244.     if (tp->th_opcode == WRQ)
  245.         (*pf->f_recv)(pf);
  246.     else
  247.         (*pf->f_send)(pf);
  248.     exit(0);
  249. }
  250.  
  251.  
  252. FILE *file;
  253.  
  254. /*
  255.  * Validate file access.  Since we
  256.  * have no uid or gid, for now require
  257.  * file to exist and be publicly
  258.  * readable/writable.
  259.  * If we were invoked with arguments
  260.  * from inetd then the file must also be
  261.  * in one of the given directory prefixes.
  262.  * Note also, full path name must be
  263.  * given as we have no login directory.
  264.  */
  265. validate_access(filename, mode)
  266.     char *filename;
  267.     int mode;
  268. {
  269.     struct stat stbuf;
  270.     int    fd;
  271.     char *cp, **dirp;
  272.  
  273.     if (*filename != '/')
  274.         return (EACCESS);
  275.     /*
  276.      * prevent tricksters from getting around the directory restrictions
  277.      */
  278.     for (cp = filename + 1; *cp; cp++)
  279.         if(*cp == '.' && strncmp(cp-1, "/../", 4) == 0)
  280.             return(EACCESS);
  281.     for (dirp = dirs; *dirp; dirp++)
  282.         if (strncmp(filename, *dirp, strlen(*dirp)) == 0)
  283.             break;
  284.     if (*dirp==0 && dirp!=dirs)
  285.         return (EACCESS);
  286.     if (stat(filename, &stbuf) < 0)
  287.         return (errno == ENOENT ? ENOTFOUND : EACCESS);
  288.     if (mode == RRQ) {
  289.         if ((stbuf.st_mode&(S_IREAD >> 6)) == 0)
  290.             return (EACCESS);
  291.     } else {
  292.         if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0)
  293.             return (EACCESS);
  294.     }
  295.     fd = open(filename, mode == RRQ ? 0 : 1);
  296.     if (fd < 0)
  297.         return (errno + 100);
  298.     file = fdopen(fd, (mode == RRQ)? "r":"w");
  299.     if (file == NULL) {
  300.         return errno+100;
  301.     }
  302.     return (0);
  303. }
  304.  
  305. int    timeout;
  306. jmp_buf    timeoutbuf;
  307.  
  308. void
  309. timer()
  310. {
  311.  
  312.     timeout += rexmtval;
  313.     if (timeout >= maxtimeout)
  314.         exit(1);
  315.     longjmp(timeoutbuf, 1);
  316. }
  317.  
  318. /*
  319.  * Send the requested file.
  320.  */
  321. sendfile(pf)
  322.     struct formats *pf;
  323. {
  324.     struct tftphdr *dp, *r_init();
  325.     register struct tftphdr *ap;    /* ack packet */
  326.     register int block = 1, size, n;
  327.  
  328.     signal(SIGALRM, timer);
  329.     dp = r_init();
  330.     ap = (struct tftphdr *)ackbuf;
  331.     do {
  332.         size = readit(file, &dp, pf->f_convert);
  333.         if (size < 0) {
  334.             nak(errno + 100);
  335.             goto abort;
  336.         }
  337.         dp->th_opcode = htons((u_short)DATA);
  338.         dp->th_block = htons((u_short)block);
  339.         timeout = 0;
  340.         (void) setjmp(timeoutbuf);
  341.  
  342. send_data:
  343.         if (send(peer, dp, size + 4, 0) != size + 4) {
  344.             syslog(LOG_ERR, "tftpd: write: %m\n");
  345.             goto abort;
  346.         }
  347.         read_ahead(file, pf->f_convert);
  348.         for ( ; ; ) {
  349.             alarm(rexmtval);        /* read the ack */
  350.             n = recv(peer, ackbuf, sizeof (ackbuf), 0);
  351.             alarm(0);
  352.             if (n < 0) {
  353.                 syslog(LOG_ERR, "tftpd: read: %m\n");
  354.                 goto abort;
  355.             }
  356.             ap->th_opcode = ntohs((u_short)ap->th_opcode);
  357.             ap->th_block = ntohs((u_short)ap->th_block);
  358.  
  359.             if (ap->th_opcode == ERROR)
  360.                 goto abort;
  361.             
  362.             if (ap->th_opcode == ACK) {
  363.                 if (ap->th_block == block) {
  364.                     break;
  365.                 }
  366.                 /* Re-synchronize with the other side */
  367.                 (void) synchnet(peer);
  368.                 if (ap->th_block == (block -1)) {
  369.                     goto send_data;
  370.                 }
  371.             }
  372.  
  373.         }
  374.         block++;
  375.     } while (size == SEGSIZE);
  376. abort:
  377.     (void) fclose(file);
  378. }
  379.  
  380. void
  381. justquit()
  382. {
  383.     exit(0);
  384. }
  385.  
  386.  
  387. /*
  388.  * Receive a file.
  389.  */
  390. recvfile(pf)
  391.     struct formats *pf;
  392. {
  393.     struct tftphdr *dp, *w_init();
  394.     register struct tftphdr *ap;    /* ack buffer */
  395.     register int block = 0, n, size;
  396.  
  397.     signal(SIGALRM, timer);
  398.     dp = w_init();
  399.     ap = (struct tftphdr *)ackbuf;
  400.     do {
  401.         timeout = 0;
  402.         ap->th_opcode = htons((u_short)ACK);
  403.         ap->th_block = htons((u_short)block);
  404.         block++;
  405.         (void) setjmp(timeoutbuf);
  406. send_ack:
  407.         if (send(peer, ackbuf, 4, 0) != 4) {
  408.             syslog(LOG_ERR, "tftpd: write: %m\n");
  409.             goto abort;
  410.         }
  411.         write_behind(file, pf->f_convert);
  412.         for ( ; ; ) {
  413.             alarm(rexmtval);
  414.             n = recv(peer, dp, PKTSIZE, 0);
  415.             alarm(0);
  416.             if (n < 0) {            /* really? */
  417.                 syslog(LOG_ERR, "tftpd: read: %m\n");
  418.                 goto abort;
  419.             }
  420.             dp->th_opcode = ntohs((u_short)dp->th_opcode);
  421.             dp->th_block = ntohs((u_short)dp->th_block);
  422.             if (dp->th_opcode == ERROR)
  423.                 goto abort;
  424.             if (dp->th_opcode == DATA) {
  425.                 if (dp->th_block == block) {
  426.                     break;   /* normal */
  427.                 }
  428.                 /* Re-synchronize with the other side */
  429.                 (void) synchnet(peer);
  430.                 if (dp->th_block == (block-1))
  431.                     goto send_ack;          /* rexmit */
  432.             }
  433.         }
  434.         /*  size = write(file, dp->th_data, n - 4); */
  435.         size = writeit(file, &dp, n - 4, pf->f_convert);
  436.         if (size != (n-4)) {                    /* ahem */
  437.             if (size < 0) nak(errno + 100);
  438.             else nak(ENOSPACE);
  439.             goto abort;
  440.         }
  441.     } while (size == SEGSIZE);
  442.     write_behind(file, pf->f_convert);
  443.     (void) fclose(file);            /* close data file */
  444.  
  445.     ap->th_opcode = htons((u_short)ACK);    /* send the "final" ack */
  446.     ap->th_block = htons((u_short)(block));
  447.     (void) send(peer, ackbuf, 4, 0);
  448.  
  449.     signal(SIGALRM, justquit);      /* just quit on timeout */
  450.     alarm(rexmtval);
  451.     n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
  452.     alarm(0);
  453.     if (n >= 4 &&                   /* if read some data */
  454.         dp->th_opcode == DATA &&    /* and got a data block */
  455.         block == dp->th_block) {    /* then my last ack was lost */
  456.         (void) send(peer, ackbuf, 4, 0);     /* resend final ack */
  457.     }
  458. abort:
  459.     return;
  460. }
  461.  
  462. struct errmsg {
  463.     int    e_code;
  464.     char    *e_msg;
  465. } errmsgs[] = {
  466.     { EUNDEF,    "Undefined error code" },
  467.     { ENOTFOUND,    "File not found" },
  468.     { EACCESS,    "Access violation" },
  469.     { ENOSPACE,    "Disk full or allocation exceeded" },
  470.     { EBADOP,    "Illegal TFTP operation" },
  471.     { EBADID,    "Unknown transfer ID" },
  472.     { EEXISTS,    "File already exists" },
  473.     { ENOUSER,    "No such user" },
  474.     { -1,        0 }
  475. };
  476.  
  477. /*
  478.  * Send a nak packet (error message).
  479.  * Error code passed in is one of the
  480.  * standard TFTP codes, or a UNIX errno
  481.  * offset by 100.
  482.  */
  483. nak(error)
  484.     int error;
  485. {
  486.     register struct tftphdr *tp;
  487.     int length;
  488.     register struct errmsg *pe;
  489.  
  490.     tp = (struct tftphdr *)buf;
  491.     tp->th_opcode = htons((u_short)ERROR);
  492.     tp->th_code = htons((u_short)error);
  493.     for (pe = errmsgs; pe->e_code >= 0; pe++)
  494.         if (pe->e_code == error)
  495.             break;
  496.     if (pe->e_code < 0) {
  497.         pe->e_msg = strerror(error - 100);
  498.         tp->th_code = EUNDEF;   /* set 'undef' errorcode */
  499.     }
  500.     strcpy(tp->th_msg, pe->e_msg);
  501.     length = strlen(pe->e_msg);
  502.     tp->th_msg[length] = '\0';
  503.     length += 5;
  504.     if (send(peer, buf, length, 0) != length)
  505.         syslog(LOG_ERR, "nak: %m\n");
  506. }
  507.