]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | |
7c673cae | 2 | #include <arpa/inet.h> |
31f18b77 | 3 | #include <ifaddrs.h> |
7c673cae FG |
4 | #include <stdlib.h> |
5 | #include <string.h> | |
31f18b77 FG |
6 | #if defined(__FreeBSD__) |
7 | #include <sys/types.h> | |
8 | #include <sys/socket.h> | |
9 | #include <netinet/in.h> | |
10 | #endif | |
7c673cae | 11 | |
31f18b77 | 12 | #include "include/ipaddr.h" |
7c673cae FG |
13 | |
14 | static void netmask_ipv4(const struct in_addr *addr, | |
15 | unsigned int prefix_len, | |
16 | struct in_addr *out) { | |
17 | uint32_t mask; | |
18 | ||
19 | if (prefix_len >= 32) { | |
20 | // also handle 32 in this branch, because >>32 is not defined by | |
21 | // the C standards | |
22 | mask = ~uint32_t(0); | |
23 | } else { | |
24 | mask = htonl(~(~uint32_t(0) >> prefix_len)); | |
25 | } | |
26 | out->s_addr = addr->s_addr & mask; | |
27 | } | |
28 | ||
29 | ||
30 | const struct sockaddr *find_ipv4_in_subnet(const struct ifaddrs *addrs, | |
31 | const struct sockaddr_in *net, | |
32 | unsigned int prefix_len) { | |
33 | struct in_addr want, temp; | |
34 | ||
35 | netmask_ipv4(&net->sin_addr, prefix_len, &want); | |
36 | ||
37 | for (; addrs != NULL; addrs = addrs->ifa_next) { | |
38 | ||
39 | if (addrs->ifa_addr == NULL) | |
40 | continue; | |
41 | ||
42 | if (strcmp(addrs->ifa_name, "lo") == 0) | |
43 | continue; | |
44 | ||
45 | if (addrs->ifa_addr->sa_family != net->sin_family) | |
46 | continue; | |
47 | ||
48 | struct in_addr *cur = &((struct sockaddr_in*)addrs->ifa_addr)->sin_addr; | |
49 | netmask_ipv4(cur, prefix_len, &temp); | |
50 | ||
51 | if (temp.s_addr == want.s_addr) { | |
52 | return addrs->ifa_addr; | |
53 | } | |
54 | } | |
55 | ||
56 | return NULL; | |
57 | } | |
58 | ||
59 | ||
60 | static void netmask_ipv6(const struct in6_addr *addr, | |
61 | unsigned int prefix_len, | |
62 | struct in6_addr *out) { | |
63 | if (prefix_len > 128) | |
64 | prefix_len = 128; | |
65 | ||
66 | memcpy(out->s6_addr, addr->s6_addr, prefix_len/8); | |
67 | if (prefix_len < 128) | |
68 | out->s6_addr[prefix_len/8] = addr->s6_addr[prefix_len/8] & ~( 0xFF >> (prefix_len % 8) ); | |
69 | if (prefix_len/8 < 15) | |
70 | memset(out->s6_addr+prefix_len/8+1, 0, 16-prefix_len/8-1); | |
71 | } | |
72 | ||
73 | ||
74 | const struct sockaddr *find_ipv6_in_subnet(const struct ifaddrs *addrs, | |
75 | const struct sockaddr_in6 *net, | |
76 | unsigned int prefix_len) { | |
77 | struct in6_addr want, temp; | |
78 | ||
79 | netmask_ipv6(&net->sin6_addr, prefix_len, &want); | |
80 | ||
81 | for (; addrs != NULL; addrs = addrs->ifa_next) { | |
82 | ||
83 | if (addrs->ifa_addr == NULL) | |
84 | continue; | |
85 | ||
86 | if (strcmp(addrs->ifa_name, "lo") == 0) | |
87 | continue; | |
88 | ||
89 | if (addrs->ifa_addr->sa_family != net->sin6_family) | |
90 | continue; | |
91 | ||
92 | struct in6_addr *cur = &((struct sockaddr_in6*)addrs->ifa_addr)->sin6_addr; | |
93 | netmask_ipv6(cur, prefix_len, &temp); | |
94 | ||
95 | if (IN6_ARE_ADDR_EQUAL(&temp, &want)) | |
96 | return addrs->ifa_addr; | |
97 | } | |
98 | ||
99 | return NULL; | |
100 | } | |
101 | ||
102 | ||
103 | const struct sockaddr *find_ip_in_subnet(const struct ifaddrs *addrs, | |
104 | const struct sockaddr *net, | |
105 | unsigned int prefix_len) { | |
106 | switch (net->sa_family) { | |
107 | case AF_INET: | |
108 | return find_ipv4_in_subnet(addrs, (struct sockaddr_in*)net, prefix_len); | |
109 | ||
110 | case AF_INET6: | |
111 | return find_ipv6_in_subnet(addrs, (struct sockaddr_in6*)net, prefix_len); | |
112 | } | |
113 | ||
114 | return NULL; | |
115 | } | |
116 | ||
117 | ||
118 | bool parse_network(const char *s, struct sockaddr_storage *network, unsigned int *prefix_len) { | |
119 | char *slash = strchr((char*)s, '/'); | |
120 | if (!slash) { | |
121 | // no slash | |
122 | return false; | |
123 | } | |
124 | if (*(slash+1) == '\0') { | |
125 | // slash is the last character | |
126 | return false; | |
127 | } | |
128 | ||
129 | char *end; | |
130 | long int num = strtol(slash+1, &end, 10); | |
131 | if (*end != '\0') { | |
132 | // junk after the prefix_len | |
133 | return false; | |
134 | } | |
135 | if (num < 0) { | |
136 | return false; | |
137 | } | |
138 | *prefix_len = num; | |
139 | ||
140 | // copy the part before slash to get nil termination | |
141 | char *addr = (char*)alloca(slash-s + 1); | |
142 | strncpy(addr, s, slash-s); | |
143 | addr[slash-s] = '\0'; | |
144 | ||
145 | // caller expects ports etc to be zero | |
146 | memset(network, 0, sizeof(*network)); | |
147 | ||
148 | // try parsing as ipv4 | |
149 | int ok; | |
150 | ok = inet_pton(AF_INET, addr, &((struct sockaddr_in*)network)->sin_addr); | |
151 | if (ok) { | |
152 | network->ss_family = AF_INET; | |
153 | return true; | |
154 | } | |
155 | ||
156 | // try parsing as ipv6 | |
157 | ok = inet_pton(AF_INET6, addr, &((struct sockaddr_in6*)network)->sin6_addr); | |
158 | if (ok) { | |
159 | network->ss_family = AF_INET6; | |
160 | return true; | |
161 | } | |
162 | ||
163 | return false; | |
164 | } |