chiark / gitweb /
Remove libidn checks/support
[elogind.git] / src / shared / in-addr-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <arpa/inet.h>
23
24 #include "in-addr-util.h"
25
26 int in_addr_is_null(int family, const union in_addr_union *u) {
27         assert(u);
28
29         if (family == AF_INET)
30                 return u->in.s_addr == 0;
31
32         if (family == AF_INET6)
33                 return
34                         u->in6.s6_addr32[0] == 0 &&
35                         u->in6.s6_addr32[1] == 0 &&
36                         u->in6.s6_addr32[2] == 0 &&
37                         u->in6.s6_addr32[3] == 0;
38
39         return -EAFNOSUPPORT;
40 }
41
42 int in_addr_is_link_local(int family, const union in_addr_union *u) {
43         assert(u);
44
45         if (family == AF_INET)
46                 return (be32toh(u->in.s_addr) & 0xFFFF0000) == (169U << 24 | 254U << 16);
47
48         if (family == AF_INET6)
49                 return IN6_IS_ADDR_LINKLOCAL(&u->in6);
50
51         return -EAFNOSUPPORT;
52 }
53
54 int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
55         assert(a);
56         assert(b);
57
58         if (family == AF_INET)
59                 return a->in.s_addr == b->in.s_addr;
60
61         if (family == AF_INET6)
62                 return
63                         a->in6.s6_addr32[0] == b->in6.s6_addr32[0] &&
64                         a->in6.s6_addr32[1] == b->in6.s6_addr32[1] &&
65                         a->in6.s6_addr32[2] == b->in6.s6_addr32[2] &&
66                         a->in6.s6_addr32[3] == b->in6.s6_addr32[3];
67
68         return -EAFNOSUPPORT;
69 }
70
71 int in_addr_prefix_intersect(
72                 int family,
73                 const union in_addr_union *a,
74                 unsigned aprefixlen,
75                 const union in_addr_union *b,
76                 unsigned bprefixlen) {
77
78         unsigned m;
79
80         assert(a);
81         assert(b);
82
83         /* Checks whether there are any addresses that are in both
84          * networks */
85
86         m = MIN(aprefixlen, bprefixlen);
87
88         if (family == AF_INET) {
89                 uint32_t x, nm;
90
91                 x = be32toh(a->in.s_addr ^ b->in.s_addr);
92                 nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m);
93
94                 return (x & nm) == 0;
95         }
96
97         if (family == AF_INET6) {
98                 unsigned i;
99
100                 if (m > 128)
101                         m = 128;
102
103                 for (i = 0; i < 16; i++) {
104                         uint8_t x, nm;
105
106                         x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i];
107
108                         if (m < 8)
109                                 nm = 0xFF << (8 - m);
110                         else
111                                 nm = 0xFF;
112
113                         if ((x & nm) != 0)
114                                 return 0;
115
116                         if (m > 8)
117                                 m -= 8;
118                         else
119                                 m = 0;
120                 }
121
122                 return 1;
123         }
124
125         return -EAFNOSUPPORT;
126 }
127
128 int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) {
129         assert(u);
130
131         /* Increases the network part of an address by one. Returns
132          * positive it that succeeds, or 0 if this overflows. */
133
134         if (prefixlen <= 0)
135                 return 0;
136
137         if (family == AF_INET) {
138                 uint32_t c, n;
139
140                 if (prefixlen > 32)
141                         prefixlen = 32;
142
143                 c = be32toh(u->in.s_addr);
144                 n = c + (1UL << (32 - prefixlen));
145                 if (n < c)
146                         return 0;
147                 n &= 0xFFFFFFFFUL << (32 - prefixlen);
148
149                 u->in.s_addr = htobe32(n);
150                 return 1;
151         }
152
153         if (family == AF_INET6) {
154                 struct in6_addr add = {}, result;
155                 uint8_t overflow = 0;
156                 unsigned i;
157
158                 if (prefixlen > 128)
159                         prefixlen = 128;
160
161                 /* First calculate what we have to add */
162                 add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8);
163
164                 for (i = 16; i > 0; i--) {
165                         unsigned j = i - 1;
166
167                         result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow;
168                         overflow = (result.s6_addr[j] < u->in6.s6_addr[j]);
169                 }
170
171                 if (overflow)
172                         return 0;
173
174                 u->in6 = result;
175                 return 1;
176         }
177
178         return -EAFNOSUPPORT;
179 }
180
181 int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
182         char *x;
183         size_t l;
184
185         assert(u);
186         assert(ret);
187
188         if (family == AF_INET)
189                 l = INET_ADDRSTRLEN;
190         else if (family == AF_INET6)
191                 l = INET6_ADDRSTRLEN;
192         else
193                 return -EAFNOSUPPORT;
194
195         x = new(char, l);
196         if (!x)
197                 return -ENOMEM;
198
199         errno = 0;
200         if (!inet_ntop(family, u, x, l)) {
201                 free(x);
202                 return errno ? -errno : -EINVAL;
203         }
204
205         *ret = x;
206         return 0;
207 }
208
209 int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
210
211         assert(s);
212         assert(ret);
213
214         if (!IN_SET(family, AF_INET, AF_INET6))
215                 return -EAFNOSUPPORT;
216
217         errno = 0;
218         if (inet_pton(family, s, ret) <= 0)
219                 return errno ? -errno : -EINVAL;
220
221         return 0;
222 }
223
224 int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret) {
225         int r;
226
227         assert(s);
228         assert(family);
229         assert(ret);
230
231         r = in_addr_from_string(AF_INET, s, ret);
232         if (r >= 0) {
233                 *family = AF_INET;
234                 return 0;
235         }
236
237         r = in_addr_from_string(AF_INET6, s, ret);
238         if (r >= 0) {
239                 *family = AF_INET6;
240                 return 0;
241         }
242
243         return -EINVAL;
244 }
245
246 unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr) {
247         assert(addr);
248
249         return 32 - u32ctz(be32toh(addr->s_addr));
250 }
251
252 struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) {
253         assert(addr);
254         assert(prefixlen <= 32);
255
256         /* Shifting beyond 32 is not defined, handle this specially. */
257         if (prefixlen == 0)
258                 addr->s_addr = 0;
259         else
260                 addr->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff);
261
262         return addr;
263 }
264
265 int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) {
266         uint8_t msb_octet = *(uint8_t*) addr;
267
268         /* addr may not be aligned, so make sure we only access it byte-wise */
269
270         assert(addr);
271         assert(prefixlen);
272
273         if (msb_octet < 128)
274                 /* class A, leading bits: 0 */
275                 *prefixlen = 8;
276         else if (msb_octet < 192)
277                 /* class B, leading bits 10 */
278                 *prefixlen = 16;
279         else if (msb_octet < 224)
280                 /* class C, leading bits 110 */
281                 *prefixlen = 24;
282         else
283                 /* class D or E, no default prefixlen */
284                 return -ERANGE;
285
286         return 0;
287 }
288
289 int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) {
290         unsigned char prefixlen;
291         int r;
292
293         assert(addr);
294         assert(mask);
295
296         r = in_addr_default_prefixlen(addr, &prefixlen);
297         if (r < 0)
298                 return r;
299
300         in_addr_prefixlen_to_netmask(mask, prefixlen);
301         return 0;
302 }
303
304 int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) {
305         assert(addr);
306
307         if (family == AF_INET) {
308                 struct in_addr mask;
309
310                 if (!in_addr_prefixlen_to_netmask(&mask, prefixlen))
311                         return -EINVAL;
312
313                 addr->in.s_addr &= mask.s_addr;
314                 return 0;
315         }
316
317         if (family == AF_INET6) {
318                 unsigned i;
319
320                 for (i = 0; i < 16; i++) {
321                         uint8_t mask;
322
323                         if (prefixlen >= 8) {
324                                 mask = 0xFF;
325                                 prefixlen -= 8;
326                         } else {
327                                 mask = 0xFF << (8 - prefixlen);
328                                 prefixlen = 0;
329                         }
330
331                         addr->in6.s6_addr[i] &= mask;
332                 }
333
334                 return 0;
335         }
336
337         return -EAFNOSUPPORT;
338 }