From 8c3d63fba843b557a817ac23646a240a50f67964 Mon Sep 17 00:00:00 2001 From: hpa Date: Tue, 7 Aug 2001 00:27:52 +0000 Subject: [PATCH] Support running without inetd. --- README | 6 +- tftpd/tftpd.8 | 31 ++++++++-- tftpd/tftpd.c | 161 ++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 167 insertions(+), 31 deletions(-) diff --git a/README b/README index 3cc732a..e1c2012 100644 --- a/README +++ b/README @@ -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 . The latest version of this collection can be found at: @@ -18,6 +18,10 @@ improvements. ===> IMPORTANT: SEE THE FILE "README.security" FOR IMPORTANT SECURITY ===> CHANGES ENACTED IN VERSION 0.17!!!!!!!!! +Changes in 0.21: + Support running in standalone mode, without inetd. + + Changes in 0.20: Portability improvements. Now known to compile and run on Solaris 8. diff --git a/tftpd/tftpd.8 b/tftpd/tftpd.8 index ad35006..505bacf 100644 --- a/tftpd/tftpd.8 +++ b/tftpd/tftpd.8 @@ -35,7 +35,7 @@ .\" from: @(#)tftpd.8 6.7 (Berkeley) 5/13/91 .\" $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 .Os .Sh NAME @@ -46,8 +46,11 @@ IPv4 Trivial File Transfer Protocol server .Nm in.tftpd .Op Fl v .Op Fl c +.Op Fl l +.Op Fl a Ar [address][:port] .Op Fl m Ar mapfile .Op Fl u Ar userid +.Op Fl t Ar timeout .Op Fl r Ar option... .Op Fl s .Op Ar directory @@ -66,7 +69,8 @@ service description; see .Xr services 5 . The server is normally started by -.Xr inetd 8 . +.Xr inetd 8 , +but can also run standalone. .Pp The use of .Xr tftp 1 @@ -91,9 +95,20 @@ Access to files may be restricted by invoking .Nm with a list of directories by including pathnames as server program arguments in -.Pa /etc/inetd.conf . -In this case access is restricted to files whose -names are prefixed by the one of the given directories. +.Pa /etc/inetd.conf +or on the standalone server command line. In this case access is +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 If the .Fl c @@ -116,11 +131,15 @@ which do not include a directory name. .Pp The .Fl u -flag can be used to specify a user ID which +option can be used to specify a user ID which .Nm will run as; the default is ``nobody''. .Pp The +.Fl t +option specifies the server timeout in inetd mode. +.Pp +The .Fl m flag specifies a file which contains filename remapping rules. .Pp diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index fda6ce1..2a34501 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -166,11 +166,31 @@ static void handle_sighup(int sig) static 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); 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 main(int argc, char **argv) { @@ -178,20 +198,24 @@ main(int argc, char **argv) struct passwd *pw; struct options *opt; struct sockaddr_in myaddr; + struct sockaddr_in bindaddr; int n = 0; int on = 1; int fd = 0; + int listen = 0; /* Standalone (listen) mode */ + char *address = NULL; /* Address to listen to */ int pid; int c; int setrv; - int timeout = 900; /* Default timeout */ + int timeout = 900; /* Default timeout */ char *user = "nobody"; /* Default user */ + char *rewrite_file = NULL; __progname = basename(argv[0]); 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) { case 'c': cancreate = 1; @@ -199,6 +223,12 @@ main(int argc, char **argv) case 's': secure = 1; break; + case 'l': + listen = 1; + break; + case 'a': + address = optarg; + break; case 't': timeout = atoi(optarg); break; @@ -219,20 +249,11 @@ main(int argc, char **argv) break; #ifdef WITH_REGEX case 'm': - { - FILE *f; - if ( rewrite_rules ) { - 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); + if ( rewrite_file ) { + syslog(LOG_ERR, "Multiple -m options"); + exit(EX_USAGE); } + rewrite_file = optarg; break; #endif case 'v': @@ -282,7 +303,74 @@ main(int argc, char **argv) syslog(LOG_ERR, "ioctl(FIONBIO): %m"); 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 */ set_signal(SIGCHLD, SIG_IGN, SA_NOCLDSTOP); @@ -296,25 +384,48 @@ main(int argc, char **argv) struct timeval tv_timeout; 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_SET(fd, &readset); tv_timeout.tv_sec = timeout; tv_timeout.tv_usec = 0; - if ( caught_sighup ) - exit(0); /* Return to inetd for respawn */ - - rv = select(fd+1, &readset, NULL, NULL, &tv_timeout); + /* Never time out if we're in listen mode */ + rv = select(fd+1, &readset, NULL, NULL, listen ? NULL : &tv_timeout); if ( rv == -1 && errno == EINTR ) continue; /* Signal caught, reloop */ - if ( rv <= 0 ) - exit(0); /* Timeout or error, return to inetd */ + if ( rv == -1 ) { + syslog(LOG_ERR, "select loop: %m"); + exit(EX_OSERR); + } else if ( rv == 0 ) { + exit(0); /* Timeout, return to inetd */ + } fromlen = sizeof (from); n = myrecvfrom(fd, buf, sizeof (buf), 0, (struct sockaddr *)&from, &fromlen, &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) { syslog(LOG_ERR, "recvfrom: %m"); exit(EX_IOERR); @@ -386,7 +497,9 @@ main(int argc, char **argv) /* Close file descriptors we don't need */ close(fd); + close(0); close(1); + close(2); /* Other basic setup */ from.sin_family = AF_INET;