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 (®ex, regexString, REG_NOSUB | REG_EXTENDED | REG_NEWLINE);
69 265 : if (reg) return -1;
70 :
71 265 : reg = regexec (®ex, mac, 0, NULL, 0);
72 265 : regfree (®ex);
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 : }
|