/*
* Copyright (c) 1983 Regents of the University of California.
* Copyright (c) 1999-2009 H. Peter Anvin
* Copyright (c) 2011 Intel Corporation; author: H. Peter Anvin
* 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"
/*
* Trivial file transfer protocol server.
*
* This version includes many modifications by Jim Guyton <guyton@rand-unix>
*/
#include <sys/ioctl.h>
#include <signal.h>
#include <ctype.h>
#include <pwd.h>
#include <limits.h>
#include <syslog.h>
#include "common/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 */
static struct request_info wrap_request;
#endif
#ifdef HAVE_IPV6
static int ai_fam = AF_UNSPEC;
#else
static int ai_fam = AF_INET;
#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;
static int peer;
static unsigned long timeout = TIMEOUT; /* Current timeout value */
static unsigned long rexmtval = TIMEOUT; /* Basic timeout value */
static unsigned long maxtimeout = TIMEOUT_LIMIT * TIMEOUT;
static int timeout_quit = 0;
static sigjmp_buf timeoutbuf;
static uint16_t rollover_val = 0;
#define PKTSIZE MAX_SEGSIZE+4
static char buf[PKTSIZE];
static char ackbuf[PKTSIZE];
static unsigned int max_blksize = MAX_SEGSIZE;
static char tmpbuf[INET6_ADDRSTRLEN], *tmp_p;
static union sock_addr from;
static socklen_t fromlen;
static off_t tsize;
static int tsize_ok;
static int ndirs;
static const char **dirs;
static int secure = 0;
int cancreate = 0;
int unixperms = 0;
int portrange = 0;
unsigned int portrange_from, portrange_to;
int verbosity = 0;
struct formats;
#ifdef WITH_REGEX
static struct rule *rewrite_rules = NULL;
#endif
int tftp(struct tftphdr *, int);
static void nak(int, const char *);
static void timer(int);
static void do_opt(const char *, const char *, char **);
static int set_blksize(uintmax_t *);
static int set_blksize2(uintmax_t *);
static int set_tsize(uintmax_t *);
static int set_timeout(uintmax_t *);
static int set_utimeout(uintmax_t *);
static int set_rollover(uintmax_t *);
struct options {
const char *o_opt;
int (*o_fnc)(uintmax_t *);
} options[] = {
{"blksize", set_blksize},
{"blksize2", set_blksize2},
{"tsize", set_tsize},
{"timeout", set_timeout},
{"utimeout", set_utimeout},
{"rollover", set_rollover},
{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 exit requests by SIGTERM and SIGINT */
static volatile sig_atomic_t exit_signal = 0;
static void handle_exit(int sig)
{
exit_signal = sig;
}
/* Handle timeout signal or timeout event */
void timer(int sig)
{
(void)sig; /* Suppress unused warning */
timeout <<= 1;
if (timeout >= maxtimeout || timeout_quit)
exit(0);
siglongjmp(timeoutbuf, 1);
}
#ifdef WITH_REGEX
static 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;
}
#endif
/*
* Rules for locking files; return 0 on success, -1 on failure
*/
static int lock_file(int fd, int lock_write)
{
#if defined(HAVE_FCNTL) && defined(HAVE_F_SETLK_DEFINITION)
struct flock fl;
fl.l_type = lock_write ? F_WRLCK : F_RDLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0; /* Whole file */
return fcntl(fd, F_SETLK, &fl);
#elif defined(HAVE_LOCK_SH_DEFINITION)
return flock(fd, lock_write ? LOCK_EX|LOCK_NB : LOCK_SH|LOCK_NB);
#else
return 0; /* Hope & pray... */
#endif
}
static void set_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);
}
}
static void pmtu_discovery_off(int fd)
{
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
int pmtu = IP_PMTUDISC_DONT;
setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
#endif
}
/*
* 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... */
}