Hello! The attached patch makes ‘tftpd’ IPv6-compatible. As a side effect, it makes it usable (!) on current GNU/Linux when a client connects via an address other than the loopback address (FROM is always AF_INET6 on my machine.)
The patch assumes that IPv6 support is available. Besides, it’d be better to import Gnulib’s ‘inet_ntop’ modules. Comments? Thanks, Ludo’. PS: My copyright assignment is now on file.
From 3be7f44305103b39a0e1bc79a9960feb5d142485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20Court=C3=A8s?= <l...@gnu.org> Date: Tue, 7 Sep 2010 17:53:23 +0200 Subject: [PATCH] Make `tftpd' IPv6-compatible. * src/tftpd.c (from): Change from `sockaddr_in' to `sockaddr_storage'. (main): Likewise for `sin'. Have PEER and SIN have the same family as FROM. (verifyhost): Rename to... (verify_host): ... this. Rewrite to handle both IPv6 and IPv4. Update callers. * tests/Makefile.am (dist_check_SCRIPTS)[ENABLE_inetd && ENABLE_tftpd && ENABLE_tftp]: Add `tftp.sh'. * tests/tftp.sh: New file. --- src/tftpd.c | 48 +++++++++++++++++++++----------- tests/Makefile.am | 9 ++++++ tests/tftp.sh | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 16 deletions(-) create mode 100755 tests/tftp.sh diff --git a/src/tftpd.c b/src/tftpd.c index 70602fa..719db81 100644 --- a/src/tftpd.c +++ b/src/tftpd.c @@ -101,7 +101,7 @@ static int maxtimeout = 5 * TIMEOUT; #define PKTSIZE SEGSIZE+4 static char buf[PKTSIZE]; static char ackbuf[PKTSIZE]; -static struct sockaddr_in from; +static struct sockaddr_storage from; static socklen_t fromlen; void tftp (struct tftphdr *, int); @@ -124,7 +124,8 @@ static int logging; static const char *errtomsg (int); static void nak (int); -static const char *verifyhost (struct sockaddr_in *); +static char *verify_host (struct sockaddr_storage *, socklen_t, + char *, size_t); @@ -172,7 +173,7 @@ main (int argc, char *argv[]) int index; register struct tftphdr *tp; int on, n; - struct sockaddr_in sin; + struct sockaddr_storage sin; set_program_name (argv[0]); iu_argp_init ("tftpd", default_program_authors); @@ -268,18 +269,19 @@ main (int argc, char *argv[]) exit (EXIT_SUCCESS); } } - from.sin_family = AF_INET; + alarm (0); close (0); close (1); - peer = socket (AF_INET, SOCK_DGRAM, 0); + + peer = socket (from.ss_family, SOCK_DGRAM, 0); if (peer < 0) { syslog (LOG_ERR, "socket: %m\n"); exit (EXIT_FAILURE); } memset (&sin, 0, sizeof (sin)); - sin.sin_family = AF_INET; + sin.ss_family = from.ss_family; if (bind (peer, (struct sockaddr *) &sin, sizeof (sin)) < 0) { syslog (LOG_ERR, "bind: %m\n"); @@ -360,8 +362,10 @@ again: ecode = (*pf->f_validate) (&filename, tp->th_opcode); if (logging) { + char node[1024]; syslog (LOG_INFO, "%s: %s request for %s: %s", - verifyhost (&from), + verify_host (&from, sizeof from, + node, sizeof node), tp->th_opcode == WRQ ? "write" : "read", filename, errtomsg (ecode)); } @@ -736,17 +740,29 @@ nak (int error) syslog (LOG_ERR, "nak: %m\n"); } -static const char * -verifyhost (struct sockaddr_in *fromp) +static char * +verify_host (struct sockaddr_storage *addr, socklen_t addr_len, + char *host_and_service, size_t len) { - struct hostent *hp; - - hp = gethostbyaddr ((char *) &fromp->sin_addr, sizeof (fromp->sin_addr), - fromp->sin_family); - if (hp) - return hp->h_name; + int err; + char host[512], service[256]; + + err = getnameinfo ((struct sockaddr *) addr, addr_len, + host, sizeof host, + service, sizeof service, + 0); + + if (err) + inet_ntop (addr->ss_family, + addr->ss_family == AF_INET + ? (void *) &((struct sockaddr_in *) addr)->sin_addr + : (void *) &((struct sockaddr_in6 *) addr)->sin6_addr, + host_and_service, len); else - return inet_ntoa (fromp->sin_addr); + snprintf (host_and_service, len, "%s%s%s", + host, *service ? "/" : "", service); + + return host_and_service; } static const char usage_str[] = diff --git a/tests/Makefile.am b/tests/Makefile.am index f64a066..4f4610a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -28,6 +28,15 @@ endif if ENABLE_traceroute dist_check_SCRIPTS += traceroute-localhost.sh endif + +if ENABLE_inetd +if ENABLE_tftpd +if ENABLE_tftp +dist_check_SCRIPTS += tftp.sh +endif +endif +endif + TESTS = $(check_PROGRAMS) $(dist_check_SCRIPTS) TESTS_ENVIRONMENT = EXEEXT=$(EXEEXT) diff --git a/tests/tftp.sh b/tests/tftp.sh new file mode 100755 index 0000000..08d5d86 --- /dev/null +++ b/tests/tftp.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +# Copyright (C) 2010 Free Software Foundation, Inc. +# +# This file is part of GNU Inetutils. +# +# GNU Inetutils is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or (at +# your option) any later version. +# +# GNU Inetutils is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see `http://www.gnu.org/licenses/'. + +# Run `inetd' with `tftpd' and try to fetch a file from there using `tftp'. + +TFTP="${TFTP:-../src/tftp$EXEEXT}" +TFTPD="${TFTPD:-../src/tftpd$EXEEXT}" +INETD="${INETD:-../src/inetd$EXEEXT}" +IFCONFIG="${IFCONFIG:-../ifconfig/ifconfig$EXEEXT}" + +PORT=7777 +INETD_CONF="inetd.conf.tmp" + +ADDRESSES="`$IFCONFIG | grep 'inet addr:' | \ + sed -e's/inet addr:\([^ ]\+\)[[:blank:]].*$/\1/g'`" + +if [ "$VERBOSE" ]; then + set -x + "$TFTP" --version + "$TFTPD" --version + "$INETD" --version +fi + +cat > "$INETD_CONF" <<EOF +$PORT dgram udp wait $USER $TFTPD tftpd -l `pwd`/tftp-test +EOF + +# Launch `inetd', assuming it's reachable at all $ADDRESSES. +$INETD "${VERBOSE:+-d}" "$INETD_CONF" & +inetd_pid="$!" + +if [ -f /dev/urandom ]; then + input="/dev/urandom" +else + input="/dev/zero" +fi + +rm -fr tftp-test tftp-test-file +mkdir tftp-test && \ + dd if="$input" of="tftp-test/tftp-test-file" bs=1024 count=170 + +for addr in $ADDRESSES +do + echo "trying with address \`$addr'..." >&2 + + rm -f tftp-test-file + echo "get tftp-test-file" | "$TFTP" $addr $PORT + + cmp tftp-test/tftp-test-file tftp-test-file + result=$? + + if [ "$result" -ne 0 ]; then + # Failure. + break + fi +done + +kill "$inetd_pid" + +rm -rf tftp-test tftp-test-file "$INETD_CONF" + +exit $result -- 1.7.0
pgpVCg6EdBWfh.pgp
Description: PGP signature