summaryrefslogtreecommitdiff
path: root/src/sp_network_utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sp_network_utils.c')
-rw-r--r--src/sp_network_utils.c159
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
7ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
8
9static inline bool cidr4_match(const struct in_addr addr,
10 const struct in_addr net, uint8_t bits);
11static inline bool cidr6_match(const struct in6_addr address,
12 const struct in6_addr network, uint8_t bits);
13static inline int get_ip_version(const char *ip);
14
15void 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 */
47static 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
55static 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
83static 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
101bool 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
125int 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}