1 /*
2 * conf.c - Configuration text file parsing definition
3 */
4
5 /***********************************************************************
6 * Copyright © 2004-2006 Rémi Denis-Courmont. *
7 * This program is free software; you can redistribute and/or modify *
8 * it under the terms of the GNU General Public License as published *
9 * by the Free Software Foundation; version 2 of the license, or (at *
10 * your option) any later version. *
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
miredo_conf_create(miredo_conf_logger logger,void * opaque)65 miredo_conf *miredo_conf_create (miredo_conf_logger logger, void *opaque)
66 {
67 miredo_conf *conf = (miredo_conf *)malloc (sizeof (*conf));
68 if (conf == NULL)
69 return NULL;
70
71 conf->head = conf->tail = NULL;
72 conf->logger = logger;
73 conf->logger_data = opaque;
74 return conf;
75 }
76
77
miredo_conf_destroy(miredo_conf * conf)78 void miredo_conf_destroy (miredo_conf *conf)
79 {
80 assert (conf != NULL);
81 miredo_conf_clear (conf, 0);
82 free (conf);
83 }
84
85
86 static
87 #ifdef __GNUC__
88 __attribute__ ((format (printf, 2, 3)))
89 #endif
90 void
LogError(miredo_conf * conf,const char * fmt,...)91 LogError (miredo_conf *conf, const char *fmt, ...)
92 {
93 assert (conf != NULL);
94 assert (fmt != NULL);
95
96 if (conf->logger == NULL)
97 return;
98
99 va_list ap;
100
101 va_start (ap, fmt);
102 conf->logger (conf->logger_data, true, fmt, ap);
103 va_end (ap);
104 }
105
106
107 static void
108 #ifdef __GNUC__
109 __attribute__ ((format (printf, 2, 3)))
110 #endif
LogWarning(miredo_conf * conf,const char * fmt,...)111 LogWarning (miredo_conf *conf, const char *fmt, ...)
112 {
113 assert (conf != NULL);
114 assert (fmt != NULL);
115
116 if (conf->logger == NULL)
117 return;
118
119 va_list ap;
120
121 va_start (ap, fmt);
122 conf->logger (conf->logger_data, false, fmt, ap);
123 va_end (ap);
124 }
125
126
miredo_conf_clear(miredo_conf * conf,int show)127 void miredo_conf_clear (miredo_conf *conf, int show)
128 {
129 /* lock here */
130 struct setting *ptr = conf->head;
131
132 conf->head = NULL;
133 /* unlock here */
134
135 while (ptr != NULL)
136 {
137 struct setting *buf = ptr->next;
138 if (show > 0)
139 {
140 LogWarning (conf, _("Superfluous directive %s at line %u"),
141 ptr->name, ptr->line);
142 show--;
143 }
144 free (ptr->name);
145 free (ptr->value);
146 free (ptr);
147 ptr = buf;
148 }
149 }
150
151
152 /**
153 * Adds a setting.
154 * @return false if memory is missing.
155 */
156 static bool
miredo_conf_set(miredo_conf * conf,const char * name,const char * value,unsigned line)157 miredo_conf_set (miredo_conf *conf, const char *name, const char *value,
158 unsigned line)
159 {
160 assert (conf != NULL);
161 assert (name != NULL);
162 assert (value != NULL);
163
164 struct setting *parm =
165 (struct setting *)malloc (sizeof (struct setting));
166
167 if (parm != NULL)
168 {
169 parm->name = strdup (name);
170 if (parm->name != NULL)
171 {
172 parm->value = strdup (value);
173 if (parm->value != NULL)
174 {
175 parm->line = line;
176 parm->next = NULL;
177
178 /* lock here */
179 if (conf->head == NULL)
180 conf->head = parm;
181 else
182 {
183 assert (conf->tail != NULL);
184 conf->tail->next = parm;
185 }
186 conf->tail = parm;
187 /* unlock here */
188
189 return true;
190 }
191 free (parm->name);
192 }
193 free (parm);
194 }
195
196 LogError (conf, _("Error (%s): %s"), "strdup", strerror (errno));
197 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 */
miredo_conf_get(miredo_conf * conf,const char * name,unsigned * line)206 char *miredo_conf_get (miredo_conf *conf, const char *name, unsigned *line)
207 {
208 for (struct setting *p = conf->head, *prev = NULL; p != NULL; p = p->next)
209 {
210 if (strcasecmp (p->name, name) == 0)
211 {
212 char *buf = p->value;
213
214 if (line != NULL)
215 *line = p->line;
216
217 if (prev != NULL)
218 prev->next = p->next;
219 else
220 conf->head = p->next;
221
222 free (p->name);
223 free (p);
224 return buf;
225 }
226 prev = p;
227 }
228
229 return NULL;
230 }
231
232
miredo_conf_read_FILE(miredo_conf * conf,FILE * stream)233 static bool miredo_conf_read_FILE (miredo_conf *conf, FILE *stream)
234 {
235 char lbuf[1056];
236 unsigned line = 0;
237
238 while (fgets (lbuf, sizeof (lbuf), stream) != NULL)
239 {
240 size_t len = strlen (lbuf) - 1;
241 line++;
242
243 if (lbuf[len] != '\n')
244 {
245 while (fgetc (stream) != '\n')
246 if (feof (stream) || ferror (stream))
247 break;
248
249 LogWarning (conf, _("Skipped overly long line %u"), line);
250 continue;
251 }
252
253 lbuf[len] = '\0';
254 char nbuf[32], vbuf[1024];
255
256 switch (sscanf (lbuf, " %31s %1023s", nbuf, vbuf))
257 {
258 case 2:
259 if ((*nbuf != '#') // comment
260 && !miredo_conf_set (conf, nbuf, vbuf, line))
261 return false;
262 break;
263
264 case 1:
265 if (*nbuf != '#')
266 LogWarning (conf, _("Ignoring line %u: %s"),
267 line, nbuf);
268 break;
269 }
270 }
271
272 if (ferror (stream))
273 {
274 LogError (conf, _("Error reading configuration file: %s"),
275 strerror (errno));
276 return false;
277 }
278 return true;
279 }
280
281
282 /* Parses a file.
283 *
284 * @return false on I/O error, true on success.
285 */
miredo_conf_read_file(miredo_conf * conf,const char * path)286 bool miredo_conf_read_file (miredo_conf *conf, const char *path)
287 {
288 assert (path != NULL);
289
290 FILE *stream = fopen (path, "r");
291 if (stream != NULL)
292 {
293 bool ret = miredo_conf_read_FILE (conf, stream);
294 fclose (stream);
295 return ret;
296 }
297
298 LogError (conf, _("Error opening configuration file %s: %s"), path,
299 strerror (errno));
300 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 */
miredo_conf_get_int16(miredo_conf * conf,const char * name,uint16_t * value,unsigned * line)311 bool miredo_conf_get_int16 (miredo_conf *conf, const char *name,
312 uint16_t *value, unsigned *line)
313 {
314 char *val = miredo_conf_get (conf, name, line);
315
316 if (val == NULL)
317 return true;
318
319 char *end;
320 unsigned long l;
321
322 l = strtoul (val, &end, 0);
323
324 if ((*end) || (l > 65535))
325 {
326 LogError (conf, _("Invalid integer value \"%s\" for %s: %s"),
327 val, name, strerror (errno));
328 free (val);
329 return false;
330 }
331 *value = (uint16_t)l;
332 free (val);
333 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 */
GetIPv4ByName(const char * hostname,uint32_t * ipv4)393 int GetIPv4ByName (const char *hostname, uint32_t *ipv4)
394 {
395 struct addrinfo help =
396 {
397 .ai_family = AF_INET,
398 .ai_socktype = SOCK_DGRAM,
399 .ai_protocol = IPPROTO_UDP
400 }, *res;
401
402 int check = getaddrinfo (hostname, NULL, &help, &res);
403 if (check)
404 return check;
405
406 *ipv4 = ((const struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr;
407 freeaddrinfo (res);
408 return 0;
409 }
410
411
miredo_conf_parse_IPv4(miredo_conf * conf,const char * name,uint32_t * ipv4)412 bool miredo_conf_parse_IPv4 (miredo_conf *conf, const char *name,
413 uint32_t *ipv4)
414 {
415 unsigned line;
416 char *val = miredo_conf_get (conf, name, &line);
417
418 if (val == NULL)
419 return true;
420
421 int check = GetIPv4ByName (val, ipv4);
422
423 if (check)
424 {
425 LogError (conf, _("Invalid hostname \"%s\" at line %u: %s"),
426 val, line, gai_strerror (check));
427 free (val);
428 return false;
429 }
430
431 free (val);
432 return true;
433 }
434
435
miredo_conf_parse_IPv6(miredo_conf * conf,const char * name,struct in6_addr * value)436 bool miredo_conf_parse_IPv6 (miredo_conf *conf, const char *name,
437 struct in6_addr *value)
438 {
439 unsigned line;
440 char *val = miredo_conf_get (conf, name, &line);
441
442 if (val == NULL)
443 return true;
444
445 struct addrinfo help =
446 {
447 .ai_family = AF_INET6,
448 .ai_socktype = SOCK_DGRAM,
449 .ai_protocol = IPPROTO_UDP
450 }, *res;
451
452 int check = getaddrinfo (val, NULL, &help, &res);
453
454 if (check)
455 {
456 LogError (conf, _("Invalid hostname \"%s\" at line %u: %s"),
457 val, line, gai_strerror (check));
458 free (val);
459 return false;
460 }
461
462 memcpy (value, &((const struct sockaddr_in6*)(res->ai_addr))->sin6_addr,
463 sizeof (struct in6_addr));
464
465 freeaddrinfo (res);
466 free (val);
467 return true;
468 }
469
470
miredo_conf_parse_teredo_prefix(miredo_conf * conf,const char * name,uint32_t * value)471 bool miredo_conf_parse_teredo_prefix (miredo_conf *conf, const char *name,
472 uint32_t *value)
473 {
474 union teredo_addr addr;
475 memset (&addr, 0, sizeof (addr));
476 addr.teredo.prefix = *value;
477
478 if (miredo_conf_parse_IPv6 (conf, name, &addr.ip6))
479 {
480 if (!is_valid_teredo_prefix (addr.teredo.prefix))
481 {
482 LogError (conf, _("Invalid Teredo IPv6 prefix: %x::/32"),
483 addr.teredo.prefix);
484 return false;
485 }
486
487 *value = addr.teredo.prefix;
488 return true;
489 }
490 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
miredo_conf_parse_syslog_facility(miredo_conf * conf,const char * name,int * facility)546 bool miredo_conf_parse_syslog_facility (miredo_conf *conf, const char *name,
547 int *facility)
548 {
549 unsigned line;
550 char *str = miredo_conf_get (conf, name, &line);
551
552 if (str == NULL)
553 return true;
554
555 for (const struct miredo_conf_syslog_facility *ptr = facilities;
556 ptr->str != NULL; ptr++)
557 {
558 if (!strcasecmp (str, ptr->str))
559 {
560 *facility = ptr->facility;
561 free (str);
562 return true;
563 }
564 }
565
566 LogError (conf, _("Unknown syslog facility \"%s\" at line %u"), str,
567 line);
568 free (str);
569 return false;
570 }
571