⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tftp_server_linux.c

📁 linux下的tftp源代码
💻 C
📖 第 1 页 / 共 2 页
字号:

*
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley.  The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/

#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1983 Regents of the University of California.\n\
All rights reserved.\n";
#endif /* not lint */

#ifndef lint
static char sccsid[] = "@(#)tftpd.c    5.8 (Berkeley) 6/18/88";
#endif /* not lint */

/*
* Trivial file transfer protocol server.
*
* This version includes many modifications by Jim Guyton <guyton@rand-unix>
*
* Further modifications by Markus Gutschke <gutschk@math.uni-muenster.de>
*  - RFC1782 option parsing
*  - RFC1783 extended blocksize
*  - "-c" option for changing the root directory
*  - "-d" option for debugging output
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/stat.h>

#include <netinet/in.h>

#include <arpa/tftp.h>

#include <alloca.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <netdb.h>
#include <setjmp.h>
#include <syslog.h>

#define    TIMEOUT        5

#ifndef    OACK
#define    OACK    06
#endif

#ifndef EOPTNEG
#define    EOPTNEG    8
#endif

extern    int errno;
struct    sockaddr_in sin = { AF_INET };
int    peer;
int    rexmtval = TIMEOUT;
int    maxtimeout = 5*TIMEOUT;

#define    PKTSIZE    (1432+4) /* SEGSIZE+4 */
int    segsize = SEGSIZE;
char    buf[PKTSIZE];
char    ackbuf[PKTSIZE];
struct    sockaddr_in from;
int    fromlen;

char    *rootdir = NULL;
int    debug = 0;

struct filters {
    struct filters *next;
    char           *fname;
} *filters = NULL;
int     isfilter = 0;

main(argc, argv)
    char *argv[];
{
    register struct tftphdr *tp;
    register int n;
    int on = 1;
    extern int optind;
    extern char *optarg;

    openlog(argv[0], LOG_PID, LOG_DAEMON);

    while ((n = getopt(argc, argv, "c:dr:")) >= 0) {
        switch (n) {
        case 'c':
            if (rootdir)
                goto usage;
            rootdir = optarg;
            break;
        case 'd':
            debug++;
            break;
        case 'r': {
            struct filters *fp = (void *)
                             malloc(sizeof(struct filters) +
                            strlen(optarg) + 1);
            fp->next  = filters;
            fp->fname = (char *)(fp + 1);
            strcpy(fp->fname, optarg);
            filters   = fp;
            break; }
        default:
        usage:
            syslog(LOG_ERR, "Usage: %s [-c chroot] "
                   "[-r readfilter] [-d]\n",
                   argv[0]);
            exit(1);
        }
    }
    if (argc-optind != 0)
        goto usage;

    ioctl(0, FIONBIO, &on);
/*    if (ioctl(0, FIONBIO, &on) < 0) {
        syslog(LOG_ERR, "ioctl(FIONBIO): %m\n");
        exit(1);
    }
*/
    fromlen = sizeof (from);
    n = recvfrom(0, buf, segsize+4, 0,
        (struct sockaddr *)&from, &fromlen);
    if (n < 0) {
        syslog(LOG_ERR, "recvfrom: %m\n");
        exit(1);
    }
    /*
     * Now that we have read the message out of the UDP
     * socket, we fork and exit.  Thus, inetd will go back
     * to listening to the tftp port, and the next request
     * to come in will start up a new instance of tftpd.
     *
     * We do this so that inetd can run tftpd in "wait" mode.
     * The problem with tftpd running in "nowait" mode is that
     * inetd may get one or more successful "selects" on the
     * tftp port before we do our receive, so more than one
     * instance of tftpd may be started up.  Worse, if tftpd
     * break before doing the above "recvfrom", inetd would
     * spawn endless instances, clogging the system.
     */
    {
        int pid;
        int i, j;

        for (i = 1; i < 20; i++) {
            pid = fork();
            if (pid < 0) {
                sleep(i);
                /*
                 * flush out to most recently sent request.
                 *
                 * This may drop some request, but those
                 * will be resent by the clients when
                 * they timeout.  The positive effect of
                 * this flush is to (try to) prevent more
                 * than one tftpd being started up to service
                 * a single request from a single client.
                 */
                j = sizeof from;
                i = recvfrom(0, buf, segsize+4, 0,
                    (struct sockaddr *)&from, &j);
                if (i > 0) {
                    n = i;
                    fromlen = j;
                }
            } else {
                break;
            }
        }
        if (pid < 0) {
            syslog(LOG_ERR, "fork: %m\n");
            exit(1);
        } else if (pid != 0) {
            exit(0);
        }
    }
    from.sin_family = AF_INET;
    alarm(0);
    close(0);
    close(1);
    peer = socket(AF_INET, SOCK_DGRAM, 0);
    if (peer < 0) {
        syslog(LOG_ERR, "socket: %m\n");
        exit(1);
    }
    if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
        syslog(LOG_ERR, "bind: %m\n");
        exit(1);
    }
    if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
        syslog(LOG_ERR, "connect: %m\n");
        exit(1);
    }
    tp = (struct tftphdr *)buf;
    tp->th_opcode = ntohs(tp->th_opcode);
    if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
        tftp(tp, n);
    exit(1);
}

int    validate_access();
int    sendfile(), recvfile();

struct formats {
    char    *f_mode;
    int    (*f_validate)();
    int    (*f_send)();
    int    (*f_recv)();
    int    f_convert;
} formats[] = {
    { "netascii",    validate_access,    sendfile,    recvfile, 1 },
    { "octet",    validate_access,    sendfile,    recvfile, 0 },
#ifdef notdef
    { "mail",    validate_user,        sendmail,    recvmail, 1 },
#endif
    { 0 }
};

int    set_blksize();

struct options {
    char    *o_opt;
    int    (*o_fnc)();
} options[] = {
    { "blksize",    set_blksize },
    { 0 }
};

/*
* Set a non-standard block size (c.f. RFC1783)
*/

set_blksize(val, ret)
    char *val;
    char **ret;
{
    static char b_ret[5];
    int sz = atoi(val);

    if (sz < 8) {
        if (debug)
            syslog(LOG_ERR, "Requested packetsize %d < 8\n", sz);
        return(0);
    } else if (sz > PKTSIZE-4) {
        if (debug)
            syslog(LOG_INFO, "Requested packetsize %d > %d\n",
                   sz, PKTSIZE-4);
        sz = PKTSIZE-4;
    } else if (debug)
        syslog(LOG_INFO, "Adjusted packetsize to %d octets\n", sz);
   
    segsize = sz;
    sprintf(*ret = b_ret, "%d", sz);
    return(1);
}

/*
* Parse RFC1782 style options
*/

do_opt(opt, val, ap)
    char *opt;
    char *val;
    char **ap;
{
    struct options *po;
    char *ret;

    for (po = options; po->o_opt; po++)
        if (strcasecmp(po->o_opt, opt) == 0) {
            if (po->o_fnc(val, &ret)) {
                if (*ap + strlen(opt) + strlen(ret) + 2 >=
                    ackbuf + sizeof(ackbuf)) {
                    if (debug)
                        syslog(LOG_ERR,
                               "Ackbuf overflow\n");
                    nak(ENOSPACE);
                    exit(1);
                }
                *ap = strrchr(strcpy(strrchr(strcpy(*ap, opt),
                                 '\000')+1, val),
                          '\000')+1;
            } else {
                nak(EOPTNEG);
                exit(1);
            }
            break;
        }
    if (debug && !po->o_opt)
        syslog(LOG_WARNING, "Unhandled option: %d = %d\n", opt, val);
    return;
}

/*
* Handle initial connection protocol.
*/
tftp(tp, size)
    struct tftphdr *tp;
    int size;
{
    register char *cp;
    int argn = 0, ecode;
    register struct formats *pf;
    char *filename, *mode;
    char *val, *opt;
    char *ap = ackbuf+2;
    int  isopts;

    ((struct tftphdr *)ackbuf)->th_opcode = ntohs(OACK);
    filename = cp = tp->th_stuff;
again:
    while (cp < buf + size) {
        if (*cp == '\0')
            break;
        cp++;
    }
    if (*cp != '\0') {
        if (debug)
            syslog(LOG_WARNING, "Received illegal request\n");
        nak(EBADOP);
        exit(1);
    }
    if (!argn++) {
        mode = ++cp;
        goto again;
    } else {
        if (debug && argn == 3)
            syslog(LOG_INFO, "Found RFC1782 style options\n");
        *(argn & 1 ? &val : &opt) = ++cp;
        if (argn & 1)
            do_opt(opt, val, &ap);
        if (cp < buf + size && *cp != '\000')
            goto again;
    }
   
    for (cp = mode; *cp; cp++)
        if (isupper(*cp))
            *cp = tolower(*cp);
    for (pf = formats; pf->f_mode; pf++)
        if (strcmp(pf->f_mode, mode) == 0)
            break;
    if (pf->f_mode == 0) {
        if (debug)
            syslog(LOG_WARNING, "Unknown data format: %s\n", mode);
        nak(EBADOP);
        exit(1);
    }

    if (rootdir) {
        cp = alloca(strlen(rootdir) + strlen(filename) + 1);
        if (cp == NULL) {
            nak(100+ENOMEM);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -