From: Mark Wooding Date: Wed, 23 Apr 2014 23:15:52 +0000 (+0100) Subject: noip.c, noip.1: Add IPv6 support. X-Git-Tag: 1.1.0~2 X-Git-Url: https://git.distorted.org.uk/~mdw/preload-hacks/commitdiff_plain/2a06ea0b5d53bb50a03818c3da598764009469dc noip.c, noip.1: Add IPv6 support. Just like that. Of course, the hard work was done earlier. --- diff --git a/noip.1 b/noip.1 index 2d1beef..dc7e0d3 100644 --- a/noip.1 +++ b/noip.1 @@ -219,9 +219,10 @@ Matches all addresses. Matches the address of one of the machine's network interfaces. .TP .I address -Matches just the given address. An +Matches just the given IPv4 or IPv6 address. An .I address -may be enclosed in square brackets. +may be enclosed in square brackets; IPv6 addresses must be so enclosed, +because colons are significant in the rest of the ACL syntax. .TP .IB address \- address Matches any address which falls in the given range. Addresses are diff --git a/noip.c b/noip.c index ec66340..9c5cd68 100644 --- a/noip.c +++ b/noip.c @@ -62,19 +62,21 @@ enum { UNUSED, STALE, USED }; /* Unix socket status values */ enum { WANT_FRESH, WANT_EXISTING }; /* Socket address dispositions */ enum { DENY, ALLOW }; /* ACL verdicts */ -static int address_families[] = { AF_INET, -1 }; +static int address_families[] = { AF_INET, AF_INET6, -1 }; #define ADDRBUFSZ 64 /* Address representations. */ typedef union ipaddr { struct in_addr v4; + struct in6_addr v6; } ipaddr; /* Convenient socket address hacking. */ typedef union address { struct sockaddr sa; struct sockaddr_in sin; + struct sockaddr_in6 sin6; } address; /* Access control list nodes */ @@ -91,7 +93,7 @@ typedef struct full_ipaddr { int af; ipaddr addr; } full_ipaddr; -#define MAX_LOCAL_IPADDRS 16 +#define MAX_LOCAL_IPADDRS 64 static full_ipaddr local_ipaddrs[MAX_LOCAL_IPADDRS]; static int n_local_ipaddrs; @@ -144,6 +146,7 @@ static void import(void) /* Socket address casts */ #define SA(sa) ((struct sockaddr *)(sa)) #define SIN(sa) ((struct sockaddr_in *)(sa)) +#define SIN6(sa) ((struct sockaddr_in6 *)(sa)) #define SUN(sa) ((struct sockaddr_un *)(sa)) /* Raw bytes */ @@ -202,6 +205,7 @@ static int family_known_p(int af) { switch (af) { case AF_INET: + case AF_INET6: return (1); default: return (0); @@ -213,6 +217,7 @@ static socklen_t family_socklen(int af) { switch (af) { case AF_INET: return (sizeof(struct sockaddr_in)); + case AF_INET6: return (sizeof(struct sockaddr_in6)); default: abort(); } } @@ -222,6 +227,7 @@ static int address_width(int af) { switch (af) { case AF_INET: return 32; + case AF_INET6: return 128; default: abort(); } } @@ -238,6 +244,23 @@ static int common_prefix_length(int af, const ipaddr *a, const ipaddr *b) if ((aa&m) == 0 && (bb&m) == m) return (32 - simple_mask_length(m)); else return (-1); } break; + case AF_INET6: { + const uint8_t *aa = a->v6.s6_addr, *bb = b->v6.s6_addr; + unsigned m; + unsigned n; + int i; + + for (i = 0; i < 16 && aa[i] == bb[i]; i++); + n = 8*i; + if (i < 16) { + m = aa[i]^bb[i]; + if ((aa[i]&m) != 0 || (bb[i]&m) != m) return (-1); + n += 8 - simple_mask_length(m); + for (i++; i < 16; i++) + if (aa[i] || bb[i] != 0xff) return (-1); + } + return (n); + } break; default: abort(); } @@ -248,6 +271,7 @@ static int port_from_sockaddr(const struct sockaddr *sa) { switch (sa->sa_family) { case AF_INET: return (ntohs(SIN(sa)->sin_port)); + case AF_INET6: return (ntohs(SIN6(sa)->sin6_port)); default: abort(); } } @@ -257,6 +281,7 @@ static void port_to_sockaddr(struct sockaddr *sa, int port) { switch (sa->sa_family) { case AF_INET: SIN(sa)->sin_port = htons(port); break; + case AF_INET6: SIN6(sa)->sin6_port = htons(port); break; default: abort(); } } @@ -265,6 +290,7 @@ static void ipaddr_from_sockaddr(ipaddr *a, const struct sockaddr *sa) { switch (sa->sa_family) { case AF_INET: a->v4 = SIN(sa)->sin_addr; break; + case AF_INET6: a->v6 = SIN6(sa)->sin6_addr; break; default: abort(); } } @@ -279,6 +305,7 @@ static int ipaddr_equal_p(int af, const ipaddr *a, const ipaddr *b) { switch (af) { case AF_INET: return (a->v4.s_addr == b->v4.s_addr); + case AF_INET6: return (memcmp(a->v6.s6_addr, b->v6.s6_addr, 16) == 0); default: abort(); } } @@ -295,6 +322,19 @@ static int sockaddr_in_range_p(const struct sockaddr *sa, return (ntohl(a->v4.s_addr) <= addr && addr <= ntohl(b->v4.s_addr)); } break; + case AF_INET6: { + const uint8_t *ss = SIN6(sa)->sin6_addr.s6_addr; + const uint8_t *aa = a->v6.s6_addr, *bb = b->v6.s6_addr; + int h = 1, l = 1; + int i; + + for (i = 0; h && l && i < 16; i++, ss++, aa++, bb++) { + if (*ss < *aa || *bb < *ss) return (0); + if (*aa < *ss) l = 0; + if (*ss < *bb) h = 0; + } + return (1); + } break; default: abort(); } @@ -311,6 +351,15 @@ static void wildcard_address(int af, struct sockaddr *sa) sin->sin_port = 0; sin->sin_addr.s_addr = INADDR_ANY; } break; + case AF_INET6: { + struct sockaddr_in6 *sin6 = SIN6(sa); + memset(sin6, 0, sizeof(sin6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = 0; + sin6->sin6_addr = in6addr_any; + sin6->sin6_scope_id = 0; + sin6->sin6_flowinfo = 0; + } break; default: abort(); } @@ -329,6 +378,16 @@ static void mask_address(int af, ipaddr *a, int plen, int highp) if (highp) addr |= ~mask; a->v4.s_addr = htonl(addr & 0xffffffff); } break; + case AF_INET6: { + int i = plen/8; + unsigned m = (0xff << (8 - plen%8)) & 0xff; + unsigned s = highp ? 0xff : 0; + if (m) { + a->v6.s6_addr[i] = (a->v6.s6_addr[i] & m) | (s & ~m); + i++; + } + for (; i < 16; i++) a->v6.s6_addr[i] = s; + } break; default: abort(); } @@ -384,7 +443,7 @@ static char *present_sockaddr(const struct sockaddr *sa, socklen_t len, } WANT(1); PUTC(0); } break; - case AF_INET: { + case AF_INET: case AF_INET6: { char addrbuf[NI_MAXHOST], portbuf[NI_MAXSERV]; int err = getnameinfo(sa, len, addrbuf, sizeof(addrbuf), @@ -407,7 +466,7 @@ nospace: /* Guess the family of a textual socket address. */ static int guess_address_family(const char *p) - { return (AF_INET); } + { return (strchr(p, ':') ? AF_INET6 : AF_INET); } /* Parse a socket address P and write the result to SA. */ static int parse_sockaddr(struct sockaddr *sa, const char *p)