1 /*
2 * Copyright (c) 2015, Laurent COUSTET <ed@zehome.com>
3 *
4 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <errno.h>
32
33 #include "tool.h"
34 #include "configlib.h"
35 #include "mlvpn.h"
36 #include "log.h"
37
38 #define MAXLINE 1024
39
40 config_t *
_conf_parseConfig(int config_fd)41 _conf_parseConfig(int config_fd)
42 {
43 int size, i = 0;
44 int bufsize = 256;
45 unsigned int linenum = 0;
46 char c;
47 char *buf;
48 char *newline;
49 char *tmp;
50 char *section = NULL;
51 FILE *configFile;
52
53 config_t *config;
54 confObj_t *confObj;
55
56 configFile = fdopen(config_fd, "r");
57 if (! configFile)
58 {
59 log_warn("config", "cannot open file %d",
60 config_fd);
61 return NULL;
62 }
63 config = (config_t *)calloc(1, sizeof(config_t));
64 if (! config)
65 fatal("config", "calloc");
66 config->next = NULL;
67 config->section = NULL;
68 config->conf = NULL;
69
70 buf = (char *)calloc(1, bufsize);
71 if (! buf)
72 fatal("config", "calloc");
73
74 while (! feof(configFile))
75 {
76 size = fread(&c, 1, 1, configFile);
77 if (size <= 0)
78 {
79 if (feof(configFile))
80 break;
81 else
82 {
83 log_warn("config", "read error");
84 if (section != NULL)
85 free(section);
86 free(config);
87 free(buf);
88 fclose(configFile);
89 return NULL;
90 }
91 }
92
93 if (bufsize < i + 64)
94 {
95 bufsize += 64;
96 buf = realloc(buf, bufsize);
97 }
98
99 switch (c)
100 {
101 case '\r':
102 break; /* Do nothing */
103 case '\n':
104 linenum++;
105 buf[i] = 0;
106 newline = _conf_strip_comment(buf, i);
107
108 if (newline)
109 {
110 if ( (tmp = _conf_get_section(newline, i, linenum)) != NULL)
111 {
112 if (section != NULL)
113 free(section);
114
115 section = tmp;
116 } else if ( (confObj = _conf_parseLine(newline,
117 strlen(newline), linenum)) != NULL)
118 {
119 if (section == NULL)
120 {
121 log_warnx("config",
122 "error near line %d: variables should "
123 "always been defined in a section", linenum);
124 } else if (_conf_setValue(config, confObj, section) == NULL) {
125 /* Error there, cleanup memory */
126 if (confObj->var)
127 free(confObj->var);
128 if (confObj->val)
129 free(confObj->val);
130 free(confObj);
131 }
132 }
133 free(newline);
134 }
135
136 i = 0;
137 break;
138 default:
139 buf[i++] = c;
140 }
141 }
142
143 if (section)
144 free(section);
145 free(buf);
146 /* Do not close for fseeking */
147 fclose(configFile);
148 return config;
149 }
150
151 /*
152 * This function will strip comments
153 */
154 char *
_conf_strip_comment(char * line,unsigned int size)155 _conf_strip_comment(char *line, unsigned int size)
156 {
157 unsigned int i, j = 0;
158 short quote = 0;
159 char c;
160 char *new;
161
162 new = calloc(1, size + 1);
163 if (! new)
164 fatal("config", "new");
165 for (i = 0; i < size; i++)
166 {
167 c = line[i];
168
169 switch (c)
170 {
171 case '\"':
172 quote ^= 1; /* Nice :) */
173 new[j++] = '\"';
174 break;
175
176 case '#':
177 if (quote == 1)
178 {
179 new[j++] = c; /* Avoid counting "http://" */
180 break;
181 }
182 else
183 {
184 /* There is a comment there, remove it :) */
185 goto exit;
186 }
187
188 break;
189
190 default:
191 new[j++] = c;
192 break;
193 }
194 }
195
196 exit:
197 /* Make sure the char* is ended. */
198 if (j == 0)
199 {
200 free(new);
201 return NULL;
202 } else {
203 new[j] = '\0';
204 return new;
205 }
206 }
207
208 /*
209 * Returns section name if line is a section.
210 * Otherwise, returns NULL.
211 */
212 char *
_conf_get_section(char * line,unsigned int linelen,unsigned int linenum)213 _conf_get_section(char *line, unsigned int linelen, unsigned int linenum)
214 {
215 unsigned int i, j;
216 char *section = NULL;
217 char *errorMsg = NULL;
218 int found_terminator = 0;
219
220 for (i = 0, j = 0; i < linelen && !found_terminator; i++)
221 {
222 switch(line[i])
223 {
224 case '[':
225 if (section)
226 {
227 errorMsg = "parse error near line %d: '[' followed by another '['";
228 goto error;
229 }
230 section = (char *)calloc(1, linelen + 1 - i);
231 if (! section)
232 fatal("config", "calloc");
233 break;
234 case ']':
235 if (! section)
236 {
237 errorMsg = "parse error near line %d: ']' found, without '['";
238 goto error;
239 }
240 found_terminator = 1;
241 section[j] = 0;
242 break;
243 default:
244 if (section)
245 section[j++] = line[i];
246 break;
247 }
248 }
249
250 if (section && ! found_terminator)
251 {
252 errorMsg = "parse error near line %d: ending ']' not found";
253 goto error;
254 }
255
256 return section;
257 error:
258 if (section)
259 free(section);
260 if (errorMsg)
261 log_warnx("config", errorMsg, linenum);
262 return NULL;
263 }
264
265 /*
266 * Parse the line and returns a confObj_t object,
267 * or NULL if parse error.
268 */
269 confObj_t *
_conf_parseLine(char * line,unsigned int linelen,unsigned int linenum)270 _conf_parseLine(char *line, unsigned int linelen, unsigned int linenum)
271 {
272 unsigned int i, j, k;
273 int len;
274 int quote, space;
275 char *buf;
276 char c;
277 void *ptr;
278 confObj_t *confObj;
279
280 if ((line == NULL) || (*line == '\0'))
281 return NULL;
282
283 confObj = (confObj_t *)calloc(1, sizeof(confObj_t));
284 if (! confObj)
285 fatal("config", "calloc");
286 confObj->var = NULL;
287 confObj->val = NULL;
288
289 buf = calloc(1, linelen + 1);
290 if (! buf)
291 fatal("config", "calloc");
292
293 /* First step: getting variable */
294 for (i = 0, quote = 0, space = 0, j = 0; i < linelen; i++)
295 {
296 c = line[i];
297
298 switch (c)
299 {
300 case '\"':
301 quote ^= 1;
302 break;
303
304 case '=':
305 if (quote) {
306 buf[j++] = c;
307 continue;
308 }
309 if (j == 0)
310 {
311 log_warnx("config",
312 "parse error near line %d: line should not start with '='",
313 linenum);
314 free(confObj);
315 free(buf);
316 return NULL;
317 }
318
319 if (confObj->var != NULL)
320 {
321 log_warnx("config",
322 "parse error near line %d: two '=' are not permitted",
323 linenum);
324 free(confObj->var);
325 free(confObj);
326 free(buf);
327 return NULL;
328 }
329
330 buf[j] = 0;
331 len = j;
332 j = 0;
333
334 /* Strip ending spaces */
335 for (k = len-1; (k > 0) && (buf[k] == ' '); k--);
336 buf[k+1] = 0;
337
338 confObj->var = strdup(buf);
339 break;
340
341 default:
342 if ((c == ' ') && (space == 0))
343 space = 1;
344 else if (c != ' ')
345 space = 0;
346
347 if ((space == 1) && (quote == 0))
348 break; /* Discards */
349 if ((space == 1) && (j == 0))
350 break; /* Discards */
351
352 if (! isascii(c))
353 {
354 log_warnx("config",
355 "parse error near line %d: "
356 "variable/value must be ASCII",
357 linenum);
358 free(buf);
359 if (confObj->var)
360 free(confObj->var);
361 free(confObj);
362 return NULL;
363 }
364 buf[j++] = c;
365 }
366 }
367
368 if ((j == 0) || (confObj->var == NULL))
369 {
370 /* _ERROR("Parse error near line %d: Variable not found.\n", linenum); */
371 free(confObj);
372 free(buf);
373 return NULL;
374 }
375
376 buf[j] = 0;
377 len = j;
378
379 /* Ugly strip */
380 for (k = len-1; (k > 0) && (buf[k] == ' '); k--);
381 buf[k+1] = 0;
382 len = k;
383
384 /* Backup */
385 ptr = buf;
386
387 for (k = 0; (k < len) && (buf[k] == ' '); k++) buf++;
388
389 if (! *buf)
390 {
391 log_warnx("config",
392 "parse error near line %d: no value",
393 linenum);
394 free(ptr);
395 free(confObj->var);
396 free(confObj);
397 return NULL;
398 }
399
400 confObj->val = strdup(buf);
401
402 /* Some cleanup */
403 free(ptr);
404
405 return confObj;
406 }
407
408 /* Private stuff */
409 config_t *
_conf_setValue(config_t * start,confObj_t * confObj,const char * section)410 _conf_setValue(config_t *start,
411 confObj_t *confObj,
412 const char *section)
413 {
414 config_t *work;
415 config_t *last;
416
417 if (start == NULL)
418 {
419 log_warnx("config",
420 "unable to set value: no sections");
421 return NULL;
422 }
423
424 if (section == NULL)
425 {
426 log_warnx("config",
427 "unable to set value: no sections");
428 return NULL;
429 }
430
431 if (start->conf == NULL)
432 {
433 start->conf = confObj;
434 start->section = strdup(section);
435 start->next = NULL; /* Useless, but safe :) */
436 } else {
437 work = (config_t *)calloc(1, sizeof(config_t));
438 if (! work)
439 fatal("config", "calloc");
440 work->next = NULL;
441 work->section = strdup(section);
442 work->conf = confObj;
443 last = start;
444 while (last->next != NULL)
445 last = last->next;
446
447 last->next = work;
448 }
449
450 return start;
451 }
452
453 /* Public stuff :) */
454 void
conf_setValue(config_t ** start,const char * var,const char * val,const char * section)455 conf_setValue( config_t **start,
456 const char *var,
457 const char *val,
458 const char *section )
459 {
460 confObj_t *obj;
461
462 if ((var == NULL) || (val == NULL))
463 {
464 log_warnx("config", "cannot set value: no variable or no value");
465 return;
466 }
467
468 if ((*start) == NULL)
469 {
470 (*start) = (config_t *)calloc(1, sizeof(config_t));
471 if (! *start)
472 fatal("config", "calloc");
473 (*start)->next = NULL;
474 (*start)->section = NULL;
475 (*start)->conf = NULL;
476 }
477
478 obj = (confObj_t *)calloc(1, sizeof(confObj_t));
479 if (! obj)
480 fatal("config", "calloc");
481 obj->var = strdup(var);
482 obj->val = strdup(val);
483
484 (*start) = _conf_setValue( *start, obj, section );
485 if (*start == NULL) {
486 free(obj->var);
487 free(obj->val);
488 free(obj);
489 }
490 }
491
492 /*
493 * This function will walk thru config_t *start
494 * and affect dest to the value of var in config file.
495 */
496 config_t *
_conf_getValue(config_t * start,const char * section,const char * var,char ** dest)497 _conf_getValue(config_t *start,
498 const char *section,
499 const char *var,
500 char **dest )
501 {
502 while (start != NULL)
503 {
504 if ((start->conf == NULL) ||
505 (start->conf->var == NULL) ||
506 (! mystr_eq(start->section, section)))
507 goto next;
508
509 if (mystr_eq(start->conf->var, var) == 1)
510 {
511 *dest = strdup(start->conf->val);
512 return start->next;
513 }
514 next:
515 start = start->next;
516 }
517
518 *dest = NULL;
519 return NULL;
520 }
521
522 void
_conf_printConfig(config_t * start)523 _conf_printConfig(config_t *start)
524 {
525 config_t *tmp = start;
526 while (tmp != NULL)
527 {
528 if (tmp->conf)
529 printf("section: %s, var: `%s' val: `%s'\n",
530 tmp->section, tmp->conf->var, tmp->conf->val);
531 tmp = tmp->next;
532 }
533 }
534
535 void
_conf_freeConfig(config_t * start)536 _conf_freeConfig(config_t *start)
537 {
538 config_t *old;
539
540 while (start != NULL)
541 {
542 if (start->conf != NULL)
543 {
544 if (start->conf->var != NULL)
545 free(start->conf->var);
546 if (start->conf->val != NULL)
547 free(start->conf->val);
548
549 free(start->conf);
550 }
551
552 if (start->section != NULL)
553 free(start->section);
554
555 old = start;
556 start = start->next;
557 free(old);
558 }
559 }
560
561 void
_conf_set_str_from_conf(config_t * config,const char * section,const char * type,char ** value,const char * def,const char * errMsg,int exit_n)562 _conf_set_str_from_conf(config_t *config,
563 const char *section,
564 const char *type,
565 char **value,
566 const char *def,
567 const char *errMsg,
568 int exit_n)
569 {
570 _conf_getValue(config, section, type, value);
571
572 if (*value == NULL)
573 {
574 if (exit_n > 0)
575 fatalx(errMsg);
576 if (errMsg)
577 log_warnx("config", "%s", errMsg);
578 if (def != NULL)
579 *value = strdup(def);
580 }
581 }
582
583 void
_conf_set_uint_from_conf(config_t * config,const char * section,const char * type,uint32_t * value,uint32_t def,const char * errMsg,int exit_n)584 _conf_set_uint_from_conf(config_t *config, const char *section,
585 const char *type, uint32_t *value, uint32_t def, const char *errMsg,
586 int exit_n)
587 {
588 char *tmp;
589 _conf_getValue(config, section, type, &tmp);
590
591 if ( tmp == NULL )
592 {
593 if (exit_n > 0)
594 fatalx(errMsg);
595 if (errMsg)
596 log_warnx("config", "%s", errMsg);
597 *value = def;
598 } else {
599 *value = atoi(tmp);
600 free(tmp);
601 }
602 }
603
604 void
_conf_set_bool_from_conf(config_t * config,const char * section,const char * type,int * value,int def,const char * errMsg,int exit_n)605 _conf_set_bool_from_conf(config_t *config,
606 const char *section,
607 const char *type,
608 int *value,
609 int def,
610 const char *errMsg,
611 int exit_n)
612 {
613 char *tmp;
614 _conf_getValue(config, section, type, &tmp);
615
616 if ( tmp == NULL )
617 {
618 if (exit_n > 0)
619 fatalx(errMsg);
620 if (errMsg)
621 log_warnx("config", "%s", errMsg);
622 *value = def;
623 } else {
624 *value = atoi(tmp);
625 if ( (*value) != 1)
626 *value = 0;
627 free(tmp);
628 }
629 }
630