aboutsummaryrefslogtreecommitdiff
path: root/libbb/atlas_check_addr.c
diff options
context:
space:
mode:
Diffstat (limited to 'libbb/atlas_check_addr.c')
-rw-r--r--libbb/atlas_check_addr.c102
1 files changed, 102 insertions, 0 deletions
diff --git a/libbb/atlas_check_addr.c b/libbb/atlas_check_addr.c
new file mode 100644
index 0000000..ea6724d
--- /dev/null
+++ b/libbb/atlas_check_addr.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2015 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+struct ipv4_prefix
+{
+ uint32_t addr;
+ unsigned len;
+}
+static bad_ipv4[] =
+{
+ { 0x7F000000, 8 }, /* 127.0.0.0/8 localhost */
+ { 0x0A000000, 8 }, /* 10.0.0.0/8 (RFC-1918) */
+ { 0xAC100000, 12 }, /* 172.16.0.0/12 (RFC-1918) */
+ { 0xC0A80000, 16 }, /* 192.168.0.0/16 (RFC-1918) */
+ { 0xA9FE0000, 16 }, /* 169.254.0.0/16 (RFC-3927) */
+ { 0xE0000000, 3 }, /* 224.0.0.0/3 multicast and reserved */
+};
+
+struct ipv6_prefix
+{
+ uint16_t addr[8];
+ unsigned len;
+}
+static bad_ipv6[] =
+{
+ { { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001 },
+ 128 }, /* ::1 loopback */
+ { { 0xE000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 },
+ 3 }, /* e000::/3 ULA, link local, multicast */
+};
+
+int atlas_check_addr(const struct sockaddr *sa, socklen_t len)
+{
+ uint16_t addr2, mask2;
+ int i, j, prefix_len;
+ uint32_t addr4, mask4;
+ uint16_t *addr2p;
+ const struct sockaddr_in *sin4p;
+ const struct sockaddr_in6 *sin6p;
+
+ switch(sa->sa_family)
+ {
+ case AF_INET:
+ if (len < sizeof(*sin4p))
+ return -1;
+ sin4p= (const struct sockaddr_in *)sa;
+ addr4= sin4p->sin_addr.s_addr;
+ addr4= ntohl(addr4);
+ for (i= 0; i<sizeof(bad_ipv4)/sizeof(bad_ipv4[0]); i++)
+ {
+ mask4= ~((1ul << (32-bad_ipv4[i].len))-1);
+ if ((addr4 & mask4) == bad_ipv4[i].addr)
+ return -1;
+ }
+ return 0;
+
+ case AF_INET6:
+ if (len < sizeof(*sin6p))
+ return -1;
+ sin6p= (const struct sockaddr_in6 *)sa;
+ addr2p= (uint16_t *)&sin6p->sin6_addr;
+ for (i= 0; i<sizeof(bad_ipv6)/sizeof(bad_ipv6[0]); i++)
+ {
+ prefix_len= bad_ipv6[i].len;
+ for (j= 0; j<prefix_len; j += 16)
+ {
+ addr2= ntohs(addr2p[j/16]);
+ if (j+16 <= prefix_len)
+ {
+ /* Match entire word */
+ if (addr2 != bad_ipv6[i].addr[j/16])
+ {
+ /* Different prefix */
+ break;
+ }
+ continue;
+ }
+ mask2= ~((1ul << (16-(prefix_len % 16)))-1);
+ if ((addr2 & mask2) == bad_ipv6[i].addr[j/16])
+ {
+ return -1;
+ }
+ break;
+ }
+ if (j < prefix_len)
+ {
+ /* No match, try the next one */
+ continue;
+ }
+
+ /* Match */
+ return -1;
+ }
+ return 0;
+ }
+ return -1; /* Default to not allowed */
+}
+