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 */
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;
/* 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 */
{
switch (af) {
case AF_INET:
+ case AF_INET6:
return (1);
default:
return (0);
{
switch (af) {
case AF_INET: return (sizeof(struct sockaddr_in));
+ case AF_INET6: return (sizeof(struct sockaddr_in6));
default: abort();
}
}
{
switch (af) {
case AF_INET: return 32;
+ case AF_INET6: return 128;
default: abort();
}
}
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();
}
{
switch (sa->sa_family) {
case AF_INET: return (ntohs(SIN(sa)->sin_port));
+ case AF_INET6: return (ntohs(SIN6(sa)->sin6_port));
default: abort();
}
}
{
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();
}
}
+
/* Extract the address part from SA and store it in A. */
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();
}
}
{
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();
}
}
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();
}
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();
}
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();
}
}
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),
/* 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)
#ifdef DEBUG
/* Write to standard error a description of the ACL node A. */
-static void dump_aclnode(aclnode *a)
+static void dump_aclnode(const aclnode *a)
{
char buf[ADDRBUFSZ];
const char *p;
fputc('\n', stderr);
}
-static void dump_acl(aclnode *a)
+static void dump_acl(const aclnode *a)
{
int act = ALLOW;
#endif
/* Returns nonzero if the ACL A allows the socket address SA. */
-static int acl_allows_p(aclnode *a, const struct sockaddr *sa)
+static int acl_allows_p(const aclnode *a, const struct sockaddr *sa)
{
unsigned short port = port_from_sockaddr(sa);
int act = ALLOW;
}
/* Decode the Unix address SUN to an Internet address SIN. If AF_HINT is
- * nonzero, an empty address (indicative of an unbound Unix-domain socket) of
- * the is translated to a wildcard Internet address of the appropriate
- * family. Returns zero on success; -1 on failure (e.g., it wasn't one of
- * our addresses).
+ * nonzero, an empty address (indicative of an unbound Unix-domain socket) is
+ * translated to a wildcard Internet address of the appropriate family.
+ * Returns zero on success; -1 on failure (e.g., it wasn't one of our
+ * addresses).
*/
static int decode_inet_addr(struct sockaddr *sa, int af_hint,
const struct sockaddr_un *sun,
}
SKIPSPC;
if (*p != ',') break;
- p++;
+ if (*p) p++;
}
return;
{
struct stat st;
- if (stat(sockdir, &st)) {
+ if (lstat(sockdir, &st)) {
if (errno == ENOENT) {
if (mkdir(sockdir, 0700)) {
perror("noip: creating socketdir");
exit(127);
}
- if (!stat(sockdir, &st))
+ if (!lstat(sockdir, &st))
goto check;
}
perror("noip: checking socketdir");