tftpd: switch to getopt_long()

Switch to using getopt_long(); include a version in case the platform
lacks it.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
This commit is contained in:
H. Peter Anvin 2008-07-30 17:16:00 -07:00
parent 77fbfeebee
commit e4d3083006
6 changed files with 253 additions and 45 deletions

View file

@ -110,11 +110,10 @@
#include <netdb.h> #include <netdb.h>
#endif #endif
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_LONG
#include <getopt.h> #include <getopt.h>
#else #else
extern char *optarg; #include "lib/getopt.h"
extern int optind, opterr, optopt;
#endif #endif
/* Test for EAGAIN/EWOULDBLOCK */ /* Test for EAGAIN/EWOULDBLOCK */

View file

@ -35,7 +35,6 @@ AC_CHECK_HEADERS(inttypes.h)
AC_CHECK_HEADERS(stdint.h) AC_CHECK_HEADERS(stdint.h)
PA_CHECK_INTTYPES_H_SANE PA_CHECK_INTTYPES_H_SANE
AC_CHECK_HEADERS(fcntl.h) AC_CHECK_HEADERS(fcntl.h)
AC_CHECK_HEADERS(getopt.h)
AC_CHECK_HEADERS(grp.h) AC_CHECK_HEADERS(grp.h)
AC_CHECK_HEADERS(libgen.h) AC_CHECK_HEADERS(libgen.h)
AC_CHECK_HEADERS(memory.h) AC_CHECK_HEADERS(memory.h)
@ -155,6 +154,7 @@ XTRA=false
PA_SEARCH_LIBS_AND_ADD(xmalloc, iberty) PA_SEARCH_LIBS_AND_ADD(xmalloc, iberty)
PA_SEARCH_LIBS_AND_ADD(xstrdup, iberty) PA_SEARCH_LIBS_AND_ADD(xstrdup, iberty)
PA_SEARCH_LIBS_AND_ADD(bsd_signal, bsd, bsdsignal) PA_SEARCH_LIBS_AND_ADD(bsd_signal, bsd, bsdsignal)
PA_SEARCH_LIBS_AND_ADD(getopt_long, getopt, getopt_long)
PA_SEARCH_LIBS_AND_ADD(getaddrinfo, [nsl resolv]) PA_SEARCH_LIBS_AND_ADD(getaddrinfo, [nsl resolv])
if $pa_add_getaddrinfo if $pa_add_getaddrinfo
then then

23
lib/getopt.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef LIB_GETOPT_H
#define LIB_GETOPT_H
extern char *optarg;
extern int optind, opterr, optopt;
struct option {
const char *name;
int has_arg;
int *flag;
int val;
};
enum {
no_argument = 0,
required_argument = 1,
optional_argument = 2,
};
int getopt_long(int, char *const *, const char *,
const struct option *, int *);
#endif /* LIB_GETOPT_H */

150
lib/getopt_long.c Normal file
View file

@ -0,0 +1,150 @@
/*
* getopt_long.c
*
* getopt_long(), or at least a common subset thereof:
*
* - Option reordering is not supported
* - -W foo is not supported
* - First optstring character "-" not supported.
*/
#include "config.h"
char *optarg;
int optind, opterr, optopt;
static struct getopt_private_state {
const char *optptr;
const char *last_optstring;
char *const *last_argv;
} pvt;
static inline const char *option_matches(const char *arg_str,
const char *opt_name)
{
while (*arg_str != '\0' && *arg_str != '=') {
if (*arg_str++ != *opt_name++)
return NULL;
}
if (*opt_name)
return NULL;
return arg_str;
}
int getopt_long(int argc, char *const *argv, const char *optstring,
const struct option *longopts, int *longindex)
{
const char *carg;
const char *osptr;
int opt;
/* getopt() relies on a number of different global state
variables, which can make this really confusing if there is
more than one use of getopt() in the same program. This
attempts to detect that situation by detecting if the
"optstring" or "argv" argument have changed since last time
we were called; if so, reinitialize the query state. */
if (optstring != pvt.last_optstring || argv != pvt.last_argv ||
optind < 1 || optind > argc) {
/* optind doesn't match the current query */
pvt.last_optstring = optstring;
pvt.last_argv = argv;
optind = 1;
pvt.optptr = NULL;
}
carg = argv[optind];
/* First, eliminate all non-option cases */
if (!carg || carg[0] != '-' || !carg[1])
return -1;
if (carg[1] == '-') {
const struct option *lo;
const char *opt_end = NULL;
optind++;
/* Either it's a long option, or it's -- */
if (!carg[2]) {
/* It's -- */
return -1;
}
for (lo = longopts; lo->name; lo++) {
if ((opt_end = option_matches(carg+2, lo->name)))
break;
}
if (!opt_end)
return '?';
if (longindex)
*longindex = lo-longopts;
if (*opt_end == '=') {
if (lo->has_arg)
optarg = (char *)opt_end+1;
else
return '?';
} else if (lo->has_arg == 1) {
if (!(optarg = argv[optind]))
return '?';
optind++;
}
if (lo->flag) {
*lo->flag = lo->val;
return 0;
} else {
return lo->val;
}
}
if ((uintptr_t) (pvt.optptr - carg) > (uintptr_t) strlen(carg)) {
/* Someone frobbed optind, change to new opt. */
pvt.optptr = carg + 1;
}
opt = *pvt.optptr++;
if (opt != ':' && (osptr = strchr(optstring, opt))) {
if (osptr[1] == ':') {
if (*pvt.optptr) {
/* Argument-taking option with attached
argument */
optarg = (char *)pvt.optptr;
optind++;
} else {
/* Argument-taking option with non-attached
argument */
if (argv[optind + 1]) {
optarg = (char *)argv[optind+1];
optind += 2;
} else {
/* Missing argument */
optind++;
return (optstring[0] == ':')
? ':' : '?';
}
}
return opt;
} else {
/* Non-argument-taking option */
/* pvt.optptr will remember the exact position to
resume at */
if (!*pvt.optptr)
optind++;
return opt;
}
} else {
/* Unknown option */
optopt = opt;
if (!*pvt.optptr)
optind++;
return '?';
}
}

View file

@ -3,7 +3,7 @@
.\" Copyright (c) 1990, 1993, 1994 .\" Copyright (c) 1990, 1993, 1994
.\" The Regents of the University of California. All rights reserved. .\" The Regents of the University of California. All rights reserved.
.\" .\"
.\" Copyright 2001 H. Peter Anvin - All Rights Reserved .\" Copyright 2001-2008 H. Peter Anvin - All Rights Reserved
.\" .\"
.\" Redistribution and use in source and binary forms, with or without .\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions .\" modification, are permitted provided that the following conditions
@ -30,7 +30,7 @@
.\" SUCH DAMAGE. .\" SUCH DAMAGE.
.\" .\"
.\"----------------------------------------------------------------------- */ .\"----------------------------------------------------------------------- */
.TH TFTPD 8 "23 July 2008" "tftp-hpa @@VERSION@@" "System Manager's Manual" .TH TFTPD 8 "30 July 2008" "tftp-hpa @@VERSION@@" "System Manager's Manual"
.SH NAME .SH NAME
.B tftpd .B tftpd
\- IPv4 Trivial File Transfer Protocol server \- IPv4 Trivial File Transfer Protocol server
@ -48,36 +48,37 @@ but can also run standalone.
.PP .PP
.SH OPTIONS .SH OPTIONS
.TP .TP
.B \-4 \fB\-\-ipv4\fP, \fB\-4\fP
Connect with IPv4 only, even if IPv6 support was compiled in. Connect with IPv4 only, even if IPv6 support was compiled in.
.TP .TP
.B \-6 \fB\-\-ipv6\fP, \fB\-6\fP
Connect with IPv6 only, if compiled in. Connect with IPv6 only, if compiled in.
.TP .TP
.B \-l \fB\-l\fP, \fB\-\-listen\fP
Run the server in standalone (listen) mode, rather than run from Run the server in standalone (listen) mode, rather than run from
.BR inetd . .BR inetd .
In listen mode, the In listen mode, the
.B \-t .B \-\-timeout
option is ignored, and the option is ignored, and the
.B \-a .B \-\-address
option can be used to specify a specific local address or port to option can be used to specify a specific local address or port to
listen to. listen to.
.TP .TP
.B \-L \fB\-\-foreground\fP, \fB\-L\fP
Similar to Similar to
.B \-l .B \-\-listen
but do not detach from the foreground process. but do not detach from the foreground process. Implies
.BR \-\-listen .
.TP .TP
\fB\-a\fP \fI[address][:port]\fP \fB\-\-address\fP \fI[address][:port]\fP, \fB\-a\fP \fI[address][:port]\fP
Specify a specific Specify a specific
.I address .I address
and and
.I port .I port
to listen to when called with the to listen to when called with the
.B \-l .B \-\-listen
or or
.B \-L .B \-\-foreground
option. The default is to listen to the option. The default is to listen to the
.I tftp .I tftp
port specified in port specified in
@ -88,29 +89,29 @@ on all local addresses.
Numeric IPv6 adresses must be enclosed in square brackets Numeric IPv6 adresses must be enclosed in square brackets
to avoid ambiguity with the optional port information. to avoid ambiguity with the optional port information.
.TP .TP
.B \-c \fB\-\-create\fP, \fB\-c\fP
Allow new files to be created. By default, Allow new files to be created. By default,
.B tftpd .B tftpd
will only allow upload of files that already exist. Files are created will only allow upload of files that already exist. Files are created
with default permissions allowing anyone to read or write them, unless with default permissions allowing anyone to read or write them, unless
the the
.B \-p .B \-\-permissive
or or
.B \-U .B \-\-umask
options are specified. options are specified.
.TP .TP
.B \-s \fB\-\-secure\fP, \fB\-s\fP
Change root directory on startup. This means the remote host does not Change root directory on startup. This means the remote host does not
need to pass along the directory as part of the transfer, and may add need to pass along the directory as part of the transfer, and may add
security. When security. When
.B \-s .B \-\-secure
is specified, exactly one is specified, exactly one
.I directory .I directory
should be specified on the command line. The use of this option is should be specified on the command line. The use of this option is
recommended for security as well as compatibility with some boot ROMs recommended for security as well as compatibility with some boot ROMs
which cannot be easily made to include a directory name in its request. which cannot be easily made to include a directory name in its request.
.TP .TP
\fB\-u\fP \fIusername\fP \fB\-\-user\fP \fIusername\fP, \fB\-u\fP \fIusername\fP
Specify the username which Specify the username which
.B tftpd .B tftpd
will run as; the default is "nobody". The user ID, group ID, and (if will run as; the default is "nobody". The user ID, group ID, and (if
@ -118,21 +119,21 @@ possible on the platform) the supplementary group IDs will be set to
the ones specified in the system permission database for this the ones specified in the system permission database for this
username. username.
.TP .TP
\fB\-U\fP \fIumask\fP \fB\-\-umask\fP \fIumask\fP, \fB\-U\fP \fIumask\fP
Sets the \fIumask\fP for newly created files to the specified value. Sets the \fIumask\fP for newly created files to the specified value.
The default is zero (anyone can read or write) if the The default is zero (anyone can read or write) if the
.B \-p .B \-\-permissive
option is not specified, or inherited from the invoking process if option is not specified, or inherited from the invoking process if
.B \-p .B \-\-permissive
is specified. is specified.
.TP .TP
.B \-p \fB\-\-permissive\fP, \fB\-p\fP
Perform no additional permissions checks above the normal Perform no additional permissions checks above the normal
system-provided access controls for the user specified via the system-provided access controls for the user specified via the
.B \-u .B \-\-user
option. option.
.TP .TP
\fB\-t\fP \fItimeout\fP \fB\-\-timeout\fP \fItimeout\fP, \fB\-t\fP \fItimeout\fP
When run from When run from
.B inetd .B inetd
this specifies how long, in seconds, to wait for a second connection this specifies how long, in seconds, to wait for a second connection
@ -141,7 +142,7 @@ before terminating the server.
will then respawn the server when another request comes in. The will then respawn the server when another request comes in. The
default is 900 (15 minutes.) default is 900 (15 minutes.)
.TP .TP
\fB\-T\fP \fItimeout\fP \fB\-\-retransmit\fP \fItimeout, \fP\fB\-T\fP \fItimeout\fP
Determine the default timeout, in microseconds, before the first Determine the default timeout, in microseconds, before the first
packet is retransmitted. This can be modified by the client if the packet is retransmitted. This can be modified by the client if the
.B timeout .B timeout
@ -149,7 +150,7 @@ or
.B utimeout .B utimeout
option is negotiated. The default is 1000000 (1 second.) option is negotiated. The default is 1000000 (1 second.)
.TP .TP
\fB\-m\fP \fIremap-file\fP \fB\-\-mapfile\fP \fIremap-file\fP, \fB\-m\fP \fIremap-file\fP
Specify the use of filename remapping. The Specify the use of filename remapping. The
.I remap-file .I remap-file
is a file containing the remapping rules. See the section on filename is a file containing the remapping rules. See the section on filename
@ -157,16 +158,19 @@ remapping below. This option may not be compiled in, see the output of
.B "in.tftpd \-V" .B "in.tftpd \-V"
to verify whether or not it is available. to verify whether or not it is available.
.TP .TP
.B \-v \fB\-\-verbose\fP, \fB\-v\fP
Increase the logging verbosity of Increase the logging verbosity of
.BR tftpd . .BR tftpd .
This flag can be specified multiple times for even higher verbosity. This flag can be specified multiple times for even higher verbosity.
.TP .TP
\fB\-r\fP \fItftp-option\fP \fB\-\-verbosity\fP \fIvalue\fP
Set the verbosity value to \fIvalue\fP.
.TP
\fB\-\-refuse\fP \fItftp-option\fP, \fB\-r\fP \fItftp-option\fP
Indicate that a specific RFC 2347 TFTP option should never be Indicate that a specific RFC 2347 TFTP option should never be
accepted. accepted.
.TP .TP
\fB\-B\fP \fImax-block-size\fP \fB\-\-blocksize\fP \fImax-block-size\fP, \fB\-B\fP \fImax-block-size\fP
Specifies the maximum permitted block size. The permitted range for Specifies the maximum permitted block size. The permitted range for
this parameter is from 512 to 65464. Some embedded clients request this parameter is from 512 to 65464. Some embedded clients request
large block sizes and yet do not handle fragmented packets correctly; large block sizes and yet do not handle fragmented packets correctly;
@ -175,11 +179,11 @@ MTU on your network minus 32 bytes (20 bytes for IP, 8 for UDP, and 4
for TFTP; less if you use IP options on your network.) For example, for TFTP; less if you use IP options on your network.) For example,
on a standard Ethernet (MTU 1500) a value of 1468 is reasonable. on a standard Ethernet (MTU 1500) a value of 1468 is reasonable.
.TP .TP
\fB\-R\fP \fIport:port\fP \fB\-\-port-range\fP \fIport:port\fP, \fB\-R\fP \fIport:port\fP
Force the server port number (the Transaction ID) to be in the Force the server port number (the Transaction ID) to be in the
specified range of port numbers. specified range of port numbers.
.TP .TP
.B \-V \fB\-\-version\fP, \fB\-V\fP
Print the version number and configuration to standard output, then Print the version number and configuration to standard output, then
exit gracefully. exit gracefully.
.SH "RFC 2347 OPTION NEGOTIATION" .SH "RFC 2347 OPTION NEGOTIATION"
@ -216,7 +220,7 @@ Set the time before the server retransmits a packet, in seconds.
Set the time before the server retransmits a packet, in microseconds. Set the time before the server retransmits a packet, in microseconds.
.PP .PP
The The
.B \-r .B \-\-refuse
option can be used to disable specific options; this may be necessary option can be used to disable specific options; this may be necessary
to work around bugs in specific TFTP client implementations. For to work around bugs in specific TFTP client implementations. For
example, some TFTP clients have been found to request the example, some TFTP clients have been found to request the
@ -225,7 +229,7 @@ option, but crash with an error if they actually get the option
accepted by the server. accepted by the server.
.SH "FILENAME REMAPPING" .SH "FILENAME REMAPPING"
The The
.B \-m .B \-\-mapfile
option specifies a file which contains filename remapping rules. Each option specifies a file which contains filename remapping rules. Each
non-comment line (comments begin with hash marks, non-comment line (comments begin with hash marks,
.BR # ) .BR # )
@ -340,17 +344,17 @@ The use of TFTP services does not require an account or password on
the server system. Due to the lack of authentication information, the server system. Due to the lack of authentication information,
.B tftpd .B tftpd
will allow only publicly readable files (o+r) to be accessed, unless the will allow only publicly readable files (o+r) to be accessed, unless the
.B \-p .B \-\-permissive
option is specified. Files may be written only if they already exist option is specified. Files may be written only if they already exist
and are publicly writable, unless the and are publicly writable, unless the
.B \-c .B \-\-create
option is specified. Note that this extends the concept of ``public'' option is specified. Note that this extends the concept of ``public''
to include all users on all hosts that can be reached through the to include all users on all hosts that can be reached through the
network; this may not be appropriate on all systems, and its network; this may not be appropriate on all systems, and its
implications should be considered before enabling TFTP service. implications should be considered before enabling TFTP service.
Typically, some kind of firewall or packet-filter solution should be Typically, some kind of firewall or packet-filter solution should be
employed. If appropriately compiled (see the output of employed. If appropriately compiled (see the output of
.BR "in.tftpd \-V" ) .BR "in.tftpd \-\-version" )
.B tftpd .B tftpd
will query the will query the
.BR hosts_access (5) .BR hosts_access (5)
@ -360,7 +364,7 @@ and rely on firewalling or kernel-based packet filters instead.
.PP .PP
The server should be set to run as the user with the lowest possible The server should be set to run as the user with the lowest possible
privilege; please see the privilege; please see the
.B \-u .B \-\-user
flag. It is probably a good idea to set up a specific user account for flag. It is probably a good idea to set up a specific user account for
.BR tftpd , .BR tftpd ,
rather than letting it run as "nobody", to guard against privilege rather than letting it run as "nobody", to guard against privilege
@ -372,12 +376,12 @@ with a list of directories by including pathnames as server program
arguments on the command line. In this case access is restricted to arguments on the command line. In this case access is restricted to
files whole names are prefixed by one of the given directories. If files whole names are prefixed by one of the given directories. If
possible, it is recommended that the possible, it is recommended that the
.B \-s .B \-\-secure
flag is used to set up a chroot() environment for the server to run in flag is used to set up a chroot() environment for the server to run in
once a connection has been set up. once a connection has been set up.
.PP .PP
Finally, the filename remapping Finally, the filename remapping
.RB ( \-m .RB ( \-\-mapfile
flag) support can be used to provide a limited amount of additional flag) support can be used to provide a limited amount of additional
access control. access control.
.SH "CONFORMING TO" .SH "CONFORMING TO"

View file

@ -277,6 +277,34 @@ static int split_port(char **ap, char **pp)
return 0; return 0;
} }
enum long_only_options {
OPT_VERBOSITY = 256,
};
static struct option long_options[] = {
{ "ipv4", 0, NULL, '4' },
{ "ipv6", 0, NULL, '6' },
{ "create", 0, NULL, 'c' },
{ "secure", 0, NULL, 's' },
{ "permissive", 0, NULL, 'p' },
{ "verbose", 0, NULL, 'v' },
{ "verbosity", 1, NULL, OPT_VERBOSITY },
{ "version", 0, NULL, 'V' },
{ "listen", 0, NULL, 'l' },
{ "foreground", 0, NULL, 'L' },
{ "address", 1, NULL, 'a' },
{ "blocksize", 1, NULL, 'B' },
{ "user", 1, NULL, 'u' },
{ "umask", 1, NULL, 'U' },
{ "refuse", 1, NULL, 'r' },
{ "timeout", 1, NULL, 't' },
{ "retransmit", 1, NULL, 'T' },
{ "port-range", 1, NULL, 'R' },
{ "map-file", 1, NULL, 'm' },
{ NULL, 0, NULL, 0 }
};
static const char short_options[] = "46cspvVlLa:B:u:U:r:t:T:R:m:";
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
struct tftphdr *tp; struct tftphdr *tp;
@ -317,7 +345,8 @@ int main(int argc, char **argv)
srand(time(NULL) ^ getpid()); srand(time(NULL) ^ getpid());
while ((c = getopt(argc, argv, "46cspvVlLa:B:u:U:r:t:T:R:m:")) != -1) while ((c = getopt_long(argc, argv, short_options, long_options, NULL))
!= -1)
switch (c) { switch (c) {
case '4': case '4':
ai_fam = AF_INET; ai_fam = AF_INET;
@ -419,6 +448,9 @@ int main(int argc, char **argv)
case 'v': case 'v':
verbosity++; verbosity++;
break; break;
case OPT_VERBOSITY:
verbosity = atoi(optarg);
break;
case 'V': case 'V':
/* Print configuration to stdout and exit */ /* Print configuration to stdout and exit */
printf("%s\n", TFTPD_CONFIG_STR); printf("%s\n", TFTPD_CONFIG_STR);