LCOV - code coverage report
Current view: top level - monetdb5/modules/atoms - inet.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 292 334 87.4 %
Date: 2024-10-07 21:21:43 Functions: 26 28 92.9 %

          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); }

Generated by: LCOV version 1.14