📄 tftpd.c
字号:
/* tftp-hpa: $Id: tftpd.c,v 1.58 2004/09/14 22:38:46 hpa Exp $ *//* $OpenBSD: tftpd.c,v 1.13 1999/06/23 17:01:36 deraadt Exp $ *//* * Copyright (c) 1983 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#include "config.h" /* Must be included first */#include "tftpd.h"#ifndef lintstatic const char *copyright UNUSED ="@(#) Copyright (c) 1983 Regents of the University of California.\n\ All rights reserved.\n";/*static char sccsid[] = "from: @(#)tftpd.c 5.13 (Berkeley) 2/26/91";*//*static char rcsid[] = "$OpenBSD: tftpd.c,v 1.13 1999/06/23 17:01:36 deraadt Exp $: tftpd.c,v 1.6 1997/02/16 23:49:21 deraadt Exp $";*/static const char *rcsid UNUSED ="tftp-hpa $Id: tftpd.c,v 1.58 2004/09/14 22:38:46 hpa Exp $";#endif /* not lint *//* * Trivial file transfer protocol server. * * This version includes many modifications by Jim Guyton <guyton@rand-unix> */#include <sys/ioctl.h>#include <signal.h>#include <netdb.h>#include <ctype.h>#include <pwd.h>#include <limits.h>#include <syslog.h>#include "tftpsubs.h"#include "recvfrom.h"#include "remap.h"#ifdef HAVE_SYS_FILIO_H#include <sys/filio.h> /* Necessary for FIONBIO on Solaris */#endif#ifdef HAVE_TCPWRAPPERS#include <tcpd.h>int deny_severity = LOG_WARNING;int allow_severity = -1; /* Don't log at all */struct request_info wrap_request;#endif#define TIMEOUT 1000000 /* Default timeout (us) */#define TRIES 6 /* Number of attempts to send each packet */#define TIMEOUT_LIMIT ((1 << TRIES)-1)const char *__progname;int peer;unsigned long timeout = TIMEOUT; /* Current timeout value */unsigned long rexmtval = TIMEOUT; /* Basic timeout value */unsigned long maxtimeout = TIMEOUT_LIMIT*TIMEOUT;int timeout_quit = 0;sigjmp_buf timeoutbuf;#define PKTSIZE MAX_SEGSIZE+4char buf[PKTSIZE];char ackbuf[PKTSIZE];unsigned int max_blksize = MAX_SEGSIZE;struct sockaddr_in from;int fromlen;off_t tsize;int tsize_ok;int ndirs;const char **dirs;int secure = 0;int cancreate = 0;int unixperms = 0;int verbosity = 0;struct formats;#ifdef WITH_REGEXstatic struct rule *rewrite_rules = NULL;#endifint tftp(struct tftphdr *, int);static void nak(int, const char *);void timer(int);void justquit(int);void do_opt(char *, char *, char **);int set_blksize(char *, char **);int set_blksize2(char *, char **);int set_tsize(char *, char **);int set_timeout(char *, char **);int set_utimeout(char *, char **);struct options { const char *o_opt; int (*o_fnc)(char *, char **);} options[] = { { "blksize", set_blksize }, { "blksize2", set_blksize2 }, { "tsize", set_tsize }, { "timeout", set_timeout }, { "utimeout", set_utimeout }, { NULL, NULL }};/* Simple handler for SIGHUP */static volatile sig_atomic_t caught_sighup = 0;static void handle_sighup(int sig){ (void)sig; /* Suppress unused warning */ caught_sighup = 1;}/* Handle timeout signal or timeout event */voidtimer(int sig){ (void)sig; /* Suppress unused warning */ timeout <<= 1; if (timeout >= maxtimeout || timeout_quit) exit(0); siglongjmp(timeoutbuf, 1);}static voidusage(void){ syslog(LOG_ERR, "Usage: %s [-vcl][-a address][-m mappings][-u user][-t inetd_timeout][-T pkt_timeout][-r option...] [-s] [directory ...]", __progname); exit(EX_USAGE);}#ifdef WITH_REGEXstatic struct rule *read_remap_rules(const char *file){ FILE *f; struct rule *rulep; f = fopen(file, "rt"); if ( !f ) { syslog(LOG_ERR, "Cannot open map file: %s: %m", file); exit(EX_NOINPUT); } rulep = parserulefile(f); fclose(f); return rulep;}#endifstatic inline voidset_socket_nonblock(int fd, int flag){ int err; int flags;#if defined(HAVE_FCNTL) && defined(HAVE_O_NONBLOCK_DEFINITION) /* Posixly correct */ err = ((flags = fcntl(fd, F_GETFL, 0)) < 0) || (fcntl(fd, F_SETFL, flag ? flags|O_NONBLOCK : flags&~O_NONBLOCK) < 0);#else flags = flag ? 1 : 0; err = (ioctl(fd, FIONBIO, &flags) < 0);#endif if ( err ) { syslog(LOG_ERR, "Cannot set nonblock flag on socket: %m"); exit(EX_OSERR); }}/* * Receive packet with synchronous timeout; timeout is adjusted * to account for time spent waiting. */static int recv_time(int s, void *rbuf, int len, unsigned int flags, unsigned long *timeout_us_p){ fd_set fdset; struct timeval tmv, t0, t1; int rv, err; unsigned long timeout_us = *timeout_us_p; unsigned long timeout_left, dt; gettimeofday(&t0, NULL); timeout_left = timeout_us; for ( ; ; ) { FD_ZERO(&fdset); FD_SET(s, &fdset); do { tmv.tv_sec = timeout_left / 1000000; tmv.tv_usec = timeout_left % 1000000; rv = select(s+1, &fdset, NULL, NULL, &tmv); err = errno; gettimeofday(&t1, NULL); dt = (t1.tv_sec - t0.tv_sec)*1000000 + (t1.tv_usec - t0.tv_usec); *timeout_us_p = timeout_left = ( dt >= timeout_us ) ? 1 : (timeout_us - dt); } while ( rv == -1 && err == EINTR ); if ( rv == 0 ) { timer(0); /* Should not return */ return -1; } set_socket_nonblock(s, 1); rv = recv(s, rbuf, len, flags); err = errno; set_socket_nonblock(s, 0); if ( rv < 0 ) { if ( E_WOULD_BLOCK(err) || err == EINTR ) { continue; /* Once again, with feeling... */ } else { errno = err; return rv; } } else { return rv; } }}intmain(int argc, char **argv){ struct tftphdr *tp; struct passwd *pw; struct options *opt; struct sockaddr_in myaddr; struct sockaddr_in bindaddr; int n; int fd = 0; int standalone = 0; /* Standalone (listen) mode */ char *address = NULL; /* Address to listen to */ pid_t pid; mode_t my_umask = 0; int spec_umask = 0; int c; int setrv; int waittime = 900; /* Default time to wait for a connect*/ const char *user = "nobody"; /* Default user */ char *p, *ep;#ifdef WITH_REGEX char *rewrite_file = NULL;#endif u_short tp_opcode; /* basename() is way too much of a pain from a portability standpoint */ p = strrchr(argv[0], '/'); __progname = (p && p[1]) ? p+1 : argv[0]; openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON); while ((c = getopt(argc, argv, "cspvVla:B:u:U:r:t:T:m:")) != -1) switch (c) { case 'c': cancreate = 1; break; case 's': secure = 1; break; case 'p': unixperms = 1; break; case 'l': standalone = 1; break; case 'a': address = optarg; break; case 't': waittime = atoi(optarg); break; case 'B': { char *vp; max_blksize = (unsigned int)strtoul(optarg, &vp, 10); if ( max_blksize < 512 || max_blksize > MAX_SEGSIZE || *vp ) { syslog(LOG_ERR, "Bad maximum blocksize value (range 512-%d): %s", MAX_SEGSIZE, optarg); exit(EX_USAGE); } } break; case 'T': { char *vp; unsigned long tov = strtoul(optarg, &vp, 10); if ( tov < 10000UL || tov > 255000000UL || *vp ) { syslog(LOG_ERR, "Bad timeout value: %s", optarg); exit(EX_USAGE); } rexmtval = timeout = tov; maxtimeout = rexmtval*TIMEOUT_LIMIT; } break; case 'u': user = optarg; break; case 'U': my_umask = strtoul(optarg, &ep, 8); if ( *ep ) { syslog(LOG_ERR, "Invalid umask: %s", optarg); exit(EX_USAGE); } spec_umask = 1; break; case 'r': for ( opt = options ; opt->o_opt ; opt++ ) { if ( !strcasecmp(optarg, opt->o_opt) ) { opt->o_opt = ""; /* Don't support this option */ break; } } if ( !opt->o_opt ) { syslog(LOG_ERR, "Unknown option: %s", optarg); exit(EX_USAGE); } break;#ifdef WITH_REGEX case 'm': if ( rewrite_file ) { syslog(LOG_ERR, "Multiple -m options"); exit(EX_USAGE); } rewrite_file = optarg; break;#endif case 'v': verbosity++; break; case 'V': /* Print configuration to stdout and exit */ printf("%s\n", TFTPD_CONFIG_STR); exit(0); break; default: usage(); break; } dirs = xmalloc((argc-optind+1)*sizeof(char *)); for ( ndirs = 0 ; optind != argc ; optind++ ) dirs[ndirs++] = argv[optind]; dirs[ndirs] = NULL; if (secure) { if (ndirs == 0) { syslog(LOG_ERR, "no -s directory"); exit(EX_USAGE); } if (ndirs > 1) { syslog(LOG_ERR, "too many -s directories"); exit(EX_USAGE); } if (chdir(dirs[0])) { syslog(LOG_ERR, "%s: %m", dirs[0]); exit(EX_NOINPUT); } } pw = getpwnam(user); if (!pw) { syslog(LOG_ERR, "no user %s: %m", user); exit(EX_NOUSER); } if ( spec_umask || !unixperms ) umask(my_umask); /* Note: on Cygwin, select() on a nonblocking socket becomes a nonblocking select. */#ifndef __CYGWIN__ set_socket_nonblock(fd, 1);#endif#ifdef WITH_REGEX if ( rewrite_file ) rewrite_rules = read_remap_rules(rewrite_file);#endif /* If we're running standalone, set up the input port */ if ( standalone ) { fd = socket(PF_INET, SOCK_DGRAM, 0); memset(&bindaddr, 0, sizeof bindaddr); bindaddr.sin_family = AF_INET; bindaddr.sin_addr.s_addr = INADDR_ANY; bindaddr.sin_port = htons(IPPORT_TFTP); if ( address ) { char *portptr, *eportptr; struct hostent *hostent; struct servent *servent; unsigned long port; address = tfstrdup(address); portptr = strrchr(address, ':'); if ( portptr ) *portptr++ = '\0'; if ( *address ) { hostent = gethostbyname(address); if ( !hostent || hostent->h_addrtype != AF_INET ) { syslog(LOG_ERR, "cannot resolve local bind address: %s", address); exit(EX_NOINPUT); } memcpy(&bindaddr.sin_addr, hostent->h_addr, hostent->h_length); } else { /* Default to using INADDR_ANY */ } if ( portptr && *portptr ) { servent = getservbyname(portptr, "udp"); if ( servent ) { bindaddr.sin_port = servent->s_port; } else if ( (port = strtoul(portptr, &eportptr, 0)) && !*eportptr ) { bindaddr.sin_port = htons(port); } else if ( !strcmp(portptr, "tftp") ) { /* It's TFTP, we're OK */ } else { syslog(LOG_ERR, "cannot resolve local bind port: %s", portptr); exit(EX_NOINPUT); } } } if (bind(fd, (struct sockaddr *)&bindaddr, sizeof bindaddr) < 0) { syslog(LOG_ERR, "cannot bind to local socket: %m"); exit(EX_OSERR);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -