aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarsten Keil <kkeil@suse.de>2008-07-30 16:15:10 +0200
committerH. Peter Anvin <hpa@zytor.com>2008-07-30 15:20:31 -0700
commitcfb85d4dec59f433ef9efee0d18b188bb2cb44fa (patch)
tree1b02e98dd419851fa3a5c19224b47924cc9246f2
parentc3a5c712e2999a5610479fda84a58847bb5543b2 (diff)
downloadtftp-hpa-cfb85d4dec59f433ef9efee0d18b188bb2cb44fa.tar.gz
Fix numeric IPv6 address handling
This patch fix a issue with numeric IPv6 addresses in the tftpd -a address[:port] option. Since IPv6 addresses use colon ':' in differnt counts itself, we cannot detect, if the last colon is a seperator, so it is needed to put the IPv6 address into square brackets, e.g. [2001:db8::1], so a optional port assignment is unambiguous. The patch also allows to specify numeric IPv6 addresses in other places enclosed in [], but in these cases it accept these also without []. Signed-off-by: Karsten Keil <kkeil@suse.de> Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--common/tftpsubs.c56
-rw-r--r--common/tftpsubs.h12
-rw-r--r--tftpd/tftpd.8.in4
-rw-r--r--tftpd/tftpd.c36
4 files changed, 102 insertions, 6 deletions
diff --git a/common/tftpsubs.c b/common/tftpsubs.c
index 45f4907..1c91e89 100644
--- a/common/tftpsubs.c
+++ b/common/tftpsubs.c
@@ -309,7 +309,7 @@ set_sock_addr(char *host,union sock_addr *s, char **name)
memset(&hints, 0, sizeof(hints));
hints.ai_family = s->sa.sa_family;
hints.ai_flags = AI_CANONNAME;
- err = getaddrinfo(host, NULL, &hints, &addrResult);
+ err = getaddrinfo(strip_address(host), NULL, &hints, &addrResult);
if (err) {
printf("Error : %s\n", gai_strerror(err));
printf("%s: unknown host\n", host);
@@ -329,3 +329,57 @@ set_sock_addr(char *host,union sock_addr *s, char **name)
freeaddrinfo(addrResult);
return 0;
}
+
+#ifdef HAVE_IPV6
+int is_numeric_ipv6(char *addr)
+{
+ /* A numeric IPv6 address consist at least of 2 ':' and
+ * it may have sequences of hex-digits and maybe contain
+ * a '.' from a IPv4 mapped address and maybe is enclosed in []
+ * we do not check here, if it is a valid IPv6 address
+ * only if is something like a numeric IPv6 address or something else
+ */
+ size_t l;
+ char *p, s = 0;
+
+ if (!addr)
+ return 0;
+ p = strrchr(addr, ']');
+ if (p) {
+ s = *p;
+ *p = 0;
+ }
+ l = strlen(addr);
+ if (p)
+ *p = s;
+ if (l<2)
+ return 0;
+ if (l != strspn(addr, "0123456789ABCDEFabcdef:.["))
+ return 0;
+ p = strchr(addr, ':');
+ if (p) {
+ p++;
+ p = strchr(addr, ':');
+ if (p)
+ return 1;
+ }
+ return 0;
+}
+
+/* strip [] from numeric IPv6 addreses */
+
+char *strip_address(char *addr)
+{
+ char *p;
+
+ if (is_numeric_ipv6(addr) && (*addr == '[')) {
+ p = addr + strlen(addr);
+ p--;
+ if (*p == ']') {
+ *p = 0;
+ addr++;
+ }
+ }
+ return addr;
+}
+#endif
diff --git a/common/tftpsubs.h b/common/tftpsubs.h
index 20cf47e..887a4ab 100644
--- a/common/tftpsubs.h
+++ b/common/tftpsubs.h
@@ -73,6 +73,18 @@ union sock_addr {
(void *)&((union sock_addr*)sock)->si.sin_addr
#endif
+#ifdef HAVE_IPV6
+int is_numeric_ipv6(char *);
+char *strip_address(char *);
+#else
+#define is_numeric_ipv6(a) 0
+
+static inline char *strip_address(char *addr)
+{
+ return addr;
+}
+#endif
+
static inline int sa_set_port(union sock_addr *s, u_short port)
{
switch (s->sa.sa_family) {
diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in
index 01007ea..049b0a6 100644
--- a/tftpd/tftpd.8.in
+++ b/tftpd/tftpd.8.in
@@ -83,6 +83,10 @@ option. The default is to listen to the
port specified in
.I /etc/services
on all local addresses.
+
+.B Please note:
+Numeric IPv6 adresses must be enclosed in square brackets
+to avoid ambiguity with the optional port information.
.TP
.B \-c
Allow new files to be created. By default,
diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c
index 8321771..e663239 100644
--- a/tftpd/tftpd.c
+++ b/tftpd/tftpd.c
@@ -256,6 +256,28 @@ static int recv_time(int s, void *rbuf, int len, unsigned int flags,
}
}
+static int split_port(char **ap, char **pp)
+{
+ char *a, *p;
+
+ a = *ap;
+ if (is_numeric_ipv6(a)) {
+ if (*a++ != '[')
+ return 1;
+ *ap = a;
+ p = strrchr(a, ']');
+ if (!p)
+ return 1;
+ *p++ = 0;
+ a = p;
+ }
+ p = strrchr(a, ':');
+ if (p)
+ *p++ = 0;
+ *pp = p;
+ return 0;
+}
+
int main(int argc, char **argv)
{
struct tftphdr *tp;
@@ -482,16 +504,19 @@ int main(int argc, char **argv)
}
#endif
if (address) {
- char *portptr, *eportptr;
+ char *portptr = NULL, *eportptr;
int err;
struct servent *servent;
unsigned long port;
address = tfstrdup(address);
- portptr = strrchr(address, ':');
- if (portptr)
- *portptr++ = '\0';
- else
+ err = split_port(&address, &portptr);
+ if (err) {
+ syslog(LOG_ERR,
+ "Numeric IPv6 addresses need to be enclosed in []");
+ exit(EX_USAGE);
+ }
+ if (!portptr)
portptr = (char *)"tftp";
if (*address) {
if (fd4 >= 0) {
@@ -507,6 +532,7 @@ int main(int argc, char **argv)
}
#ifdef HAVE_IPV6
if (fd6 >= 0) {
+ bindaddr6.sin6_family = AF_INET6;
err = set_sock_addr(address,
(union sock_addr *)&bindaddr6, NULL);
if (err) {