1 : /*
2 : * conf.c - Configuration text file parsing definition
3 : * $Id: conf.c 1810 2006-10-26 19:49:51Z remi $
4 : */
5 :
6 : /***********************************************************************
7 : * Copyright © 2004-2006 Rémi Denis-Courmont. *
8 : * This program is free software; you can redistribute and/or modify *
9 : * it under the terms of the GNU General Public License as published *
10 : * by the Free Software Foundation; version 2 of the license. *
11 : * *
12 : * This program is distributed in the hope that it will be useful, *
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
15 : * See the GNU General Public License for more details. *
16 : * *
17 : * You should have received a copy of the GNU General Public License *
18 : * along with this program; if not, you can get it from: *
19 : * http://www.gnu.org/copyleft/gpl.html *
20 : ***********************************************************************/
21 :
22 : #ifdef HAVE_CONFIG_H
23 : # include <config.h>
24 : #endif
25 :
26 : #include <gettext.h>
27 : #include <assert.h>
28 :
29 : #include <stdio.h>
30 : #include <stdlib.h> // malloc(), free()
31 : #include <stdarg.h>
32 : #include <inttypes.h>
33 : #include <string.h>
34 : #include <stdbool.h>
35 :
36 : #include <errno.h>
37 : #include <syslog.h>
38 :
39 : #include <sys/types.h>
40 : #include <sys/socket.h> // AF_INET, SOCK_DGRAM
41 : #include <netinet/in.h>
42 : #include <netdb.h>
43 : #include <libteredo/teredo.h>
44 :
45 : #include "miredo.h"
46 : #include "conf.h"
47 :
48 : struct setting
49 : {
50 : char *name;
51 : char *value;
52 : unsigned line;
53 : struct setting *next;
54 : };
55 :
56 :
57 : struct miredo_conf
58 : {
59 : struct setting *head, *tail;
60 : miredo_conf_logger logger;
61 : void *logger_data;
62 : };
63 :
64 :
65 : miredo_conf *miredo_conf_create (miredo_conf_logger logger, void *opaque)
66 3 : {
67 3 : miredo_conf *conf = (miredo_conf *)malloc (sizeof (*conf));
68 3 : if (conf == NULL)
69 0 : return NULL;
70 :
71 3 : conf->head = conf->tail = NULL;
72 3 : conf->logger = logger;
73 3 : conf->logger_data = opaque;
74 3 : return conf;
75 : }
76 :
77 :
78 : void miredo_conf_destroy (miredo_conf *conf)
79 3 : {
80 3 : assert (conf != NULL);
81 3 : miredo_conf_clear (conf, 0);
82 3 : free (conf);
83 3 : }
84 :
85 :
86 : static
87 : #ifdef __GNUC__
88 : __attribute__ ((format (printf, 2, 3)))
89 : #endif
90 : void
91 : LogError (miredo_conf *conf, const char *fmt, ...)
92 0 : {
93 0 : assert (conf != NULL);
94 0 : assert (fmt != NULL);
95 :
96 0 : if (conf->logger == NULL)
97 0 : return;
98 :
99 : va_list ap;
100 :
101 0 : va_start (ap, fmt);
102 0 : conf->logger (conf->logger_data, true, fmt, ap);
103 0 : va_end (ap);
104 : }
105 :
106 :
107 : static void
108 : #ifdef __GNUC__
109 : __attribute__ ((format (printf, 2, 3)))
110 : #endif
111 : LogWarning (miredo_conf *conf, const char *fmt, ...)
112 0 : {
113 0 : assert (conf != NULL);
114 0 : assert (fmt != NULL);
115 :
116 0 : if (conf->logger == NULL)
117 0 : return;
118 :
119 : va_list ap;
120 :
121 0 : va_start (ap, fmt);
122 0 : conf->logger (conf->logger_data, false, fmt, ap);
123 0 : va_end (ap);
124 : }
125 :
126 :
127 : void miredo_conf_clear (miredo_conf *conf, int show)
128 6 : {
129 : /* lock here */
130 6 : struct setting *ptr = conf->head;
131 :
132 6 : conf->head = NULL;
133 : /* unlock here */
134 :
135 12 : while (ptr != NULL)
136 : {
137 0 : struct setting *buf = ptr->next;
138 0 : if (show > 0)
139 : {
140 0 : LogWarning (conf, _("Superfluous directive %s at line %u"),
141 : ptr->name, ptr->line);
142 0 : show--;
143 : }
144 0 : free (ptr->name);
145 0 : free (ptr->value);
146 0 : free (ptr);
147 0 : ptr = buf;
148 : }
149 6 : }
150 :
151 :
152 : /**
153 : * Adds a setting.
154 : * @return false if memory is missing.
155 : */
156 : static bool
157 : miredo_conf_set (miredo_conf *conf, const char *name, const char *value,
158 : unsigned line)
159 6 : {
160 6 : assert (conf != NULL);
161 6 : assert (name != NULL);
162 6 : assert (value != NULL);
163 :
164 : struct setting *parm =
165 6 : (struct setting *)malloc (sizeof (struct setting));
166 :
167 6 : if (parm != NULL)
168 : {
169 6 : parm->name = strdup (name);
170 6 : if (parm->name != NULL)
171 : {
172 6 : parm->value = strdup (value);
173 6 : if (parm->value != NULL)
174 : {
175 6 : parm->line = line;
176 6 : parm->next = NULL;
177 :
178 : /* lock here */
179 6 : if (conf->head == NULL)
180 3 : conf->head = parm;
181 : else
182 : {
183 3 : assert (conf->tail != NULL);
184 3 : conf->tail->next = parm;
185 : }
186 6 : conf->tail = parm;
187 : /* unlock here */
188 :
189 6 : return true;
190 : }
191 0 : free (parm->name);
192 : }
193 0 : free (parm);
194 : }
195 :
196 0 : LogError (conf, _("Error (%s): %s"), "strdup", strerror (errno));
197 0 : return false;
198 : }
199 :
200 :
201 : /*
202 : * Looks up a setting by name.
203 : * @return NULL if not found.
204 : * Otherwise, return value must be free()d by caller.
205 : */
206 : char *miredo_conf_get (miredo_conf *conf, const char *name, unsigned *line)
207 21 : {
208 45 : for (struct setting *p = conf->head, *prev = NULL; p != NULL; p = p->next)
209 : {
210 30 : if (strcasecmp (p->name, name) == 0)
211 : {
212 6 : char *buf = p->value;
213 :
214 6 : if (line != NULL)
215 3 : *line = p->line;
216 :
217 6 : if (prev != NULL)
218 3 : prev->next = p->next;
219 : else
220 3 : conf->head = p->next;
221 :
222 6 : free (p->name);
223 6 : free (p);
224 6 : return buf;
225 : }
226 24 : prev = p;
227 : }
228 :
229 15 : return NULL;
230 : }
231 :
232 :
233 : static bool miredo_conf_read_FILE (miredo_conf *conf, FILE *stream)
234 3 : {
235 : char lbuf[1056];
236 3 : unsigned line = 0;
237 :
238 105 : while (fgets (lbuf, sizeof (lbuf), stream) != NULL)
239 : {
240 99 : size_t len = strlen (lbuf) - 1;
241 99 : line++;
242 :
243 99 : if (lbuf[len] != '\n')
244 : {
245 0 : while (fgetc (stream) != '\n')
246 0 : if (feof (stream) || ferror (stream))
247 : break;
248 :
249 0 : LogWarning (conf, _("Skipped overly long line %u"), line);
250 0 : continue;
251 : }
252 :
253 99 : lbuf[len] = '\0';
254 : char nbuf[32], vbuf[1024];
255 :
256 99 : switch (sscanf (lbuf, " %31s %1023s", nbuf, vbuf))
257 : {
258 : case 2:
259 75 : if ((*nbuf != '#') // comment
260 : && !miredo_conf_set (conf, nbuf, vbuf, line))
261 0 : return false;
262 75 : break;
263 :
264 : case 1:
265 3 : if (*nbuf != '#')
266 0 : LogWarning (conf, _("Ignoring line %u: %s"),
267 : line, nbuf);
268 : break;
269 : }
270 : }
271 :
272 3 : if (ferror (stream))
273 : {
274 0 : LogError (conf, _("Error reading configuration file: %s"),
275 : strerror (errno));
276 0 : return false;
277 : }
278 3 : return true;
279 : }
280 :
281 :
282 : /* Parses a file.
283 : *
284 : * @return false on I/O error, true on success.
285 : */
286 : bool miredo_conf_read_file (miredo_conf *conf, const char *path)
287 3 : {
288 3 : assert (path != NULL);
289 :
290 3 : FILE *stream = fopen (path, "r");
291 3 : if (stream != NULL)
292 : {
293 3 : bool ret = miredo_conf_read_FILE (conf, stream);
294 3 : fclose (stream);
295 3 : return ret;
296 : }
297 :
298 0 : LogError (conf, _("Error opening configuration file %s: %s"), path,
299 : strerror (errno));
300 0 : return false;
301 : }
302 :
303 :
304 : /**
305 : * Looks up an unsigned 16-bits integer. Returns false if the
306 : * setting was found but incorrectly formatted.
307 : *
308 : * If the setting was not found value, returns true and leave
309 : * *value unchanged.
310 : */
311 : bool miredo_conf_get_int16 (miredo_conf *conf, const char *name,
312 : uint16_t *value, unsigned *line)
313 3 : {
314 3 : char *val = miredo_conf_get (conf, name, line);
315 :
316 3 : if (val == NULL)
317 3 : return true;
318 :
319 : char *end;
320 : unsigned long l;
321 :
322 0 : l = strtoul (val, &end, 0);
323 :
324 0 : if ((*end) || (l > 65535))
325 : {
326 0 : LogError (conf, _("Invalid integer value \"%s\" for %s: %s"),
327 : val, name, strerror (errno));
328 0 : free (val);
329 0 : return false;
330 : }
331 0 : *value = (uint16_t)l;
332 0 : free (val);
333 0 : return true;
334 : }
335 :
336 :
337 : #if 0
338 : /* This is supposedly bad for DSO (but we are not a DSO atm) */
339 : static const char *true_strings[] = { "yes", "true", "on", "enabled", NULL };
340 : static const char *false_strings[] =
341 : { "no", "false", "off", "disabled", NULL };
342 :
343 : bool miredo_conf_get_bool (miredo_conf *conf, const char *name,
344 : bool *value, unsigned *line)
345 : {
346 : char *val = miredo_conf_get (conf, name, line);
347 :
348 : if (val == NULL)
349 : return true;
350 : else
351 : {
352 : // check if value is a number
353 : long l;
354 : char *end;
355 :
356 : l = strtol (val, &end, 0);
357 :
358 : if (*end == '\0') // success
359 : {
360 : *value = (l != 0);
361 : free (val);
362 : return true;
363 : }
364 : }
365 :
366 : for (const char **ptr = true_strings; *ptr != NULL; ptr++)
367 : if (!strcasecmp (val, *ptr))
368 : {
369 : *value = true;
370 : free (val);
371 : return true;
372 : }
373 :
374 : for (const char **ptr = false_strings; *ptr != NULL; ptr++)
375 : if (!strcasecmp (val, *ptr))
376 : {
377 : *value = false;
378 : free (val);
379 : return true;
380 : }
381 :
382 : LogError (conf, _("Invalid boolean value \"%s\" for %s"), val, name);
383 : free (val);
384 : return false;
385 : }
386 : #endif
387 :
388 : /* Utilities function */
389 :
390 : /**
391 : * Looks up an IPv4 address (network byte order) associated with hostname.
392 : */
393 : int GetIPv4ByName (const char *hostname, uint32_t *ipv4)
394 3 : {
395 : struct addrinfo help =
396 : {
397 : .ai_family = AF_INET,
398 : .ai_socktype = SOCK_DGRAM,
399 : .ai_protocol = IPPROTO_UDP
400 3 : }, *res;
401 :
402 3 : int check = getaddrinfo (hostname, NULL, &help, &res);
403 3 : if (check)
404 0 : return check;
405 :
406 3 : *ipv4 = ((const struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr;
407 3 : freeaddrinfo (res);
408 3 : return 0;
409 : }
410 :
411 :
412 : bool miredo_conf_parse_IPv4 (miredo_conf *conf, const char *name,
413 : uint32_t *ipv4)
414 9 : {
415 : unsigned line;
416 9 : char *val = miredo_conf_get (conf, name, &line);
417 :
418 9 : if (val == NULL)
419 6 : return true;
420 :
421 3 : int check = GetIPv4ByName (val, ipv4);
422 :
423 3 : if (check)
424 : {
425 0 : LogError (conf, _("Invalid hostname \"%s\" at line %u: %s"),
426 : val, line, gai_strerror (check));
427 0 : free (val);
428 0 : return false;
429 : }
430 :
431 3 : free (val);
432 3 : return true;
433 : }
434 :
435 :
436 : bool miredo_conf_parse_IPv6 (miredo_conf *conf, const char *name,
437 : struct in6_addr *value)
438 0 : {
439 : unsigned line;
440 0 : char *val = miredo_conf_get (conf, name, &line);
441 :
442 0 : if (val == NULL)
443 0 : return true;
444 :
445 : struct addrinfo help =
446 : {
447 : .ai_family = AF_INET6,
448 : .ai_socktype = SOCK_DGRAM,
449 : .ai_protocol = IPPROTO_UDP
450 0 : }, *res;
451 :
452 0 : int check = getaddrinfo (val, NULL, &help, &res);
453 :
454 0 : if (check)
455 : {
456 0 : LogError (conf, _("Invalid hostname \"%s\" at line %u: %s"),
457 : val, line, gai_strerror (check));
458 0 : free (val);
459 0 : return false;
460 : }
461 :
462 0 : memcpy (value, &((const struct sockaddr_in6*)(res->ai_addr))->sin6_addr,
463 : sizeof (struct in6_addr));
464 :
465 0 : freeaddrinfo (res);
466 0 : free (val);
467 0 : return true;
468 : }
469 :
470 :
471 : bool miredo_conf_parse_teredo_prefix (miredo_conf *conf, const char *name,
472 : uint32_t *value)
473 0 : {
474 : union teredo_addr addr;
475 0 : memset (&addr, 0, sizeof (addr));
476 0 : addr.teredo.prefix = *value;
477 :
478 0 : if (miredo_conf_parse_IPv6 (conf, name, &addr.ip6))
479 : {
480 0 : if (!is_valid_teredo_prefix (addr.teredo.prefix))
481 : {
482 0 : LogError (conf, _("Invalid Teredo IPv6 prefix: %x::/32"),
483 : addr.teredo.prefix);
484 0 : return false;
485 : }
486 :
487 0 : *value = addr.teredo.prefix;
488 0 : return true;
489 : }
490 0 : return false;
491 : }
492 :
493 :
494 : static const struct miredo_conf_syslog_facility
495 : {
496 : const char *str;
497 : int facility;
498 : } facilities[] =
499 : {
500 : #ifdef LOG_AUTH
501 : { "auth", LOG_AUTH },
502 : #endif
503 : #ifdef LOG_AUTHPRIV
504 : { "authpriv", LOG_AUTHPRIV },
505 : #endif
506 : #ifdef LOG_CRON
507 : { "cron", LOG_CRON },
508 : #endif
509 : #ifdef LOG_DAEMON
510 : { "daemon", LOG_DAEMON },
511 : #endif
512 : #ifdef LOG_FTP
513 : { "ftp", LOG_FTP },
514 : #endif
515 : #ifdef LOG_KERN
516 : { "kern", LOG_KERN },
517 : #endif
518 : { "local0", LOG_LOCAL0 },
519 : { "local1", LOG_LOCAL1 },
520 : { "local2", LOG_LOCAL2 },
521 : { "local3", LOG_LOCAL3 },
522 : { "local4", LOG_LOCAL4 },
523 : { "local5", LOG_LOCAL5 },
524 : { "local6", LOG_LOCAL6 },
525 : { "local7", LOG_LOCAL7 },
526 : #ifdef LOG_LPR
527 : { "lpr", LOG_LPR },
528 : #endif
529 : #ifdef LOG_MAIL
530 : { "mail", LOG_MAIL },
531 : #endif
532 : #ifdef LOG_NEWS
533 : { "news", LOG_NEWS },
534 : #endif
535 : #ifdef LOG_SYSLOG
536 : { "syslog", LOG_SYSLOG },
537 : #endif
538 : { "user", LOG_USER },
539 : #ifdef LOG_UUCP
540 : { "uucp", LOG_UUCP },
541 : #endif
542 : { NULL, 0 }
543 : };
544 :
545 :
546 : bool miredo_conf_parse_syslog_facility (miredo_conf *conf, const char *name,
547 : int *facility)
548 3 : {
549 : unsigned line;
550 3 : char *str = miredo_conf_get (conf, name, &line);
551 :
552 3 : if (str == NULL)
553 3 : return true;
554 :
555 0 : for (const struct miredo_conf_syslog_facility *ptr = facilities;
556 0 : ptr->str != NULL; ptr++)
557 : {
558 0 : if (!strcasecmp (str, ptr->str))
559 : {
560 0 : *facility = ptr->facility;
561 0 : free (str);
562 0 : return true;
563 : }
564 : }
565 :
566 0 : LogError (conf, _("Unknown syslog facility \"%s\" at line %u"), str,
567 : line);
568 0 : free (str);
569 0 : return false;
570 : }
|