Use readline() in read_config_file() instead of fgets.
[tinc] / src / conf.c
1 /*
2     conf.c -- configuration code
3     Copyright (C) 1998 Robert van der Meulen
4     Copyright (C) 1998,1999,2000 Ivo Timmermans <itimmermans@bigfoot.com>
5                             2000 Guus Sliepen <guus@sliepen.warande.net>
6                             2000 Cris van Pelt <tribbel@arise.dhs.org>
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: conf.c,v 1.9.4.25 2000/11/29 14:27:24 zarq Exp $
23 */
24
25 #include "config.h"
26
27 #include <ctype.h>
28 #include <errno.h>
29 #include <netdb.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <syslog.h>
34
35 #include <xalloc.h>
36 #include <utils.h> /* for cp */
37
38 #include "conf.h"
39 #include "netutl.h" /* for strtoip */
40
41 #include "system.h"
42
43 config_t *config = NULL;
44 int debug_lvl = 0;
45 int timeout = 0; /* seconds before timeout */
46 char *confbase = NULL;           /* directory in which all config files are */
47 char *netname = NULL;            /* name of the vpn network */
48
49 /* Will be set if HUP signal is received. It will be processed when it is safe. */
50 int sighup = 0;
51
52 /*
53   These are all the possible configurable values
54 */
55 static internal_config_t hazahaza[] = {
56 /* Main configuration file keywords */
57   { "Name",         config_name,       TYPE_NAME },
58   { "ConnectTo",    config_connectto,      TYPE_NAME },
59   { "PingTimeout",  config_pingtimeout,    TYPE_INT },
60   { "TapDevice",    config_tapdevice,      TYPE_NAME },
61   { "PrivateKey",   config_privatekey,     TYPE_NAME },
62   { "KeyExpire",    config_keyexpire,      TYPE_INT },
63   { "Hostnames",    config_hostnames,    TYPE_BOOL },
64   { "Interface",    config_interface,      TYPE_NAME },
65   { "InterfaceIP",  config_interfaceip,    TYPE_IP },
66 /* Host configuration file keywords */
67   { "Address",      config_address,        TYPE_NAME },
68   { "Port",         config_port,           TYPE_INT },
69   { "PublicKey",    config_publickey,      TYPE_NAME },
70   { "Subnet",       config_subnet,         TYPE_IP },           /* Use IPv4 subnets only for now */
71   { "RestrictHosts", config_restricthosts, TYPE_BOOL },
72   { "RestrictSubnets", config_restrictsubnets, TYPE_BOOL },
73   { "RestrictAddress", config_restrictaddress, TYPE_BOOL },
74   { "RestrictPort", config_restrictport,   TYPE_BOOL },
75   { "IndirectData", config_indirectdata,   TYPE_BOOL },
76   { "TCPonly",      config_tcponly,        TYPE_BOOL },
77   { NULL, 0, 0 }
78 };
79
80 /*
81   Add given value to the list of configs cfg
82 */
83 config_t *
84 add_config_val(config_t **cfg, int argtype, char *val)
85 {
86   config_t *p;
87   char *q;
88 cp
89   p = (config_t*)xmalloc(sizeof(*p));
90   p->data.val = 0;
91
92   switch(argtype)
93     {
94     case TYPE_INT:
95       p->data.val = strtol(val, &q, 0);
96       if(q && *q)
97         p->data.val = 0;
98       break;
99     case TYPE_NAME:
100       p->data.ptr = xmalloc(strlen(val) + 1);
101       strcpy(p->data.ptr, val);
102       break;
103     case TYPE_IP:
104       p->data.ip = strtoip(val);
105       break;
106     case TYPE_BOOL:
107       if(!strcasecmp("yes", val))
108         p->data.val = stupid_true;
109       else if(!strcasecmp("no", val))
110         p->data.val = stupid_false;
111       else
112         p->data.val = 0;
113     }
114
115   p->argtype = argtype;
116
117   if(p->data.val)
118     {
119       p->next = *cfg;
120       *cfg = p;
121 cp
122       return p;
123     }
124   else
125     {
126       free(p);
127 cp
128       return NULL;
129     }
130 }
131
132 /*
133   Read exactly one line and strip the trailing newline if any.  If the
134   file was on EOF, return NULL. Otherwise, return all the data in a
135   dynamically allocated buffer.
136 */
137 char *readline(FILE *fp)
138 {
139   char *newline = NULL;
140   char *p;
141   char *line; /* The array that contains everything that has been read
142                  so far */
143   char *idx; /* Read into this pointer, which points to an offset
144                 within line */
145   size_t size, newsize; /* The size of the current array pointed to by
146                            line */
147   size_t maxlen; /* Maximum number of characters that may be read with
148                     fgets.  This is newsize - oldsize. */
149
150   if(feof(fp))
151     return NULL;
152   
153   size = 100;
154   maxlen = size;
155   line = xmalloc(size);
156   idx = line;
157   for(;;)
158     {
159       errno = 0;
160       p = fgets(idx, maxlen, fp);
161       if(p == NULL)  /* EOF or error */
162         {
163           if(feof(fp))
164             break;
165
166           /* otherwise: error; let the calling function print an error
167              message if applicable */
168           free(line);
169           return NULL;
170         }
171
172       newline = strchr(p, '\n');
173       if(newline == NULL)
174         /* We haven't yet read everything to the end of the line */
175         {
176           newsize = size << 1;
177           line = xrealloc(line, newsize);
178           idx = &line[size - 1];
179           maxlen = newsize - size + 1;
180           size = newsize;
181         }
182       else
183         {
184           *newline = '\0'; /* kill newline */
185           break;  /* yay */
186         }
187     }
188
189   return line;
190 }
191
192 /*
193   Parse a configuration file and put the results in the configuration tree
194   starting at *base.
195 */
196 int read_config_file(config_t **base, const char *fname)
197 {
198   int err = -1;
199   FILE *fp;
200   char *line;
201   char *p, *q;
202   int i, lineno = 0;
203   config_t *cfg;
204 cp
205   if((fp = fopen (fname, "r")) == NULL)
206     {
207       return -1;
208     }
209
210   for(;;)
211     {
212       if((line = readline(fp)) == NULL)
213         {
214           err = -1;
215           break;
216         }
217         
218       lineno++;
219
220       if((p = strtok(line, "\t =")) == NULL)
221         continue; /* no tokens on this line */
222
223       if(p[0] == '#')
224         continue; /* comment: ignore */
225
226       for(i = 0; hazahaza[i].name != NULL; i++)
227         if(!strcasecmp(hazahaza[i].name, p))
228           break;
229
230       if(!hazahaza[i].name)
231         {
232           syslog(LOG_ERR, _("Invalid variable name on line %d while reading config file %s"),
233                   lineno, fname);
234           break;
235         }
236
237       if(((q = strtok(NULL, "\t\n\r =")) == NULL) || q[0] == '#')
238         {
239           fprintf(stderr, _("No value for variable on line %d while reading config file %s"),
240                   lineno, fname);
241           break;
242         }
243
244       cfg = add_config_val(base, hazahaza[i].argtype, q);
245       if(cfg == NULL)
246         {
247           fprintf(stderr, _("Invalid value for variable on line %d while reading config file %s"),
248                   lineno, fname);
249           break;
250         }
251
252       cfg->which = hazahaza[i].which;
253       if(!config)
254         config = cfg;
255     }
256
257   fclose (fp);
258 cp
259   return err;
260 }
261
262 int read_server_config()
263 {
264   char *fname;
265   int x;
266 cp
267   asprintf(&fname, "%s/tinc.conf", confbase);
268   x = read_config_file(&config, fname);
269   if(x != 0)
270     {
271       fprintf(stderr, _("Failed to read `%s': %m\n"),
272               fname);
273     }
274   free(fname);
275 cp
276   return x;  
277 }
278
279 /*
280   Look up the value of the config option type
281 */
282 const config_t *get_config_val(config_t *p, which_t type)
283 {
284 cp
285   for(; p != NULL; p = p->next)
286     if(p->which == type)
287       break;
288 cp
289   return p;
290 }
291
292 /*
293   Remove the complete configuration tree.
294 */
295 void clear_config(config_t **base)
296 {
297   config_t *p, *next;
298 cp
299   for(p = *base; p != NULL; p = next)
300     {
301       next = p->next;
302       if(p->data.ptr && (p->argtype == TYPE_NAME))
303         {
304           free(p->data.ptr);
305         }
306       free(p);
307     }
308   *base = NULL;
309 cp
310 }
311
312 #define is_safe_file(p) 1
313
314 FILE *ask_and_safe_open(const char* filename, const char* what)
315 {
316   FILE *r;
317   char *directory;
318   char *fn;
319   int len;
320
321   /* Check stdin and stdout */
322   if(!isatty(0) || !isatty(1))
323     {
324       /* Argh, they are running us from a script or something.  Write
325          the files to the current directory and let them burn in hell
326          for ever. */
327       fn = xstrdup(filename);
328     }
329   else
330     {
331       /* Ask for a file and/or directory name. */
332       fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
333               what, filename);
334       fflush(stdout);  /* Don't wait for a newline */
335       if((fn = readline(stdin)) == NULL)
336         {
337           fprintf(stderr, _("Error while reading stdin: %m\n"));
338           return NULL;
339         }
340       if(strlen(fn) == 0)
341         /* User just pressed enter. */
342         fn = xstrdup(filename);
343     }
344
345   if((strchr(fn, '/') == NULL) || (fn[0] != '/'))
346     {
347       /* The directory is a relative path or a filename. */
348       char *p;
349       
350       directory = get_current_dir_name();
351       len = strlen(fn) + strlen(directory) + 2; /* 1 for the / */
352       p = xmalloc(len);
353       snprintf(p, len, "%s/%s", directory, fn);
354       free(fn);
355       fn = p;
356     }
357
358   if(!is_safe_file(fn))
359     {
360       fprintf(stderr, _("The file `%s' (or any of the leading directories) has unsafe permissions.\n"
361                         "I will not create or overwrite this file.\n"),
362                         fn);
363       return NULL;
364     }
365
366   if((r = fopen(fn, "w")) == NULL)
367     {
368       fprintf(stderr, _("Error opening file `%s': %m\n"),
369               fn);
370     }
371
372   free(fn);
373   free(directory);
374   
375   return r;
376 }