Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Contains the get direction of the hosts plugin
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 : #include "hosts.h"
11 : #include "keymetaformatting.h"
12 :
13 : #include <errno.h>
14 : #include <netdb.h>
15 : #include <sys/socket.h>
16 : #include <sys/types.h>
17 :
18 : #ifndef HAVE_KDBCONFIG
19 : #include "kdbconfig.h"
20 : #endif
21 :
22 : #include <kdbproposal.h>
23 :
24 : #define MAX_SPACE_BUFFER = 16;
25 :
26 : typedef void CommentConstructor (KeySet *, size_t, const char *, const char *);
27 :
28 : /*
29 : * Determines the address family of the supplied network address
30 : *
31 : * @param address the network address to be analyzed
32 : * @return a number identifying the network address (e.g. AF_INET) or -1 if an error occurred
33 : */
34 55 : static int getAddressFamily (const char * address)
35 : {
36 : struct addrinfo hint;
37 : struct addrinfo * info;
38 55 : memset (&hint, 0, sizeof (hint));
39 :
40 : /* no specific family is requested and name lookups are disabled */
41 : hint.ai_family = AF_UNSPEC;
42 55 : hint.ai_flags = AI_NUMERICHOST;
43 :
44 55 : int ret = getaddrinfo (address, 0, &hint, &info);
45 :
46 55 : if (ret != 0)
47 : {
48 : return -1;
49 : }
50 :
51 53 : int result = info->ai_family;
52 53 : freeaddrinfo (info);
53 53 : return result;
54 : }
55 :
56 55 : static void addAddressHierarchy (Key * key, char * fieldbuffer)
57 : {
58 : /* determine whether this is an ipv4 or ipv6 entry */
59 55 : int family = getAddressFamily (fieldbuffer);
60 :
61 : /* in case of an error default to ipv4 */
62 55 : switch (family)
63 : {
64 : case AF_INET6:
65 16 : keyAddBaseName (key, "ipv6");
66 16 : break;
67 : default:
68 39 : keyAddBaseName (key, "ipv4");
69 : }
70 55 : }
71 :
72 142 : size_t elektraParseToken (char ** token, const char * line)
73 : {
74 142 : size_t i = 0;
75 :
76 : /* skip whitespaces */
77 381 : while (line[i] == ' ' || line[i] == '\t')
78 97 : i++;
79 :
80 : /* end of line or string, abort */
81 142 : if (line[i] == '\0' || line[i] == '\n') return 0;
82 :
83 : size_t start = i;
84 :
85 : /* count the number of characters in the token */
86 1362 : while (line[i] != ' ' && line[i] != '\t' && line[i] != '\0' && line[i] != '\n')
87 1224 : i++;
88 :
89 138 : size_t tokenSize = i - start + 1;
90 138 : *token = (char *) elektraMalloc (tokenSize);
91 138 : strncpy (*token, line + start, tokenSize);
92 138 : (*token)[tokenSize - 1] = '\0';
93 :
94 138 : return i;
95 : }
96 :
97 53 : static void setOrderMeta (Key * key, int order)
98 : {
99 : char buffer[MAX_ORDER_SIZE];
100 53 : snprintf (buffer, MAX_ORDER_SIZE, "%d", order);
101 53 : keySetMeta (key, "order", buffer);
102 53 : }
103 :
104 156 : static int parseComment (KeySet * comments, char * line, const char * commentStart, CommentConstructor constructor)
105 : {
106 : /* count the number of whitespace characters before the comment */
107 156 : size_t spaces = elektraCountStartSpaces (line);
108 :
109 156 : if (*(line + spaces) == '\n')
110 : {
111 59 : constructor (comments, spaces, 0, 0);
112 59 : return 1;
113 : }
114 :
115 97 : size_t commentStartLen = strlen (commentStart);
116 97 : if (!strncmp (line + spaces, commentStart, commentStartLen))
117 : {
118 : /* check for newlines */
119 10 : char * newLine = strchr (line, '\n');
120 10 : if (newLine)
121 : {
122 10 : *newLine = '\0';
123 : }
124 :
125 10 : constructor (comments, spaces, commentStart, line + spaces + commentStartLen);
126 10 : return 1;
127 : }
128 :
129 : return 0;
130 : }
131 :
132 55 : static char * parseCanonicalName (Key * result, char * line)
133 : {
134 : char * fieldBuffer;
135 55 : char * tokenPointer = line;
136 :
137 : /* read the ip address (if any) */
138 55 : int sret = elektraParseToken (&fieldBuffer, line);
139 :
140 55 : if (sret == 0) return 0;
141 :
142 55 : tokenPointer += sret;
143 :
144 : /* determine whether this is an ipv4 or ipv6 entry */
145 55 : addAddressHierarchy (result, fieldBuffer);
146 :
147 : /* store the ip address */
148 55 : keySetString (result, fieldBuffer);
149 :
150 55 : elektraFree (fieldBuffer);
151 :
152 : /* read the canonical name */
153 55 : sret = elektraParseToken (&fieldBuffer, tokenPointer);
154 :
155 55 : if (sret == 0) return 0;
156 :
157 55 : tokenPointer += sret;
158 55 : keyAddBaseName (result, fieldBuffer);
159 55 : elektraFree (fieldBuffer);
160 :
161 55 : return tokenPointer;
162 : }
163 :
164 32 : static char * parseAlias (KeySet * append, const Key * hostParent, char * tokenPointer)
165 : {
166 : char * fieldBuffer;
167 32 : int sret = 0;
168 32 : sret = elektraParseToken (&fieldBuffer, tokenPointer);
169 32 : if (sret == 0) return 0;
170 :
171 28 : Key * alias = keyDup (hostParent);
172 28 : keyAddBaseName (alias, fieldBuffer);
173 28 : elektraFree (fieldBuffer);
174 :
175 : /* only add the alias if it does not exist already */
176 28 : if (ksLookup (append, alias, KDB_O_NONE))
177 : {
178 2 : keyDel (alias);
179 : }
180 : else
181 : {
182 26 : ksAppendKey (append, alias);
183 : }
184 :
185 28 : return tokenPointer + sret;
186 : }
187 :
188 102 : static int elektraKeySetMetaKeySet (Key * key, KeySet * metaKeySet)
189 : {
190 102 : if (!key) return 0;
191 102 : if (!metaKeySet) return 0;
192 :
193 : Key * currentMeta;
194 102 : cursor_t initialCursor = ksGetCursor (metaKeySet);
195 102 : ksRewind (metaKeySet);
196 301 : while ((currentMeta = ksNext (metaKeySet)))
197 : {
198 97 : keySetMeta (key, keyName (currentMeta), keyString (currentMeta));
199 : }
200 :
201 102 : ksSetCursor (metaKeySet, initialCursor);
202 :
203 102 : return 1;
204 : }
205 :
206 147 : int elektraHostsGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned, Key * parentKey)
207 : {
208 147 : int errnosave = errno;
209 : char readBuffer[HOSTS_KDB_BUFFER_SIZE];
210 :
211 147 : if (!strcmp (keyName (parentKey), "system/elektra/modules/hosts"))
212 : {
213 98 : KeySet * moduleConfig =
214 : #include "contract.h"
215 98 : ksAppend (returned, moduleConfig);
216 98 : ksDel (moduleConfig);
217 98 : return 1;
218 : }
219 :
220 49 : FILE * fp = fopen (keyValue (parentKey), "r");
221 :
222 49 : if (fp == 0)
223 : {
224 0 : ELEKTRA_SET_ERROR_GET (parentKey);
225 0 : errno = errnosave;
226 0 : return -1;
227 : }
228 :
229 49 : ksClear (returned);
230 49 : KeySet * append = ksNew (ksGetSize (returned) * 2, KS_END);
231 :
232 49 : Key * key = keyDup (parentKey);
233 49 : ksAppendKey (append, key);
234 :
235 49 : Key * currentKey = 0;
236 49 : KeySet * comments = ksNew (0, KS_END);
237 49 : size_t order = 1;
238 49 : char * tokenPointer = 0;
239 : while (1)
240 : {
241 124 : char * fret = fgets (readBuffer, HOSTS_KDB_BUFFER_SIZE, fp);
242 :
243 124 : if (!fret) break;
244 :
245 75 : if (!currentKey)
246 : {
247 57 : currentKey = keyDup (parentKey);
248 : }
249 :
250 75 : if (parseComment (comments, readBuffer, "#", &elektraAddLineComment)) continue;
251 :
252 55 : tokenPointer = parseCanonicalName (currentKey, readBuffer);
253 55 : if (tokenPointer == 0) continue;
254 :
255 : /* canonical names have to be unique. If the hosts file contains
256 : * duplicates, we honor only the first entry. This mirrors the
257 : * behaviour of most name resolution implementations
258 : */
259 55 : if (ksLookup (append, currentKey, KDB_O_NONE))
260 : {
261 2 : keyDel (currentKey);
262 2 : currentKey = 0;
263 2 : ksClear (comments);
264 2 : continue;
265 : }
266 :
267 : /* assign an order to the entry */
268 53 : setOrderMeta (currentKey, order);
269 53 : ++order;
270 :
271 53 : ksAppendKey (append, currentKey);
272 :
273 : /* Read in aliases */
274 : while (1)
275 : {
276 : /* if we find a comment, there cannot be any more aliases */
277 81 : if (parseComment (comments, tokenPointer, "#", &elektraAddInlineComment)) break;
278 :
279 : /* if we reach the end of the line, there cannot be any more aliases */
280 32 : tokenPointer = parseAlias (append, currentKey, tokenPointer);
281 32 : if (tokenPointer == 0) break;
282 : }
283 :
284 : /* flush collected comments and start over with a new entry */
285 53 : elektraKeySetMetaKeySet (currentKey, comments);
286 53 : ksClear (comments);
287 53 : currentKey = 0;
288 : }
289 :
290 49 : keyDel (currentKey);
291 :
292 49 : if (comments)
293 : {
294 : /* save comments without a matching entry to the parentkey */
295 49 : elektraKeySetMetaKeySet (parentKey, comments);
296 49 : ksClear (comments);
297 49 : ksDel (comments);
298 : }
299 :
300 : int ret;
301 49 : if (!ferror (fp))
302 : {
303 49 : ksClear (returned);
304 49 : ksAppend (returned, append);
305 49 : ksDel (append);
306 49 : ret = 1;
307 : }
308 : else
309 : {
310 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "General parse error. Reason: %s", strerror (errno));
311 0 : ksDel (append);
312 0 : ret = -1;
313 : }
314 :
315 49 : fclose (fp);
316 49 : errno = errnosave;
317 49 : return ret;
318 : }
319 :
320 612 : Plugin * ELEKTRA_PLUGIN_EXPORT
321 : {
322 : // clang-format off
323 612 : return elektraPluginExport("hosts",
324 : ELEKTRA_PLUGIN_GET, &elektraHostsGet,
325 : ELEKTRA_PLUGIN_SET, &elektraHostsSet,
326 : ELEKTRA_PLUGIN_END);
327 : }
328 :
|