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