diff options
| author | Sebastien Blot | 2017-09-20 10:11:01 +0200 |
|---|---|---|
| committer | Sebastien Blot | 2017-09-20 10:11:01 +0200 |
| commit | 868f96c759b6650d88ff9f4fbc5c048302134248 (patch) | |
| tree | c0de0af318bf77a8959164ef11aeeeb2b7bab294 /src/sp_network_utils.c | |
Initial import
Diffstat (limited to 'src/sp_network_utils.c')
| -rw-r--r-- | src/sp_network_utils.c | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/src/sp_network_utils.c b/src/sp_network_utils.c new file mode 100644 index 0000000..28bc324 --- /dev/null +++ b/src/sp_network_utils.c | |||
| @@ -0,0 +1,159 @@ | |||
| 1 | #include <arpa/inet.h> | ||
| 2 | #include <netinet/in.h> | ||
| 3 | #include <sys/socket.h> | ||
| 4 | |||
| 5 | #include "php_snuffleupagus.h" | ||
| 6 | |||
| 7 | ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) | ||
| 8 | |||
| 9 | static inline bool cidr4_match(const struct in_addr addr, | ||
| 10 | const struct in_addr net, uint8_t bits); | ||
| 11 | static inline bool cidr6_match(const struct in6_addr address, | ||
| 12 | const struct in6_addr network, uint8_t bits); | ||
| 13 | static inline int get_ip_version(const char *ip); | ||
| 14 | |||
| 15 | void apply_mask_on_ip(char *out, const char *const remote_addr) { | ||
| 16 | uint8_t mask4 = SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv4; | ||
| 17 | uint8_t mask6 = SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv6; | ||
| 18 | const int ip_version = get_ip_version(remote_addr); | ||
| 19 | |||
| 20 | memset(out, 0, 128); | ||
| 21 | |||
| 22 | if (ip_version == AF_INET) { | ||
| 23 | struct in_addr out4; | ||
| 24 | inet_pton(AF_INET, remote_addr, &out4); | ||
| 25 | const long n = out4.s_addr & htonl(0xFFFFFFFFu << (32 - mask4)); | ||
| 26 | out[0] = (n >> 24) & 0xFF; | ||
| 27 | out[1] = (n >> 16) & 0xFF; | ||
| 28 | out[2] = (n >> 8) & 0xFF; | ||
| 29 | out[3] = (n >> 0) & 0xFF; | ||
| 30 | } else if (ip_version == AF_INET6) { | ||
| 31 | inet_pton(AF_INET6, remote_addr, out); | ||
| 32 | uint32_t *p_ip = (uint32_t *)out; | ||
| 33 | while (32 < mask6) { | ||
| 34 | *p_ip = 0xFFFFFFFFu; | ||
| 35 | p_ip++; | ||
| 36 | mask6 -= 32; | ||
| 37 | } | ||
| 38 | if (0 != mask6) { | ||
| 39 | *p_ip = htonl(0xFFFFFFFFu << (32 - mask6)); | ||
| 40 | } | ||
| 41 | } else { | ||
| 42 | sp_log_err("ip_mask", "It seems that %s isn't a valid ip.", remote_addr); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | /* http://fxr.watson.org/fxr/source/include/net/xfrm.h?v=linux-2.6#L840 */ | ||
| 47 | static inline bool cidr4_match(const struct in_addr addr, | ||
| 48 | const struct in_addr net, uint8_t bits) { | ||
| 49 | if (bits == 0) { // C99 6.5.7 (3): u32 << 32 is undefined behaviour | ||
| 50 | return true; | ||
| 51 | } | ||
| 52 | return !((addr.s_addr ^ net.s_addr) & htonl(0xFFFFFFFFu << (32 - bits))); | ||
| 53 | } | ||
| 54 | |||
| 55 | static inline bool cidr6_match(const struct in6_addr address, | ||
| 56 | const struct in6_addr network, uint8_t bits) { | ||
| 57 | //#ifdef LINUX | ||
| 58 | const uint32_t *a = address.s6_addr32; | ||
| 59 | const uint32_t *n = network.s6_addr32; | ||
| 60 | /* | ||
| 61 | #else | ||
| 62 | const uint32_t *a = address.__u6_addr.__u6_addr32; | ||
| 63 | const uint32_t *n = network.__u6_addr.__u6_addr32; | ||
| 64 | #endif | ||
| 65 | */ | ||
| 66 | int bits_whole, bits_incomplete; | ||
| 67 | bits_whole = bits >> 5; // number of whole u32 | ||
| 68 | bits_incomplete = bits & 0x1F; // number of bits in incomplete u32 | ||
| 69 | if (bits_whole) { | ||
| 70 | if (memcmp(a, n, bits_whole << 2)) { | ||
| 71 | return false; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | if (bits_incomplete) { | ||
| 75 | uint32_t mask = htonl((0xFFFFFFFFu) << (32 - bits_incomplete)); | ||
| 76 | if ((a[bits_whole] ^ n[bits_whole]) & mask) { | ||
| 77 | return false; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | return true; | ||
| 81 | } | ||
| 82 | |||
| 83 | static inline int get_ip_version(const char *ip) { | ||
| 84 | struct in_addr out4; | ||
| 85 | struct in6_addr out6; | ||
| 86 | int res = inet_pton(AF_INET, ip, &out4); | ||
| 87 | if (0 == res) { | ||
| 88 | if (1 == inet_pton(AF_INET6, ip, &out6)) { | ||
| 89 | return AF_INET6; | ||
| 90 | } else { | ||
| 91 | return -1; | ||
| 92 | } | ||
| 93 | } else if (1 == res) { | ||
| 94 | return AF_INET; | ||
| 95 | } else { | ||
| 96 | return -1; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | // TODO factorise a bit this function | ||
| 101 | bool cidr_match(const char *ip, const sp_cidr *cidr) { | ||
| 102 | struct in_addr out4; | ||
| 103 | struct in6_addr out6; | ||
| 104 | |||
| 105 | switch (get_ip_version(ip)) { | ||
| 106 | case AF_INET: | ||
| 107 | if (AF_INET != cidr->ip_version) { | ||
| 108 | return false; | ||
| 109 | } | ||
| 110 | inet_pton(AF_INET, ip, &out4); | ||
| 111 | return cidr4_match(out4, cidr->ip.ipv4, cidr->mask); | ||
| 112 | case AF_INET6: | ||
| 113 | if (AF_INET6 != cidr->ip_version) { | ||
| 114 | return false; | ||
| 115 | } | ||
| 116 | inet_pton(AF_INET6, ip, &out6); | ||
| 117 | return cidr6_match(out6, cidr->ip.ipv6, cidr->mask); | ||
| 118 | default: | ||
| 119 | sp_log_err("cidr_match", "Weird ip (%s) family", ip); | ||
| 120 | break; | ||
| 121 | } | ||
| 122 | return false; | ||
| 123 | } | ||
| 124 | |||
| 125 | int get_ip_and_cidr(char *ip, sp_cidr *cidr) { | ||
| 126 | errno = 0; | ||
| 127 | char *mask = strchr(ip, '/'); | ||
| 128 | |||
| 129 | if (NULL == mask) { | ||
| 130 | sp_log_err("config", | ||
| 131 | "'%s' isn't a valid network mask, it seems that you forgot a '/'.", | ||
| 132 | ip); | ||
| 133 | return -1; | ||
| 134 | } | ||
| 135 | |||
| 136 | if (sscanf(mask + 1, "%hhu", &(cidr->mask)) != 1) { | ||
| 137 | sp_log_err("config", "'%s' isn't a valid network mask.", mask + 1); | ||
| 138 | return -1; | ||
| 139 | } | ||
| 140 | |||
| 141 | ip[mask - ip] = '\0'; // NULL the '/' char | ||
| 142 | |||
| 143 | cidr->ip_version = get_ip_version(ip); | ||
| 144 | |||
| 145 | if (AF_INET == cidr->ip_version) { | ||
| 146 | if (cidr->mask > 32) { | ||
| 147 | sp_log_err("config", "'%d' isn't a valid ipv4 mask.", cidr->mask); | ||
| 148 | return -1; | ||
| 149 | } | ||
| 150 | inet_pton(AF_INET, ip, &(cidr->ip.ipv4)); | ||
| 151 | } else if (AF_INET6 == cidr->ip_version) { | ||
| 152 | inet_pton(AF_INET6, ip, &(cidr->ip.ipv6)); | ||
| 153 | } else { | ||
| 154 | return -1; | ||
| 155 | } | ||
| 156 | |||
| 157 | ip[mask - ip] = '/'; | ||
| 158 | return 0; | ||
| 159 | } | ||
