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