summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2011-12-08 21:48:06 -0800
committerH. Peter Anvin <hpa@zytor.com>2011-12-08 21:48:06 -0800
commitc6d2c36b1a2b1986cab7aebb72d120fa118bdfeb (patch)
tree200ba3e1488a9eb276562b6a57c894d5d841d0c3
parentbadf05140d3c2408715a73a52c0f35887e337c04 (diff)
downloadtftp-hpa-c6d2c36b1a2b1986cab7aebb72d120fa118bdfeb.tar.gz
tftpd: the "is this address local" algorithm no longer works on Linux
Linux no longer tries to match the local address with the remote one, so address_is_local() fails. Try instead to simply see if we can bind to the explicit address. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--tftpd/recvfrom.c39
1 files changed, 23 insertions, 16 deletions
diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c
index 3ee5642..24abce3 100644
--- a/tftpd/recvfrom.c
+++ b/tftpd/recvfrom.c
@@ -51,52 +51,59 @@ struct in_pktinfo {
#endif
/*
- * Check to see if this is a valid local address. If so, we should
- * end up having the same local and remote address when trying to
- * bind to it.
+ * Check to see if this is a valid local address, meaning that we can
+ * legally bind to it.
*/
static int address_is_local(const union sock_addr *addr)
{
- union sock_addr sa;
+ union sock_addr sa1, sa2;
int sockfd = -1;
int e;
int rv = 0;
socklen_t addrlen;
+ memcpy(&sa1, addr, sizeof sa1);
+
/* Multicast or universal broadcast address? */
- if (addr->sa.sa_family == AF_INET) {
- if (ntohl(addr->si.sin_addr.s_addr) >= (224UL << 24))
+ if (sa1.sa.sa_family == AF_INET) {
+ if (ntohl(sa1.si.sin_addr.s_addr) >= (224UL << 24))
return 0;
+ sa1.si.sin_port = 0; /* Any port */
}
#ifdef HAVE_IPV6
- else if (addr->sa.sa_family == AF_INET6) {
- if (IN6_IS_ADDR_MULTICAST(&addr->s6.sin6_addr))
+ else if (sa1.sa.sa_family == AF_INET6) {
+ if (IN6_IS_ADDR_MULTICAST(&sa1.s6.sin6_addr))
return 0;
+ sa1.s6.sin6_port = 0; /* Any port */
}
#endif
else
return 0;
- sockfd = socket(addr->sa.sa_family, SOCK_DGRAM, 0);
+ sockfd = socket(sa1.sa.sa_family, SOCK_DGRAM, 0);
if (sockfd < 0)
goto err;
- if (connect(sockfd, &addr->sa, SOCKLEN(addr)))
+ if (bind(sockfd, &sa1.sa, SOCKLEN(&sa1)))
goto err;
addrlen = SOCKLEN(addr);
- if (getsockname(sockfd, (struct sockaddr *)&sa, &addrlen))
+ if (getsockname(sockfd, (struct sockaddr *)&sa2, &addrlen))
goto err;
- if (addr->sa.sa_family == AF_INET)
- rv = sa.si.sin_addr.s_addr == addr->si.sin_addr.s_addr;
+ if (sa1.sa.sa_family != sa2.sa.sa_family)
+ goto err;
+
+ if (sa2.sa.sa_family == AF_INET)
+ rv = sa1.si.sin_addr.s_addr == sa2.si.sin_addr.s_addr;
#ifdef HAVE_IPV6
- else if (addr->sa.sa_family == AF_INET6)
- rv = IN6_ARE_ADDR_EQUAL(&sa.s6.sin6_addr, &addr->s6.sin6_addr);
+ else if (sa2.sa.sa_family == AF_INET6)
+ rv = IN6_ARE_ADDR_EQUAL(&sa1.s6.sin6_addr, &sa2.s6.sin6_addr);
#endif
else
rv = 0;
- err:
+
+err:
e = errno;
if (sockfd >= 0)