568103373c9a5a1612c22cfa779143a5a04ce07c
[tinc] / src / conf.c
1 /*
2     conf.c -- configuration code
3     Copyright (C) 1998 Robert van der Meulen
4                   1998-2005 Ivo Timmermans
5                   2000-2006 Guus Sliepen <guus@tinc-vpn.org>
6                   2000 Cris van Pelt
7
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17
18     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22     $Id$
23 */
24
25 #include "system.h"
26
27 #include "avl_tree.h"
28 #include "conf.h"
29 #include "logger.h"
30 #include "netutl.h"                             /* for str2address */
31 #include "utils.h"                              /* for cp */
32 #include "xalloc.h"
33
34 avl_tree_t *config_tree;
35
36 int pinginterval = 0;                   /* seconds between pings */
37 int pingtimeout = 0;                    /* seconds to wait for response */
38 char *confbase = NULL;                  /* directory in which all config files are */
39 char *netname = NULL;                   /* name of the vpn network */
40
41 static int config_compare(const config_t *a, const config_t *b)
42 {
43         int result;
44
45         result = strcasecmp(a->variable, b->variable);
46
47         if(result)
48                 return result;
49
50         result = a->line - b->line;
51
52         if(result)
53                 return result;
54         else
55                 return strcmp(a->file, b->file);
56 }
57
58 void init_configuration(avl_tree_t ** config_tree)
59 {
60         cp();
61
62         *config_tree = avl_alloc_tree((avl_compare_t) config_compare, (avl_action_t) free_config);
63 }
64
65 void exit_configuration(avl_tree_t ** config_tree)
66 {
67         cp();
68
69         avl_delete_tree(*config_tree);
70         *config_tree = NULL;
71 }
72
73 config_t *new_config(void)
74 {
75         cp();
76
77         return xmalloc_and_zero(sizeof(config_t));
78 }
79
80 void free_config(config_t *cfg)
81 {
82         cp();
83
84         if(cfg->variable)
85                 free(cfg->variable);
86
87         if(cfg->value)
88                 free(cfg->value);
89
90         if(cfg->file)
91                 free(cfg->file);
92
93         free(cfg);
94 }
95
96 void config_add(avl_tree_t *config_tree, config_t *cfg)
97 {
98         cp();
99
100         avl_insert(config_tree, cfg);
101 }
102
103 config_t *lookup_config(avl_tree_t *config_tree, char *variable)
104 {
105         config_t cfg, *found;
106
107         cp();
108
109         cfg.variable = variable;
110         cfg.file = "";
111         cfg.line = 0;
112
113         found = avl_search_closest_greater(config_tree, &cfg);
114
115         if(!found)
116                 return NULL;
117
118         if(strcasecmp(found->variable, variable))
119                 return NULL;
120
121         return found;
122 }
123
124 config_t *lookup_config_next(avl_tree_t *config_tree, const config_t *cfg)
125 {
126         avl_node_t *node;
127         config_t *found;
128
129         cp();
130
131         node = avl_search_node(config_tree, cfg);
132
133         if(node) {
134                 if(node->next) {
135                         found = node->next->data;
136
137                         if(!strcasecmp(found->variable, cfg->variable))
138                                 return found;
139                 }
140         }
141
142         return NULL;
143 }
144
145 bool get_config_bool(const config_t *cfg, bool *result)
146 {
147         cp();
148
149         if(!cfg)
150                 return false;
151
152         if(!strcasecmp(cfg->value, "yes")) {
153                 *result = true;
154                 return true;
155         } else if(!strcasecmp(cfg->value, "no")) {
156                 *result = false;
157                 return true;
158         }
159
160         logger(LOG_ERR, _("\"yes\" or \"no\" expected for configuration variable %s in %s line %d"),
161                    cfg->variable, cfg->file, cfg->line);
162
163         return false;
164 }
165
166 bool get_config_int(const config_t *cfg, int *result)
167 {
168         cp();
169
170         if(!cfg)
171                 return false;
172
173         if(sscanf(cfg->value, "%d", result) == 1)
174                 return true;
175
176         logger(LOG_ERR, _("Integer expected for configuration variable %s in %s line %d"),
177                    cfg->variable, cfg->file, cfg->line);
178
179         return false;
180 }
181
182 bool get_config_string(const config_t *cfg, char **result)
183 {
184         cp();
185
186         if(!cfg)
187                 return false;
188
189         *result = xstrdup(cfg->value);
190
191         return true;
192 }
193
194 bool get_config_address(const config_t *cfg, struct addrinfo **result)
195 {
196         struct addrinfo *ai;
197
198         cp();
199
200         if(!cfg)
201                 return false;
202
203         ai = str2addrinfo(cfg->value, NULL, 0);
204
205         if(ai) {
206                 *result = ai;
207                 return true;
208         }
209
210         logger(LOG_ERR, _("Hostname or IP address expected for configuration variable %s in %s line %d"),
211                    cfg->variable, cfg->file, cfg->line);
212
213         return false;
214 }
215
216 bool get_config_subnet(const config_t *cfg, subnet_t ** result)
217 {
218         subnet_t subnet = {0};
219
220         cp();
221
222         if(!cfg)
223                 return false;
224
225         if(!str2net(&subnet, cfg->value)) {
226                 logger(LOG_ERR, _("Subnet expected for configuration variable %s in %s line %d"),
227                            cfg->variable, cfg->file, cfg->line);
228                 return false;
229         }
230
231         /* Teach newbies what subnets are... */
232
233         if(((subnet.type == SUBNET_IPV4)
234                 && !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof(ipv4_t)))
235                 || ((subnet.type == SUBNET_IPV6)
236                 && !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof(ipv6_t)))) {
237                 logger(LOG_ERR, _ ("Network address and prefix length do not match for configuration variable %s in %s line %d"),
238                            cfg->variable, cfg->file, cfg->line);
239                 return false;
240         }
241
242         *(*result = new_subnet()) = subnet;
243
244         return true;
245 }
246
247 /*
248   Read exactly one line and strip the trailing newline if any.  If the
249   file was on EOF, return NULL. Otherwise, return all the data in a
250   dynamically allocated buffer.
251
252   If line is non-NULL, it will be used as an initial buffer, to avoid
253   unnecessary mallocing each time this function is called.  If buf is
254   given, and buf needs to be expanded, the var pointed to by buflen
255   will be increased.
256 */
257 static char *readline(FILE * fp, char **buf, size_t *buflen)
258 {
259         char *newline = NULL;
260         char *p;
261         char *line;                                     /* The array that contains everything that has been read so far */
262         char *idx;                                      /* Read into this pointer, which points to an offset within line */
263         size_t size, newsize;           /* The size of the current array pointed to by line */
264         size_t maxlen;                          /* Maximum number of characters that may be read with fgets.  This is newsize - oldsize. */
265
266         if(feof(fp))
267                 return NULL;
268
269         if(buf && buflen) {
270                 size = *buflen;
271                 line = *buf;
272         } else {
273                 size = 100;
274                 line = xmalloc(size);
275         }
276
277         maxlen = size;
278         idx = line;
279         *idx = 0;
280
281         for(;;) {
282                 errno = 0;
283                 p = fgets(idx, maxlen, fp);
284
285                 if(!p) {                                /* EOF or error */
286                         if(feof(fp))
287                                 break;
288
289                         /* otherwise: error; let the calling function print an error message if applicable */
290                         free(line);
291                         return NULL;
292                 }
293
294                 newline = strchr(p, '\n');
295
296                 if(!newline) {                  /* We haven't yet read everything to the end of the line */
297                         newsize = size << 1;
298                         line = xrealloc(line, newsize);
299                         idx = &line[size - 1];
300                         maxlen = newsize - size + 1;
301                         size = newsize;
302                 } else {
303                         *newline = '\0';        /* kill newline */
304                         break;                          /* yay */
305                 }
306         }
307
308         if(buf && buflen) {
309                 *buflen = size;
310                 *buf = line;
311         }
312
313         return line;
314 }
315
316 /*
317   Parse a configuration file and put the results in the configuration tree
318   starting at *base.
319 */
320 int read_config_file(avl_tree_t *config_tree, const char *fname)
321 {
322         int err = -2;                           /* Parse error */
323         FILE *fp;
324         char *buffer, *line;
325         char *variable, *value, *eol;
326         int lineno = 0;
327         int len;
328         bool ignore = false;
329         config_t *cfg;
330         size_t bufsize;
331
332         cp();
333
334         fp = fopen(fname, "r");
335
336         if(!fp) {
337                 logger(LOG_ERR, _("Cannot open config file %s: %s"), fname,
338                            strerror(errno));
339                 return -3;
340         }
341
342         bufsize = 100;
343         buffer = xmalloc(bufsize);
344
345         for(;;) {
346                 line = readline(fp, &buffer, &bufsize);
347
348                 if(!line) {
349                         err = -1;
350                         break;
351                 }
352
353                 if(feof(fp)) {
354                         err = 0;
355                         break;
356                 }
357
358                 lineno++;
359
360                 if(!*line || *line == '#')
361                         continue;
362
363                 if(ignore) {
364                         if(!strncmp(line, "-----END", 8))
365                                 ignore = false;
366                         continue;
367                 }
368                 
369                 if(!strncmp(line, "-----BEGIN", 10)) {
370                         ignore = true;
371                         continue;
372                 }
373
374                 variable = value = line;
375
376                 eol = line + strlen(line);
377                 while(strchr("\t ", *--eol))
378                         *eol = '\0';
379
380                 len = strcspn(value, "\t =");
381                 value += len;
382                 value += strspn(value, "\t ");
383                 if(*value == '=') {
384                         value++;
385                         value += strspn(value, "\t ");
386                 }
387                 variable[len] = '\0';
388
389         
390                 if(!*value) {
391                         logger(LOG_ERR, _("No value for variable `%s' on line %d while reading config file %s"),
392                                    variable, lineno, fname);
393                         break;
394                 }
395
396                 cfg = new_config();
397                 cfg->variable = xstrdup(variable);
398                 cfg->value = xstrdup(value);
399                 cfg->file = xstrdup(fname);
400                 cfg->line = lineno;
401
402                 config_add(config_tree, cfg);
403         }
404
405         free(buffer);
406         fclose(fp);
407
408         return err;
409 }
410
411 bool read_server_config()
412 {
413         char *fname;
414         int x;
415
416         cp();
417
418         asprintf(&fname, "%s/tinc.conf", confbase);
419         x = read_config_file(config_tree, fname);
420
421         if(x == -1) {                           /* System error: complain */
422                 logger(LOG_ERR, _("Failed to read `%s': %s"), fname, strerror(errno));
423         }
424
425         free(fname);
426
427         return x == 0;
428 }
429
430 FILE *ask_and_open(const char *filename, const char *what, const char *mode)
431 {
432         FILE *r;
433         char *directory;
434         char *fn;
435
436         /* Check stdin and stdout */
437         if(!isatty(0) || !isatty(1)) {
438                 /* Argh, they are running us from a script or something.  Write
439                    the files to the current directory and let them burn in hell
440                    for ever. */
441                 fn = xstrdup(filename);
442         } else {
443                 /* Ask for a file and/or directory name. */
444                 fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
445                                 what, filename);
446                 fflush(stdout);
447
448                 fn = readline(stdin, NULL, NULL);
449
450                 if(!fn) {
451                         fprintf(stderr, _("Error while reading stdin: %s\n"),
452                                         strerror(errno));
453                         return NULL;
454                 }
455
456                 if(!strlen(fn))
457                         /* User just pressed enter. */
458                         fn = xstrdup(filename);
459         }
460
461 #ifdef HAVE_MINGW
462         if(fn[0] != '\\' && fn[0] != '/' && !strchr(fn, ':')) {
463 #else
464         if(fn[0] != '/') {
465 #endif
466                 /* The directory is a relative path or a filename. */
467                 char *p;
468
469                 directory = get_current_dir_name();
470                 asprintf(&p, "%s/%s", directory, fn);
471                 free(fn);
472                 free(directory);
473                 fn = p;
474         }
475
476         umask(0077);                            /* Disallow everything for group and other */
477
478         /* Open it first to keep the inode busy */
479
480         r = fopen(fn, mode);
481
482         if(!r) {
483                 fprintf(stderr, _("Error opening file `%s': %s\n"),
484                                 fn, strerror(errno));
485                 free(fn);
486                 return NULL;
487         }
488
489         free(fn);
490
491         return r;
492 }