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) {