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