LCOV - code coverage report
Current view: top level - src/plugins/macaddr - macaddr.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 77 79 97.5 %
Date: 2019-09-12 12:28:41 Functions: 7 8 87.5 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Source for macaddr plugin
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : #include "macaddr.h"
      11             : 
      12             : #include <assert.h>
      13             : #include <ctype.h>
      14             : #include <kdberrors.h>
      15             : #include <kdbhelper.h>
      16             : #include <kdbprivate.h>
      17             : #include <regex.h>
      18             : #include <stdio.h>
      19             : #include <stdlib.h>
      20             : 
      21             : #define VALIDATION_SUCCESS 0
      22             : #define VALIDATION_ERROR 1
      23             : #define VALIDATION_ISINT 2
      24             : 
      25             : #define MAXMACINT 281474976710655
      26             : 
      27             : /**
      28             :  * Transforms a mac string into a 64 bit integer
      29             :  * @param key the key containing the mac address
      30             :  */
      31          42 : void transformMac (Key * key)
      32             : {
      33          42 :         const char * macKey = keyString (key);
      34             : 
      35          42 :         char * macWithoutSeparators = elektraMalloc (13);
      36             : 
      37          42 :         size_t len = strlen (macKey);
      38          42 :         size_t j = 0;
      39         712 :         for (size_t i = 0; i < len; ++i)
      40             :         {
      41         670 :                 if (macKey[i] == ':' || macKey[i] == '-') continue;
      42             : 
      43         504 :                 macWithoutSeparators[j++] = macKey[i];
      44             :         }
      45          42 :         macWithoutSeparators[12] = '\0';
      46             : 
      47          42 :         unsigned long long intValue = strtoull (macWithoutSeparators, NULL, 16);
      48             : 
      49          42 :         const int n = snprintf (NULL, 0, "%llu", intValue);
      50          42 :         char * buffer = elektraMalloc (n + 1);
      51          42 :         snprintf (buffer, n + 1, "%llu", intValue);
      52             : 
      53          42 :         keySetString (key, buffer);
      54          42 :         elektraFree (buffer);
      55          42 :         elektraFree (macWithoutSeparators);
      56          42 : }
      57             : 
      58             : /**
      59             :  * Checks if the first string parameter conforms to the regex of the second string parameter
      60             :  * @param mac the string to check
      61             :  * @param regexString the regex to apply
      62             :  * @retval VALIDATION_SUCCESS if a match has been found, else VALIDATION_ERROR
      63             :  */
      64         265 : int checkRegex (const char * mac, const char * regexString)
      65             : {
      66             :         regex_t regex;
      67             : 
      68         265 :         int reg = regcomp (&regex, regexString, REG_NOSUB | REG_EXTENDED | REG_NEWLINE);
      69         265 :         if (reg) return -1;
      70             : 
      71         265 :         reg = regexec (&regex, mac, 0, NULL, 0);
      72         265 :         regfree (&regex);
      73             : 
      74         265 :         return reg == REG_NOMATCH ? VALIDATION_ERROR : VALIDATION_SUCCESS;
      75             : }
      76             : 
      77             : /**
      78             :  * Checks if the string parameter consists solely of numbers and the overall number is valid for a MAC address
      79             :  * @param mac the string to check
      80             :  * @retval VALIDATION_ISINT if mac solely consists of numbers and is valid, else VALIDATION_ERROR
      81             :  */
      82         137 : int checkIntMac (const char * mac)
      83             : {
      84         137 :         if (strlen (mac) < 1) return 1;
      85             :         char * endptr;
      86             : 
      87         137 :         unsigned long long ret = 0;
      88         137 :         errno = 0;
      89         137 :         ret = strtoull (mac, &endptr, 10);
      90             : 
      91         137 :         if (errno == EINVAL || errno == ERANGE || *endptr != '\0') return VALIDATION_ERROR;
      92          18 :         if (ret > MAXMACINT) return VALIDATION_ERROR;
      93             : 
      94          13 :         return VALIDATION_ISINT;
      95             : }
      96             : 
      97             : /**
      98             :  * Checks if the supplied MAC key is a valid MAC address
      99             :  * @param key the key containing the MAC address
     100             :  * @retval VALIDATION_SUCCESS if MAC address is valid, VALIDATION_ISINT if MAC address is a 64 bit integer and valid, else VALIDATION_ERROR
     101             :  */
     102         137 : int validateMac (Key * key)
     103             : {
     104         137 :         const Key * metaKey = keyGetMeta (key, "check/macaddr");
     105         137 :         if (!metaKey) return 1;
     106             : 
     107         137 :         const char * mac = keyString (key);
     108             : 
     109         137 :         const char * regexColon = "^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$";
     110         137 :         const char * regexHyphen = "^([0-9A-Fa-f]{2}-){5}([0-9A-Fa-f]{2})$";
     111         137 :         const char * regexHyphenSingle = "^([0-9A-Fa-f]{6}-)([0-9A-Fa-f]{6})$";
     112             : 
     113         137 :         const char * regexStrings[] = { regexColon, regexHyphen, regexHyphenSingle };
     114             : 
     115             :         int ret;
     116         137 :         int i = 0;
     117             : 
     118         137 :         ret = checkIntMac (mac);
     119             : 
     120         539 :         while (ret == VALIDATION_ERROR && i < 3)
     121             :         {
     122         265 :                 ret = checkRegex (mac, regexStrings[i]);
     123         265 :                 ++i;
     124             :         }
     125             : 
     126             :         return ret;
     127             : }
     128             : 
     129          64 : int elektraMacaddrGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * parentKey)
     130             : {
     131          64 :         if (!elektraStrCmp (keyName (parentKey), "system/elektra/modules/macaddr"))
     132             :         {
     133          37 :                 KeySet * contract =
     134          37 :                         ksNew (30, keyNew ("system/elektra/modules/macaddr", KEY_VALUE, "macaddr plugin waits for your orders", KEY_END),
     135             :                                keyNew ("system/elektra/modules/macaddr/exports", KEY_END),
     136             :                                keyNew ("system/elektra/modules/macaddr/exports/get", KEY_FUNC, elektraMacaddrGet, KEY_END),
     137             :                                keyNew ("system/elektra/modules/macaddr/exports/set", KEY_FUNC, elektraMacaddrSet, KEY_END),
     138             : 
     139             : #include ELEKTRA_README
     140             : 
     141             :                                keyNew ("system/elektra/modules/macaddr/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
     142          37 :                 ksAppend (returned, contract);
     143          37 :                 ksDel (contract);
     144             : 
     145          37 :                 return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     146             :         }
     147          27 :         ksRewind (returned);
     148             :         Key * cur;
     149         120 :         while ((cur = ksNext (returned)) != NULL)
     150             :         {
     151          66 :                 const Key * meta = keyGetMeta (cur, "check/macaddr");
     152          66 :                 if (!meta) continue;
     153             : 
     154          50 :                 int rc = validateMac (cur);
     155          50 :                 if (rc == VALIDATION_ERROR)
     156             :                 {
     157           0 :                         ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "String '%s' is not in a supported format", keyString (cur));
     158           0 :                         return ELEKTRA_PLUGIN_STATUS_ERROR;
     159             :                 }
     160             : 
     161          50 :                 if (rc != VALIDATION_ISINT)
     162             :                 {
     163          42 :                         char * origvalue = elektraStrDup (keyString (cur));
     164          42 :                         transformMac (cur);
     165          42 :                         keySetMeta (cur, "origvalue", origvalue);
     166          42 :                         elektraFree (origvalue);
     167             :                 }
     168             :         }
     169             : 
     170             :         return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     171             : }
     172             : 
     173          93 : int elektraMacaddrSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * parentKey)
     174             : {
     175          93 :         ksRewind (returned);
     176             :         Key * cur;
     177         269 :         while ((cur = ksNext (returned)) != NULL)
     178             :         {
     179         114 :                 const Key * meta = keyGetMeta (cur, "check/macaddr");
     180         114 :                 if (!meta) continue;
     181             : 
     182          98 :                 const Key * origValue = keyGetMeta (cur, "origvalue");
     183          98 :                 if (origValue)
     184             :                 {
     185          11 :                         keySetString (cur, keyString (origValue));
     186          11 :                         continue;
     187             :                 }
     188             : 
     189          87 :                 int rc = validateMac (cur);
     190          87 :                 if (rc == VALIDATION_ERROR)
     191             :                 {
     192          31 :                         ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (
     193             :                                 parentKey,
     194             :                                 "%s is not in a supported format. Supported formats are:\nXX:XX:XX:XX:XX:XX\n"
     195             :                                 "XX-XX-XX-XX-XX-XX\nXXXXXX-XXXXXX\nInteger values (0 - 281474976710655)",
     196             :                                 keyString (cur));
     197          31 :                         return ELEKTRA_PLUGIN_STATUS_ERROR;
     198             :                 }
     199             :         }
     200             : 
     201             :         return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     202             : }
     203             : 
     204         199 : Plugin * ELEKTRA_PLUGIN_EXPORT
     205             : {
     206             :         // clang-format off
     207         199 :         return elektraPluginExport ("macaddr",
     208             :                                     ELEKTRA_PLUGIN_GET, &elektraMacaddrGet,
     209             :                                     ELEKTRA_PLUGIN_SET, &elektraMacaddrSet,
     210             :                                     ELEKTRA_PLUGIN_END);
     211             :         // clang-format on
     212             : }

Generated by: LCOV version 1.13