1 | /* |
2 | * This Source Code Form is subject to the terms of the Mozilla Public |
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
5 | * |
6 | * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. |
7 | */ |
8 | |
9 | /* |
10 | * @f inet |
11 | * @a Fabian Groffen |
12 | * @v 1.0 |
13 | * @* The inet module |
14 | * The inet module contains a collection of functions that operate on IPv4 |
15 | * addresses. The most relevant functions are the `containment' functions |
16 | * that deal with subnet masks. The functionality of this module is |
17 | * greatly inspired by the PostgreSQL inet atom. |
18 | * |
19 | */ |
20 | #include "monetdb_config.h" |
21 | #include "gdk.h" |
22 | #include "mal.h" |
23 | #include "mal_exception.h" |
24 | |
25 | /* |
26 | * @* Implementation Code |
27 | * The first 4 bytes of the used lng are in use by the four quads of the |
28 | * IPv4 address, stored in network order. In the four bytes left, |
29 | * additional information is stored. |
30 | * Currently the fifth byte holds the number of bits from the IPv4 address |
31 | * that should match (ie. /8, /16, /24, /32) also known as subnet mask. |
32 | * The last byte holds whether inet atom represents the value nil or not. |
33 | * The value nil is represented as (per byte) 0000 0001. |
34 | * |
35 | */ |
36 | typedef struct _inet { |
37 | /* use a union to force alignment compatible with lng */ |
38 | union { |
39 | struct { |
40 | unsigned char _q1; |
41 | unsigned char _q2; |
42 | unsigned char _q3; |
43 | unsigned char _q4; |
44 | unsigned char _mask; |
45 | unsigned char _filler1; |
46 | unsigned char _filler2; |
47 | unsigned char _isnil; |
48 | } s; |
49 | lng alignment; |
50 | } u; |
51 | } inet; |
52 | #define q1 u.s._q1 |
53 | #define q2 u.s._q2 |
54 | #define q3 u.s._q3 |
55 | #define q4 u.s._q4 |
56 | #define mask u.s._mask |
57 | #define filler1 u.s._filler1 |
58 | #define filler2 u.s._filler2 |
59 | #define isnil u.s._isnil |
60 | |
61 | #ifdef WORDS_BIGENDIAN |
62 | /* HACK ALERT: once upon a time, lng_nil was used as inet_nil, but on |
63 | * big endian hardware, the byte that is not zero is on the other end; |
64 | * luckily, a mask of 0 is pretty useless, so we regard 128.0.0.0/0 |
65 | * also as nil */ |
66 | #define is_inet_nil(i) ((((i)->q1 == 0 && (i)->isnil != 0) || ((i)->q1 == 128 && (i)->isnil == 0 && (i)->filler1 == 0 && (i)->filler2 == 0)) && (i)->q2 == 0 && (i)->q3 == 0 && (i)->q4 == 0 && (i)->mask == 0) |
67 | #else |
68 | #define is_inet_nil(i) ((i)->q1 == 0 && (i)->q2 == 0 && (i)->q3 == 0 && (i)->q4 == 0 && (i)->mask == 0 && (i)->isnil != 0) |
69 | #endif |
70 | #define in_setnil(i) (i)->q1 = (i)->q2 = (i)->q3 = (i)->q4 = (i)->mask = (i)->filler1 = (i)->filler2 = 0; (i)->isnil = 1 |
71 | |
72 | mal_export ssize_t INETfromString(const char *src, size_t *len, inet **retval, bool external); |
73 | mal_export ssize_t INETtoString(str *retval, size_t *len, const inet *handle, bool external); |
74 | mal_export int INETcompare(const inet *l, const inet *r); |
75 | mal_export str INETnew(inet *retval, str *in); |
76 | mal_export str INET_isnil(bit *retval, const inet *val); |
77 | mal_export str INET_comp_EQ(bit *retval, const inet *val1, const inet *val2); |
78 | mal_export str INET_comp_NEQ(bit *retval, const inet *val1, const inet *val2); |
79 | mal_export str INET_comp_LT(bit *retval, const inet *val1, const inet *val2); |
80 | mal_export str INET_comp_GT(bit *retval, const inet *val1, const inet *val2); |
81 | mal_export str INET_comp_LE(bit *retval, const inet *val1, const inet *val2); |
82 | mal_export str INET_comp_GE(bit *retval, const inet *val1, const inet *val2); |
83 | mal_export str INET_comp_CW(bit *retval, const inet *val1, const inet *val2); |
84 | mal_export str INET_comp_CWE(bit *retval, const inet *val1, const inet *val2); |
85 | mal_export str INET_comp_CS(bit *retval, const inet *val1, const inet *val2); |
86 | mal_export str INET_comp_CSE(bit *retval, const inet *val1, const inet *val2); |
87 | mal_export str INETbroadcast(inet *retval, const inet *val); |
88 | mal_export str INEThost(str *retval, const inet *val); |
89 | mal_export str INETmasklen(int *retval, const inet *val); |
90 | mal_export str INETsetmasklen(inet *retval, const inet *val, const int *msk); |
91 | mal_export str INETnetmask(inet *retval, const inet *val); |
92 | mal_export str INEThostmask(inet *retval, const inet *val); |
93 | mal_export str INETnetwork(inet *retval, const inet *val); |
94 | mal_export str INETtext(str *retval, const inet *val); |
95 | mal_export str INETabbrev(str *retval, const inet *val); |
96 | mal_export str INET_inet(inet *d, const inet *s); |
97 | mal_export str INET_fromstr(inet *ret, str *s); |
98 | mal_export const inet *INETnull(void); |
99 | |
100 | static inet inet_nil = {{{0,0,0,0,0,0,0,1}}}; |
101 | |
102 | /** |
103 | * Creates a new inet from the given string. |
104 | * Warning: GDK function, does NOT pass a string by reference, and wants |
105 | * a pointer to a pointer for the retval! |
106 | * Returns the number of chars read |
107 | */ |
108 | ssize_t |
109 | INETfromString(const char *src, size_t *len, inet **retval, bool external) |
110 | { |
111 | int i, last, type; |
112 | long parse; /* type long returned by strtol() */ |
113 | char *endptr; |
114 | char sep = '.'; |
115 | |
116 | last = 0; |
117 | type = 0; |
118 | |
119 | if (*len < sizeof(inet) || *retval == NULL) { |
120 | GDKfree(*retval); |
121 | *retval = GDKzalloc(sizeof(inet)); |
122 | if( *retval == NULL){ |
123 | *len = 0; |
124 | return -1; |
125 | } |
126 | *len = sizeof(inet); |
127 | } else { |
128 | **retval = (inet) {.q1 = 0,}; |
129 | } |
130 | |
131 | /* handle the nil string */ |
132 | if (external && strcmp(src, "nil" ) == 0) { |
133 | in_setnil(*retval); |
134 | return 3; |
135 | } |
136 | if (GDK_STRNIL(src)) { |
137 | in_setnil(*retval); |
138 | return 1; |
139 | } |
140 | |
141 | /* use the DIY technique to guarantee maximum cross-platform |
142 | * portability */ |
143 | for (i = 0; src[i] != '\0'; i++) { |
144 | if (src[i] == '.' || src[i] == '/') { |
145 | sep = src[i]; |
146 | parse = strtol(src + last, &endptr, 10); |
147 | if (*endptr != sep || last >= i) { |
148 | GDKerror("Error while parsing, unexpected string '%s'" , endptr); |
149 | goto error; |
150 | } |
151 | if (parse > 255 || parse < 0) { |
152 | GDKerror("Illegal quad value: %ld" , parse); |
153 | goto error; |
154 | } |
155 | switch (type) { |
156 | case 0: |
157 | (*retval)->q1 = (unsigned char) parse; |
158 | break; |
159 | case 1: |
160 | (*retval)->q2 = (unsigned char) parse; |
161 | break; |
162 | case 2: |
163 | (*retval)->q3 = (unsigned char) parse; |
164 | break; |
165 | case 3: |
166 | (*retval)->q4 = (unsigned char) parse; |
167 | break; |
168 | } |
169 | |
170 | last = i + 1; |
171 | type++; |
172 | |
173 | if (sep == '/') { |
174 | /* zero out (default) unused bytes */ |
175 | switch (type) { |
176 | case 1: |
177 | (*retval)->q2 = (unsigned char) 0; |
178 | /* fall through */ |
179 | case 2: |
180 | (*retval)->q3 = (unsigned char) 0; |
181 | /* fall through */ |
182 | case 3: |
183 | (*retval)->q4 = (unsigned char) 0; |
184 | break; |
185 | } |
186 | /* force evaluation of the mask below when we break |
187 | * out of this loop */ |
188 | type = 4; |
189 | break; |
190 | } |
191 | } |
192 | } |
193 | /* parse the last quad |
194 | * the contract is that the caller makes sure the string is |
195 | * null-terminated here */ |
196 | parse = strtol(src + last, &endptr, 10); |
197 | if (*endptr != '\0' || (sep != '/' && last >= i)) { |
198 | GDKerror("Error while parsing, unexpected string '%s'" , endptr); |
199 | goto error; |
200 | } |
201 | if (type == 3) { |
202 | if (parse > 255 || parse < 0) { |
203 | GDKerror("Illegal quad value: %ld" , parse); |
204 | goto error; |
205 | } |
206 | (*retval)->q4 = (unsigned char) parse; |
207 | /* default to an exact match (all bits) */ |
208 | (*retval)->mask = (unsigned char) 32; |
209 | } else if (type == 4) { |
210 | if (parse < 0 || parse > 32) { |
211 | GDKerror("Illegal mask value: %ld" , parse); |
212 | goto error; |
213 | } |
214 | (*retval)->mask = (unsigned char) parse; |
215 | } else { |
216 | GDKerror("Error while parsing, unexpected string '%s'" , endptr); |
217 | goto error; |
218 | } |
219 | |
220 | return (ssize_t) (endptr - src); |
221 | |
222 | error: |
223 | in_setnil(*retval); |
224 | return -1; |
225 | } |
226 | /** |
227 | * Returns the string representation of the given inet value. |
228 | * Warning: GDK function |
229 | * Returns the length of the string |
230 | */ |
231 | ssize_t |
232 | INETtoString(str *retval, size_t *len, const inet *handle, bool external) |
233 | { |
234 | const inet *value = (const inet *)handle; |
235 | |
236 | if (*len < 20 || *retval == NULL) { |
237 | GDKfree(*retval); |
238 | *retval = GDKmalloc(sizeof(char) * (*len = 20)); |
239 | if( *retval == NULL) |
240 | return -1; |
241 | } |
242 | if (is_inet_nil(value)) { |
243 | if (external) |
244 | return snprintf(*retval, *len, "nil" ); |
245 | strcpy(*retval, str_nil); |
246 | return 1; |
247 | } else if (value->mask == 32) { |
248 | return snprintf(*retval, *len, "%d.%d.%d.%d" , |
249 | value->q1, value->q2, value->q3, value->q4); |
250 | } else { |
251 | return snprintf(*retval, *len, "%d.%d.%d.%d/%d" , |
252 | value->q1, value->q2, value->q3, value->q4, |
253 | value->mask); |
254 | } |
255 | } |
256 | /** |
257 | * Returns a inet, parsed from a string. The fromStr function is used |
258 | * to parse the string. |
259 | */ |
260 | str |
261 | INETnew(inet *retval, str *in) |
262 | { |
263 | ssize_t pos; |
264 | size_t len = sizeof(inet); |
265 | |
266 | pos = INETfromString(*in, &len, &retval, false); |
267 | if (pos < 0) |
268 | throw(PARSE, "inet.new" , GDK_EXCEPTION); |
269 | |
270 | return (MAL_SUCCEED); |
271 | } |
272 | |
273 | int |
274 | INETcompare(const inet *l, const inet *r) |
275 | { |
276 | bit res = 0; |
277 | if (is_inet_nil(l)) |
278 | return is_inet_nil(r) ? 0 : -1; |
279 | if (is_inet_nil(r)) |
280 | return 1; |
281 | INET_comp_EQ(&res, l, r); |
282 | if (res) |
283 | return 0; |
284 | INET_comp_LT(&res, l, r); |
285 | if (res) |
286 | return -1; |
287 | return 1; |
288 | } |
289 | |
290 | /* === Operators === */ |
291 | /** |
292 | * Returns whether val represents a nil inet value |
293 | */ |
294 | str |
295 | INET_isnil(bit *retval, const inet *val) |
296 | { |
297 | *retval = is_inet_nil(val); |
298 | |
299 | return (MAL_SUCCEED); |
300 | } |
301 | /** |
302 | * Returns whether val1 and val2 are equal. |
303 | */ |
304 | str |
305 | INET_comp_EQ(bit *retval, const inet *val1, const inet *val2) |
306 | { |
307 | if (is_inet_nil(val1) || is_inet_nil(val2)) { |
308 | *retval = bit_nil; |
309 | } else if (val1->q1 == val2->q1 && val1->q2 == val2->q2 && |
310 | val1->q3 == val2->q3 && val1->q4 == val2->q4 && |
311 | val1->mask == val2->mask) { |
312 | *retval = 1; |
313 | } else { |
314 | *retval = 0; |
315 | } |
316 | |
317 | return (MAL_SUCCEED); |
318 | } |
319 | /** |
320 | * Returns whether val1 and val2 are not equal. |
321 | */ |
322 | str |
323 | INET_comp_NEQ(bit *retval, const inet *val1, const inet *val2) |
324 | { |
325 | if (is_inet_nil(val1) || is_inet_nil(val2)) { |
326 | *retval = bit_nil; |
327 | } else if (val1->q1 == val2->q1 && val1->q2 == val2->q2 && |
328 | val1->q3 == val2->q3 && val1->q4 == val2->q4 && |
329 | val1->mask == val2->mask) { |
330 | *retval = 0; |
331 | } else { |
332 | *retval = 1; |
333 | } |
334 | |
335 | return (MAL_SUCCEED); |
336 | } |
337 | /** |
338 | * Returns whether val1 is smaller than val2. |
339 | */ |
340 | str |
341 | INET_comp_LT(bit *retval, const inet *val1, const inet *val2) |
342 | { |
343 | if (is_inet_nil(val1) || is_inet_nil(val2)) { |
344 | *retval = bit_nil; |
345 | } else if (val1->q1 < val2->q1) { |
346 | *retval = 1; |
347 | } else if (val1->q1 > val2->q1) { |
348 | *retval = 0; |
349 | } else if (val1->q2 < val2->q2) { |
350 | *retval = 1; |
351 | } else if (val1->q2 > val2->q2) { |
352 | *retval = 0; |
353 | } else if (val1->q3 < val2->q3) { |
354 | *retval = 1; |
355 | } else if (val1->q3 > val2->q3) { |
356 | *retval = 0; |
357 | } else if (val1->q4 < val2->q4) { |
358 | *retval = 1; |
359 | } else if (val1->q4 > val2->q4) { |
360 | *retval = 0; |
361 | } else if (val1->mask < val2->mask) { |
362 | *retval = 1; |
363 | } else { |
364 | *retval = 0; |
365 | } |
366 | |
367 | return (MAL_SUCCEED); |
368 | } |
369 | /** |
370 | * Returns whether val1 is greater than val2. |
371 | */ |
372 | str |
373 | INET_comp_GT(bit *retval, const inet *val1, const inet *val2) |
374 | { |
375 | return (INET_comp_LT(retval, val2, val1)); |
376 | } |
377 | /** |
378 | * Returns whether val1 is smaller than or equal to val2. |
379 | */ |
380 | str |
381 | INET_comp_LE(bit *retval, const inet *val1, const inet *val2) |
382 | { |
383 | bit ret; |
384 | |
385 | INET_comp_LT(&ret, val1, val2); |
386 | if (ret == 0) |
387 | INET_comp_EQ(&ret, val1, val2); |
388 | |
389 | *retval = ret; |
390 | return (MAL_SUCCEED); |
391 | } |
392 | /** |
393 | * Returns whether val1 is smaller than or equal to val2. |
394 | */ |
395 | str |
396 | INET_comp_GE(bit *retval, const inet *val1, const inet *val2) |
397 | { |
398 | bit ret; |
399 | |
400 | /* warning: we use LT here with swapped arguments to avoid one |
401 | * method invocation, since inet_comp_GT does the same */ |
402 | INET_comp_LT(&ret, val2, val1); |
403 | if (ret == 0) |
404 | INET_comp_EQ(&ret, val1, val2); |
405 | |
406 | *retval = ret; |
407 | return (MAL_SUCCEED); |
408 | } |
409 | /** |
410 | * Returns whether val1 is contained within val2 |
411 | */ |
412 | str |
413 | INET_comp_CW(bit *retval, const inet *val1, const inet *val2) |
414 | { |
415 | if (is_inet_nil(val1) || is_inet_nil(val2)) { |
416 | *retval = bit_nil; |
417 | } else if (val1->mask <= val2->mask) { |
418 | /* if the mask is bigger (less specific) or equal it can never |
419 | * be contained within */ |
420 | *retval = 0; |
421 | } else { |
422 | unsigned int msk; |
423 | unsigned char m[4]; |
424 | |
425 | if (val2->mask > 0) |
426 | msk = ~0U << (32 - val2->mask); |
427 | else |
428 | msk = 0; |
429 | |
430 | m[0] = (msk >> 24) & 0xFF; |
431 | m[1] = (msk >> 16) & 0xFF; |
432 | m[2] = (msk >> 8) & 0xFF; |
433 | m[3] = msk & 0xFF; |
434 | |
435 | /* all operations here are done byte based, to avoid byte sex |
436 | * problems */ |
437 | |
438 | /* if you want to see some bytes, remove this comment |
439 | fprintf(stderr, "%x %x %x %x => %x %x %x %x %x %x %x %x\n", |
440 | m[0], m[1], m[2], m[3], val1->q1, val1->q2, |
441 | val1->q3, val1->q4, val2->q1, val2->q2, val2->q3, |
442 | val2->q4); |
443 | */ |
444 | |
445 | if ((val1->q1 & m[0]) == (val2->q1 & m[0]) && |
446 | (val1->q2 & m[1]) == (val2->q2 & m[1]) && |
447 | (val1->q3 & m[2]) == (val2->q3 & m[2]) && |
448 | (val1->q4 & m[3]) == (val2->q4 & m[3])) { |
449 | *retval = 1; |
450 | } else { |
451 | *retval = 0; |
452 | } |
453 | |
454 | /* example: (hex notation) |
455 | * inet1: 10.0.0.0/24 |
456 | * IP1: 10 00 00 00 |
457 | * mask1: ff ff ff 00 |
458 | * &1: 10 00 00 00 |
459 | * inet2: 10.0.0.254 |
460 | * IP2: 10 00 00 ef |
461 | * mask1: ff ff ff 00 |
462 | * &2: 10 00 00 00 |
463 | * &1 and &2 are equal, so inet2 is within inet1 |
464 | */ |
465 | } |
466 | return (MAL_SUCCEED); |
467 | } |
468 | /** |
469 | * Returns whether val1 is contained within or equal to val2 |
470 | */ |
471 | str |
472 | INET_comp_CWE(bit *retval, const inet *val1, const inet *val2) |
473 | { |
474 | bit ret; |
475 | |
476 | /* use existing code, not fully optimal, but cheap enough */ |
477 | INET_comp_CW(&ret, val1, val2); |
478 | if (!ret) |
479 | INET_comp_EQ(&ret, val1, val2); |
480 | |
481 | *retval = ret; |
482 | return (MAL_SUCCEED); |
483 | } |
484 | /** |
485 | * Returns whether val1 is contains val2 |
486 | */ |
487 | str |
488 | INET_comp_CS(bit *retval, const inet *val1, const inet *val2) |
489 | { |
490 | /* swap the input arguments and call the contained within function */ |
491 | return (INET_comp_CW(retval, val2, val1)); |
492 | } |
493 | /** |
494 | * Returns whether val1 contains or is equal to val2 |
495 | */ |
496 | str |
497 | INET_comp_CSE(bit *retval, const inet *val1, const inet *val2) |
498 | { |
499 | /* swap the input arguments and call the contained within function */ |
500 | return (INET_comp_CWE(retval, val2, val1)); |
501 | } |
502 | |
503 | |
504 | /* === Functions === */ |
505 | /** |
506 | * Returns the broadcast address for the network the inet represents. |
507 | * If the subnet mask is 32, the given input inet is returned. |
508 | */ |
509 | str |
510 | INETbroadcast(inet *retval, const inet *val) |
511 | { |
512 | *retval = *val; |
513 | if (!is_inet_nil(val) && val->mask != 32) { |
514 | unsigned int msk; |
515 | unsigned char m[4]; |
516 | |
517 | if (val->mask > 0) |
518 | msk = ~0U << (32 - val->mask); |
519 | else |
520 | msk = 0; |
521 | |
522 | msk = ~msk; /* invert the mask */ |
523 | m[0] = (msk >> 24) & 0xFF; |
524 | m[1] = (msk >> 16) & 0xFF; |
525 | m[2] = (msk >> 8) & 0xFF; |
526 | m[3] = msk & 0xFF; |
527 | |
528 | /* if you want to see some bytes, remove this comment |
529 | fprintf(stderr, "%x %x %x %x => %x %x %x %x\n", |
530 | m[0], m[1], m[2], m[3], val->q1, val->q2, |
531 | val->q3, val->q4); |
532 | */ |
533 | |
534 | /* apply the inverted mask, so we get the broadcast */ |
535 | retval->q1 |= m[0]; |
536 | retval->q2 |= m[1]; |
537 | retval->q3 |= m[2]; |
538 | retval->q4 |= m[3]; |
539 | |
540 | /* example: (hex notation) |
541 | * inet: 10.0.0.1/24 |
542 | * IP: 10 00 00 01 |
543 | * mask: 00 00 00 ff |
544 | * &: 10 00 00 ff |
545 | * results in 10.0.0.255 |
546 | */ |
547 | } |
548 | return (MAL_SUCCEED); |
549 | } |
550 | /** |
551 | * Extract only the IP address as text. Unlike the toString function, |
552 | * this function never returns the netmask length. |
553 | */ |
554 | str |
555 | INEThost(str *retval, const inet *val) |
556 | { |
557 | str ip; |
558 | |
559 | if (is_inet_nil(val)) { |
560 | *retval = GDKstrdup(str_nil); |
561 | if( *retval == NULL) |
562 | throw(MAL,"INEThost" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
563 | } else { |
564 | ip = GDKmalloc(sizeof(char) * 16); |
565 | if( ip == NULL) |
566 | throw(MAL,"INEThost" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
567 | sprintf(ip, "%d.%d.%d.%d" , val->q1, val->q2, val->q3, val->q4); |
568 | *retval = ip; |
569 | } |
570 | return (MAL_SUCCEED); |
571 | } |
572 | /** |
573 | * Extract netmask length. |
574 | */ |
575 | str |
576 | INETmasklen(int *retval, const inet *val) |
577 | { |
578 | if (is_inet_nil(val)) { |
579 | *retval = int_nil; |
580 | } else { |
581 | *retval = val->mask; |
582 | } |
583 | return (MAL_SUCCEED); |
584 | } |
585 | /** |
586 | * Set netmask length for inet value. |
587 | */ |
588 | str |
589 | INETsetmasklen(inet *retval, const inet *val, const int *msk) |
590 | { |
591 | if (*msk < 0 || *msk > 32) |
592 | throw(ILLARG, "inet.setmask" , "Illegal netmask length value: %d" , *msk); |
593 | |
594 | *retval = *val; |
595 | if (!is_inet_nil(val)) |
596 | retval->mask = *msk; |
597 | |
598 | return (MAL_SUCCEED); |
599 | } |
600 | /** |
601 | * Construct netmask for network. |
602 | */ |
603 | str |
604 | INETnetmask(inet *retval, const inet *val) |
605 | { |
606 | *retval = *val; |
607 | if (!is_inet_nil(val)) { |
608 | unsigned int msk; |
609 | unsigned char m[4]; |
610 | |
611 | if (val->mask > 0) |
612 | msk = ~0U << (32 - val->mask); |
613 | else |
614 | msk = 0; |
615 | |
616 | m[0] = (msk >> 24) & 0xFF; |
617 | m[1] = (msk >> 16) & 0xFF; |
618 | m[2] = (msk >> 8) & 0xFF; |
619 | m[3] = msk & 0xFF; |
620 | |
621 | retval->q1 = m[0]; |
622 | retval->q2 = m[1]; |
623 | retval->q3 = m[2]; |
624 | retval->q4 = m[3]; |
625 | retval->mask = 32; |
626 | |
627 | /* example: (hex notation) |
628 | * inet: 10.0.0.1/24 |
629 | * mask: ff ff ff 00 |
630 | * results in 255.255.255.0 |
631 | */ |
632 | } |
633 | return (MAL_SUCCEED); |
634 | } |
635 | /** |
636 | * Construct host mask for network. |
637 | */ |
638 | str |
639 | INEThostmask(inet *retval, const inet *val) |
640 | { |
641 | INETnetmask(retval, val); |
642 | /* invert the netmask to obtain the host mask */ |
643 | if (!is_inet_nil(retval)) { |
644 | retval->q1 = ~retval->q1; |
645 | retval->q2 = ~retval->q2; |
646 | retval->q3 = ~retval->q3; |
647 | retval->q4 = ~retval->q4; |
648 | } |
649 | |
650 | /* example: (hex notation) |
651 | * netmask: 255.255.255.0 |
652 | * IP: ff ff ff 00 |
653 | * ~: 00 00 00 ff |
654 | * results in 0.0.0.255 |
655 | */ |
656 | |
657 | return (MAL_SUCCEED); |
658 | } |
659 | /** |
660 | * Extract network part of address, returns the same inet if the netmask |
661 | * is equal to 32. This function basically zeros out values that are |
662 | * not covered by the netmask. |
663 | */ |
664 | str |
665 | INETnetwork(inet *retval, const inet *val) |
666 | { |
667 | *retval = *val; |
668 | if (!is_inet_nil(val)) { |
669 | unsigned int msk; |
670 | unsigned char m[4]; |
671 | |
672 | if (val->mask > 0) |
673 | msk = ~0U << (32 - val->mask); |
674 | else |
675 | msk = 0; |
676 | |
677 | m[0] = (msk >> 24) & 0xFF; |
678 | m[1] = (msk >> 16) & 0xFF; |
679 | m[2] = (msk >> 8) & 0xFF; |
680 | m[3] = msk & 0xFF; |
681 | |
682 | retval->q1 &= m[0]; |
683 | retval->q2 &= m[1]; |
684 | retval->q3 &= m[2]; |
685 | retval->q4 &= m[3]; |
686 | |
687 | /* example: (hex notation) |
688 | * inet: 10.0.0.1/24 |
689 | * IP: 10 00 00 01 |
690 | * mask: ff ff ff 00 |
691 | * &: 10 00 00 00 |
692 | * results in 10.0.0.0/24 |
693 | */ |
694 | } |
695 | return (MAL_SUCCEED); |
696 | } |
697 | /** |
698 | * Extract IP address and netmask length as text. Unlike the toStr |
699 | * function, this function always prints the netmask length. |
700 | */ |
701 | str |
702 | INETtext(str *retval, const inet *val) |
703 | { |
704 | str ip; |
705 | |
706 | if (is_inet_nil(val)) { |
707 | *retval = GDKstrdup(str_nil); |
708 | if( *retval == NULL) |
709 | throw(MAL,"INETtext" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
710 | } else { |
711 | ip = GDKmalloc(sizeof(char) * 20); |
712 | if( ip == NULL) |
713 | throw(MAL,"INETtext" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
714 | |
715 | snprintf(ip, sizeof(char) * 20, "%d.%d.%d.%d/%d" , |
716 | val->q1, val->q2, val->q3, val->q4, val->mask); |
717 | *retval = ip; |
718 | } |
719 | return (MAL_SUCCEED); |
720 | } |
721 | /** |
722 | * Abbreviated display format as text. The abbreviation is only made if |
723 | * the value has no bits set to right of mask. Otherwise the return of |
724 | * this function is equal to the function text. |
725 | */ |
726 | str |
727 | INETabbrev(str *retval, const inet *val) |
728 | { |
729 | str ip; |
730 | |
731 | if (is_inet_nil(val)) { |
732 | *retval = GDKstrdup(str_nil); |
733 | if (*retval == NULL) |
734 | throw(MAL, "inet.abbrev" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
735 | } else { |
736 | unsigned int msk; |
737 | unsigned char m[4]; |
738 | |
739 | if (val->mask > 0) |
740 | msk = ~0U << (32 - val->mask); |
741 | else |
742 | msk = 0; |
743 | msk = ~msk; /* invert the mask */ |
744 | |
745 | m[0] = (msk >> 24) & 0xFF; |
746 | m[1] = (msk >> 16) & 0xFF; |
747 | m[2] = (msk >> 8) & 0xFF; |
748 | m[3] = msk & 0xFF; |
749 | |
750 | if ((val->q1 & m[0]) != 0 || |
751 | (val->q2 & m[1]) != 0 || |
752 | (val->q3 & m[2]) != 0 || |
753 | (val->q4 & m[3]) != 0) { |
754 | msk = 32; |
755 | } else { |
756 | msk = val->mask; |
757 | } |
758 | |
759 | /* example: (hex notation) |
760 | * inet: 10.1.0.0/16 |
761 | * IP: 10 01 00 00 |
762 | * mask: 00 00 ff ff |
763 | * &: 00 00 00 00 |
764 | * all zero, thus no bits on the right side of the mask |
765 | */ |
766 | |
767 | ip = GDKmalloc(sizeof(char) * 20); |
768 | if (ip == NULL) |
769 | throw(MAL, "inet.abbrev" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
770 | |
771 | if (msk > 24) { |
772 | snprintf(ip, sizeof(char) * 20, "%d.%d.%d.%d/%d" , |
773 | val->q1, val->q2, val->q3, val->q4, val->mask); |
774 | } else if (msk > 16) { |
775 | snprintf(ip, sizeof(char) * 20, "%d.%d.%d/%d" , |
776 | val->q1, val->q2, val->q3, val->mask); |
777 | } else if (msk > 8) { |
778 | snprintf(ip, sizeof(char) * 20, "%d.%d/%d" , |
779 | val->q1, val->q2, val->mask); |
780 | } else if (msk > 0) { |
781 | snprintf(ip, sizeof(char) * 20, "%d/%d" , val->q1, val->mask); |
782 | } else { |
783 | snprintf(ip, sizeof(char) * 20, "/0" ); |
784 | } |
785 | |
786 | *retval = ip; |
787 | } |
788 | return (MAL_SUCCEED); |
789 | } |
790 | str |
791 | INET_inet(inet *d, const inet *s) |
792 | { |
793 | *d = *s; |
794 | return MAL_SUCCEED; |
795 | } |
796 | str |
797 | INET_fromstr(inet *ret, str *s) |
798 | { |
799 | size_t len = sizeof(inet); |
800 | if (INETfromString(*s, &len, &ret, false) < 0) |
801 | throw(MAL, "inet.inet" , GDK_EXCEPTION); |
802 | return MAL_SUCCEED; |
803 | } |
804 | |
805 | const inet * |
806 | INETnull(void) |
807 | { |
808 | return &inet_nil; |
809 | } |
810 | |