Support running without inetd.

This commit is contained in:
hpa 2001-08-07 00:27:52 +00:00
parent 5ed924bce2
commit 8c3d63fba8
3 changed files with 167 additions and 31 deletions

6
README
View file

@ -1,4 +1,4 @@
This is tftp-hpa-0.19; this version was put out by H. Peter Anvin This is tftp-hpa-0.21; this version was put out by H. Peter Anvin
<hpa@zytor.com>. <hpa@zytor.com>.
The latest version of this collection can be found at: The latest version of this collection can be found at:
@ -18,6 +18,10 @@ improvements.
===> IMPORTANT: SEE THE FILE "README.security" FOR IMPORTANT SECURITY ===> IMPORTANT: SEE THE FILE "README.security" FOR IMPORTANT SECURITY
===> CHANGES ENACTED IN VERSION 0.17!!!!!!!!! ===> CHANGES ENACTED IN VERSION 0.17!!!!!!!!!
Changes in 0.21:
Support running in standalone mode, without inetd.
Changes in 0.20: Changes in 0.20:
Portability improvements. Now known to compile and run on Portability improvements. Now known to compile and run on
Solaris 8. Solaris 8.

View file

@ -35,7 +35,7 @@
.\" from: @(#)tftpd.8 6.7 (Berkeley) 5/13/91 .\" from: @(#)tftpd.8 6.7 (Berkeley) 5/13/91
.\" $OpenBSD: tftpd.8,v 1.7 1999/07/09 13:35:51 aaron Exp $ .\" $OpenBSD: tftpd.8,v 1.7 1999/07/09 13:35:51 aaron Exp $
.\" .\"
.Dd July 10, 2001 .Dd August 6, 2001
.Dt TFTPD 8 .Dt TFTPD 8
.Os .Os
.Sh NAME .Sh NAME
@ -46,8 +46,11 @@ IPv4 Trivial File Transfer Protocol server
.Nm in.tftpd .Nm in.tftpd
.Op Fl v .Op Fl v
.Op Fl c .Op Fl c
.Op Fl l
.Op Fl a Ar [address][:port]
.Op Fl m Ar mapfile .Op Fl m Ar mapfile
.Op Fl u Ar userid .Op Fl u Ar userid
.Op Fl t Ar timeout
.Op Fl r Ar option... .Op Fl r Ar option...
.Op Fl s .Op Fl s
.Op Ar directory .Op Ar directory
@ -66,7 +69,8 @@ service description;
see see
.Xr services 5 . .Xr services 5 .
The server is normally started by The server is normally started by
.Xr inetd 8 . .Xr inetd 8 ,
but can also run standalone.
.Pp .Pp
The use of The use of
.Xr tftp 1 .Xr tftp 1
@ -91,9 +95,20 @@ Access to files may be restricted by invoking
.Nm .Nm
with a list of directories by including pathnames with a list of directories by including pathnames
as server program arguments in as server program arguments in
.Pa /etc/inetd.conf . .Pa /etc/inetd.conf
In this case access is restricted to files whose or on the standalone server command line. In this case access is
names are prefixed by the one of the given directories. restricted to files whose names are prefixed by the one of the given
directories.
.Pp
If the
.Fl l
flag is used, the server runs in standalone (listen) mode. In listen
mode, the
.Fl t
option is ignored, and the
.Fl a
option can be used to specify a specific local address or port to
listen to.
.Pp .Pp
If the If the
.Fl c .Fl c
@ -116,11 +131,15 @@ which do not include a directory name.
.Pp .Pp
The The
.Fl u .Fl u
flag can be used to specify a user ID which option can be used to specify a user ID which
.Nm .Nm
will run as; the default is ``nobody''. will run as; the default is ``nobody''.
.Pp .Pp
The The
.Fl t
option specifies the server timeout in inetd mode.
.Pp
The
.Fl m .Fl m
flag specifies a file which contains filename remapping rules. flag specifies a file which contains filename remapping rules.
.Pp .Pp

View file

@ -166,11 +166,31 @@ static void handle_sighup(int sig)
static void static void
usage(void) usage(void)
{ {
syslog(LOG_ERR, "Usage: %s [-vc][-m mappings][-u user][-t timeout][-r option...] [-s] [directory ...]", syslog(LOG_ERR, "Usage: %s [-vcl][-a address][-m mappings][-u user][-t timeout][-r option...] [-s] [directory ...]",
__progname); __progname);
exit(EX_USAGE); exit(EX_USAGE);
} }
#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
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
@ -178,20 +198,24 @@ main(int argc, char **argv)
struct passwd *pw; struct passwd *pw;
struct options *opt; struct options *opt;
struct sockaddr_in myaddr; struct sockaddr_in myaddr;
struct sockaddr_in bindaddr;
int n = 0; int n = 0;
int on = 1; int on = 1;
int fd = 0; int fd = 0;
int listen = 0; /* Standalone (listen) mode */
char *address = NULL; /* Address to listen to */
int pid; int pid;
int c; int c;
int setrv; int setrv;
int timeout = 900; /* Default timeout */ int timeout = 900; /* Default timeout */
char *user = "nobody"; /* Default user */ char *user = "nobody"; /* Default user */
char *rewrite_file = NULL;
__progname = basename(argv[0]); __progname = basename(argv[0]);
openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
while ((c = getopt(argc, argv, "csvu:r:t:m:")) != -1) while ((c = getopt(argc, argv, "csvla:u:r:t:m:")) != -1)
switch (c) { switch (c) {
case 'c': case 'c':
cancreate = 1; cancreate = 1;
@ -199,6 +223,12 @@ main(int argc, char **argv)
case 's': case 's':
secure = 1; secure = 1;
break; break;
case 'l':
listen = 1;
break;
case 'a':
address = optarg;
break;
case 't': case 't':
timeout = atoi(optarg); timeout = atoi(optarg);
break; break;
@ -219,20 +249,11 @@ main(int argc, char **argv)
break; break;
#ifdef WITH_REGEX #ifdef WITH_REGEX
case 'm': case 'm':
{ if ( rewrite_file ) {
FILE *f; syslog(LOG_ERR, "Multiple -m options");
if ( rewrite_rules ) { exit(EX_USAGE);
syslog(LOG_ERR, "Multiple -m options");
exit(EX_USAGE);
}
f = fopen(optarg, "rt");
if ( !f ) {
syslog(LOG_ERR, "Cannot open map file: %s: %m", optarg);
exit(EX_NOINPUT);
}
rewrite_rules = parserulefile(f);
fclose(f);
} }
rewrite_file = optarg;
break; break;
#endif #endif
case 'v': case 'v':
@ -282,7 +303,74 @@ main(int argc, char **argv)
syslog(LOG_ERR, "ioctl(FIONBIO): %m"); syslog(LOG_ERR, "ioctl(FIONBIO): %m");
exit(EX_OSERR); exit(EX_OSERR);
} }
if ( rewrite_file )
rewrite_rules = read_remap_rules(rewrite_file);
/* If we're running standalone, set up the input port */
if ( listen ) {
fd = socket(PF_INET, SOCK_DGRAM, 0);
memset(&bindaddr, 0, sizeof bindaddr);
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, &bindaddr, sizeof bindaddr) ) {
syslog(LOG_ERR, "cannot bind to local socket: %m");
exit(EX_OSERR);
}
/* Daemonize this process */
{
int f = fork();
if ( f > 0 )
exit(0);
if ( f < 0 ) {
syslog(LOG_ERR, "cannot fork: %m");
exit(EX_OSERR);
}
close(0); close(1); close(2);
setsid();
}
}
/* This means we don't want to wait() for children */ /* This means we don't want to wait() for children */
set_signal(SIGCHLD, SIG_IGN, SA_NOCLDSTOP); set_signal(SIGCHLD, SIG_IGN, SA_NOCLDSTOP);
@ -296,25 +384,48 @@ main(int argc, char **argv)
struct timeval tv_timeout; struct timeval tv_timeout;
int rv; int rv;
if ( caught_sighup ) {
caught_sighup = 0;
if ( listen ) {
#ifdef HAVE_REGEX
/* This is unfortunately a memory leak. Hopefully
SIGHUPs aren't too common. */
if ( rewrite_file )
rewrite_rules = read_remap_rules(rewrite_file);
#endif
} else {
/* Return to inetd for respawn */
exit(0);
}
}
FD_ZERO(&readset); FD_ZERO(&readset);
FD_SET(fd, &readset); FD_SET(fd, &readset);
tv_timeout.tv_sec = timeout; tv_timeout.tv_sec = timeout;
tv_timeout.tv_usec = 0; tv_timeout.tv_usec = 0;
if ( caught_sighup ) /* Never time out if we're in listen mode */
exit(0); /* Return to inetd for respawn */ rv = select(fd+1, &readset, NULL, NULL, listen ? NULL : &tv_timeout);
rv = select(fd+1, &readset, NULL, NULL, &tv_timeout);
if ( rv == -1 && errno == EINTR ) if ( rv == -1 && errno == EINTR )
continue; /* Signal caught, reloop */ continue; /* Signal caught, reloop */
if ( rv <= 0 ) if ( rv == -1 ) {
exit(0); /* Timeout or error, return to inetd */ syslog(LOG_ERR, "select loop: %m");
exit(EX_OSERR);
} else if ( rv == 0 ) {
exit(0); /* Timeout, return to inetd */
}
fromlen = sizeof (from); fromlen = sizeof (from);
n = myrecvfrom(fd, buf, sizeof (buf), 0, n = myrecvfrom(fd, buf, sizeof (buf), 0,
(struct sockaddr *)&from, &fromlen, (struct sockaddr *)&from, &fromlen,
&myaddr); &myaddr);
if ( listen && myaddr.sin_addr.s_addr == INADDR_ANY ) {
/* myrecvfrom() didn't capture the source address; but we might
have bound to a specific address, if so we should use it */
memcpy(&myaddr.sin_addr, &bindaddr.sin_addr, sizeof bindaddr.sin_addr);
}
if (n < 0) { if (n < 0) {
syslog(LOG_ERR, "recvfrom: %m"); syslog(LOG_ERR, "recvfrom: %m");
exit(EX_IOERR); exit(EX_IOERR);
@ -386,7 +497,9 @@ main(int argc, char **argv)
/* Close file descriptors we don't need */ /* Close file descriptors we don't need */
close(fd); close(fd);
close(0);
close(1); close(1);
close(2);
/* Other basic setup */ /* Other basic setup */
from.sin_family = AF_INET; from.sin_family = AF_INET;