LCOV - code coverage report
Current view: top level - monetdb5/modules/atoms - inet.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 265 309 85.8 %
Date: 2021-10-13 02:24:04 Functions: 26 28 92.9 %

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

Generated by: LCOV version 1.14