1 /*
2  * ffproxy (c) 2002-2004 Niklas Olmes <niklas@noxa.de>
3  * http://faith.eu.org
4  *
5  * $Id: db.c,v 2.1 2004/12/31 08:59:15 niklas Exp $
6  *
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free
9  * Software Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program; if not, write to the Free Software Foundation, Inc., 675
19  * Mass Ave, Cambridge, MA 02139, USA.
20  */
21 
22 #include "configure.h"
23 #ifdef HAVE_SYS_TYPES_H
24 # include <sys/types.h>
25 #endif
26 
27 #include <stdio.h>
28 #ifdef HAVE_STDLIB_H
29 # include <stdlib.h>
30 #endif
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34 #include <string.h>
35 #include <regex.h>
36 #include <pwd.h>
37 #include <grp.h>
38 
39 #include "cfg.h"
40 #include "print.h"
41 #include "msg.h"
42 #include "alloc.h"
43 #include "file.h"
44 #include "db.h"
45 
46 static void     clear_databases(void);
47 static void     clear_db(char *[]);
48 static void     clear_rdb(regex_t *[]);
49 static void     read_db(const char *, char *[]);
50 static void     read_rdb(const char *, regex_t *[]);
51 static void     read_file(const char *, struct msg *);
52 static void	read_config_file(void);
53 static void	verify_config(void);
54 
55 #define MAX_E 256
56 regex_t        *a_ip[MAX_E];
57 regex_t        *a_host[MAX_E];
58 char           *a_dyndns[MAX_E];
59 regex_t        *f_host[MAX_E];
60 regex_t        *f_url[MAX_E];
61 regex_t        *f_hdr_drop[MAX_E];
62 regex_t        *f_hdr_match[MAX_E];
63 char           *f_hdr_entry[MAX_E];
64 char           *f_hdr_add[MAX_E];
65 regex_t        *f_rhdr_drop[MAX_E];
66 regex_t        *f_rhdr_match[MAX_E];
67 char           *f_rhdr_entry[MAX_E];
68 struct msg      e_inv;
69 struct msg      e_res;
70 struct msg      e_con;
71 struct msg      e_post;
72 struct msg      e_fil;
73 
74 
75 void
reload_databases(void)76 reload_databases(void)
77 {
78 	clear_databases();
79 	load_databases();
80 }
81 
82 void
load_databases(void)83 load_databases(void)
84 {
85 	extern struct cfg config;
86 
87 	read_config_file();
88 	verify_config();
89 
90 	if (*config.dbdir != '\0' && chdir(config.dbdir) != 0)
91 		fatal("could not chdir() to dbdir (%s)", config.dbdir);
92 
93 	read_rdb("db/access.ip", a_ip);
94 	read_rdb("db/access.host", a_host);
95 	read_db("db/access.dyndns", a_dyndns);
96 	read_rdb("db/filter.host.match", f_host);
97 	read_rdb("db/filter.url.match", f_url);
98 	read_rdb("db/filter.header.drop", f_hdr_drop);
99 	read_rdb("db/filter.header.match", f_hdr_match);
100 	read_db("db/filter.header.entry", f_hdr_entry);
101 	read_db("db/filter.header.add", f_hdr_add);
102 	read_rdb("db/filter.rheader.drop", f_rhdr_drop);
103 	read_rdb("db/filter.rheader.match", f_rhdr_match);
104 	read_db("db/filter.rheader.entry", f_rhdr_entry);
105 	read_file("html/invalid", &e_inv);
106 	read_file("html/resolve", &e_res);
107 	read_file("html/connect", &e_con);
108 	read_file("html/post", &e_post);
109 	read_file("html/filtered", &e_fil);
110 }
111 
112 static void
clear_databases(void)113 clear_databases(void)
114 {
115 	clear_rdb(a_ip);
116 	clear_rdb(a_host);
117 	clear_db(a_dyndns);
118 	clear_rdb(f_host);
119 	clear_rdb(f_url);
120 	clear_rdb(f_hdr_drop);
121 	clear_rdb(f_hdr_match);
122 	clear_db(f_hdr_entry);
123 	clear_db(f_hdr_add);
124 	clear_rdb(f_rhdr_drop);
125 	clear_rdb(f_rhdr_match);
126 	clear_db(f_rhdr_entry);
127 	free(e_inv.c);
128 	free(e_res.c);
129 	free(e_con.c);
130 	free(e_post.c);
131 	free(e_fil.c);
132 	e_inv.len = e_res.len = e_con.len = e_post.len = e_fil.len = 0;
133 }
134 
135 static void
clear_db(char * db[])136 clear_db(char *db[])
137 {
138 	int             i;
139 
140 	i = 0;
141 	while (db[i] != NULL) {
142 		free(db[i]);
143 		db[i++] = NULL;
144 	}
145 }
146 
147 static void
clear_rdb(regex_t * r[])148 clear_rdb(regex_t * r[])
149 {
150 	int             i;
151 
152 	i = 0;
153 	while (r[i] != NULL) {
154 		regfree(r[i]);
155 		free(r[i]);
156 		r[i++] = NULL;
157 	}
158 }
159 
160 static void
read_db(const char * f,char * db[])161 read_db(const char *f, char *db[])
162 {
163 	FILE           *fp;
164 	char            buf[512], *p;
165 	size_t          i;
166 
167 	fp = my_fopen(f);
168 
169 	i = 0;
170 	while (fgets(buf, sizeof(buf), fp) != NULL && i < MAX_E - 1) {
171 		if (buf[0] == '#' || buf[0] == '\r' || buf[0] == '\n')
172 			continue;
173 		if ((p = strchr(buf, '\n')) == NULL) {
174 			(void) fclose(fp);
175 			fatal_n("line too long in file %s", f);
176 		}
177 		*p = '\0';
178 		p = (char *) my_alloc(strlen(buf) + 1);
179 		strcpy(p, buf);
180 		db[i++] = p;
181 	}
182 	(void) fclose(fp);
183 
184 	db[i] = NULL;
185 }
186 
187 static void
read_rdb(const char * f,regex_t * r[])188 read_rdb(const char *f, regex_t * r[])
189 {
190 	FILE           *fp;
191 	regex_t        *regex;
192 	char            buf[512], *p;
193 	char            errbuf[512];
194 	size_t          i;
195 	int             err;
196 
197 	fp = my_fopen(f);
198 
199 	i = 0;
200 	while (fgets(buf, sizeof(buf), fp) != NULL && i < MAX_E - 1) {
201 		if (buf[0] == '#' || buf[0] == '\n' || buf[0] == '\r')
202 			continue;
203 		if ((p = strchr(buf, '\n')) == NULL) {
204 			(void) fclose(fp);
205 			fatal_n("line too long in file %s", f);
206 		}
207 		*p = '\0';
208 		regex = (regex_t *) my_alloc(sizeof(regex_t));
209 		if ((err = regcomp(regex, buf, REG_EXTENDED)) != 0) {
210 			(void) regerror(err, regex, errbuf, sizeof(errbuf));
211 			warn("invalid regular expression (%s) in file (%s): %s", buf, f, errbuf);
212 			free(regex);
213 			continue;
214 		}
215 		r[i++] = regex;
216 	}
217 	(void) fclose(fp);
218 
219 	r[i] = NULL;
220 }
221 
222 static void
read_file(const char * fn,struct msg * m)223 read_file(const char *fn, struct msg * m)
224 {
225 	int             f;
226 	char            buf[8192];
227 	ssize_t         len;
228 
229 	f = my_open(fn);
230 	len = read(f, &buf, sizeof(buf));
231 
232 	m->c = (char *) my_alloc(len + 1);
233 	(void) memcpy(m->c, buf, len);
234 	m->c[len] = '\0';
235 	m->len = len;
236 
237 	(void) close(f);
238 }
239 
240 #include "dns.h"
241 
242 static void
read_config_file(void)243 read_config_file(void)
244 {
245 	extern struct cfg config;
246 	FILE           *fp;
247 	char            obuf[100];
248 	char            abuf[100];
249 	char            b[300];
250 
251 	if (*config.file == '\0') {
252 		;
253 	} else if ((fp = fopen(config.file, "r")) != NULL) {
254 		while (fgets(b, sizeof(b), fp) != NULL) {
255 			(void) sscanf(b, "%99s %99s", obuf, abuf);
256 			if (config.first && strcmp("daemonize", obuf) == 0) {
257 				if (strcmp(abuf, "yes") == 0)
258 					config.daemon = 1;
259 				else
260 					config.daemon = 0;
261 				continue;
262 			} else if (strcmp("child_processes", obuf) == 0) {
263 				config.childs = atoi(abuf);
264 				continue;
265 			} else if (config.first && strcmp("bind_ipv4", obuf) == 0) {
266 				if (strcmp(abuf, "yes") == 0)
267 					config.bind_ipv4 = 1;
268 				else
269 					config.bind_ipv4 = 0;
270 				continue;
271 			} else if (config.first && strcmp("bind_ipv6", obuf) == 0) {
272 				if (strcmp(abuf, "yes") == 0)
273 					config.bind_ipv6 = 1;
274 				else
275 					config.bind_ipv6 = 0;
276 				continue;
277 			} else if (config.first && strcmp("bind_ipv4_host", obuf) == 0) {
278 				(void) strncpy(config.ipv4, abuf, sizeof(config.ipv4) - 1);
279 				config.ipv4[sizeof(config.ipv4) - 1] = '\0';
280 				continue;
281 			} else if (config.first && strcmp("bind_ipv6_host", obuf) == 0) {
282 				(void) strncpy(config.ipv6, abuf, sizeof(config.ipv6) - 1);
283 				config.ipv6[sizeof(config.ipv6) - 1] = '\0';
284 				continue;
285 			} else if (config.first && strcmp("port", obuf) == 0) {
286 				config.port = atoi(abuf);
287 				continue;
288 			} else if (strcmp("use_ipv6", obuf) == 0) {
289 				if (strcmp(abuf, "yes") == 0)
290 					config.use_ipv6 = 1;
291 				else
292 					config.use_ipv6 = 0;
293 				continue;
294 			} else if (config.first && strcmp("uid", obuf) == 0) {
295 				if (!(config.uid = atoi(abuf))) {
296 					struct passwd *pwd;
297 					if ((pwd = getpwnam(abuf)))
298 						config.uid = (unsigned long) pwd->pw_uid;
299 					else
300 						fatal_n("UID %s not found", abuf);
301 				}
302 				continue;
303 			} else if (config.first && strcmp("gid", obuf) == 0) {
304 				if (!(config.gid = atoi(abuf))) {
305 					struct group *grp;
306 					if ((grp = getgrnam(abuf)))
307 						config.gid = (unsigned long) grp->gr_gid;
308 					else
309 						fatal_n("GID %s not found", abuf);
310 				}
311 				continue;
312 			} else if (config.first && strcmp("chroot_dir", obuf) == 0) {
313 				(void) strncpy(config.chroot, abuf, sizeof(config.chroot) - 1);
314 				config.chroot[sizeof(config.chroot) - 1] = 0;
315 				continue;
316 			} else if (strcmp("forward_proxy", obuf) == 0) {
317 				(void) strncpy(config.proxyhost, abuf, sizeof(config.proxyhost) - 1);
318 				config.proxyhost[sizeof(config.proxyhost) - 1] = 0;
319 				continue;
320 			} else if (strcmp("forward_proxy_port", obuf) == 0) {
321 				config.proxyport = atoi(abuf);
322 				continue;
323 			} else if (strcmp("forward_proxy_ipv6", obuf) == 0) {
324 				if (strcmp(abuf, "yes") == 0)
325 					config.aux_proxy_ipv6 = 1;
326 				else
327 					config.aux_proxy_ipv6 = 0;
328 				continue;
329 			} else if (config.first && strcmp("db_files_path", obuf) == 0) {
330 				(void) strncpy(config.dbdir, abuf, sizeof(config.dbdir) - 1);
331 				config.dbdir[sizeof(config.dbdir) - 1] = 0;
332 				continue;
333 			} else if (strcmp("backlog_size", obuf) == 0) {
334 				config.backlog = atoi(abuf);
335 				continue;
336 			} else if (strcmp("use_syslog", obuf) == 0) {
337 				if (strcmp(abuf, "yes") == 0)
338 					config.syslog = 1;
339 				else
340 					config.syslog = 0;
341 				continue;
342 			} else if (strcmp("log_all_requests", obuf) == 0) {
343 				if (strcmp(abuf, "yes") == 0)
344 					config.logrequests = 1;
345 				else
346 					config.logrequests = 0;
347 				continue;
348 			} else if (strcmp("accel_host", obuf) == 0) {
349 				(void) strncpy(config.accelhost, abuf, sizeof(config.accelhost) - 1);
350 				config.accelhost[sizeof(config.accelhost) - 1] = '\0';
351 				continue;
352 			} else if (strcmp("accel_port", obuf) == 0) {
353 				config.accelport = atoi(abuf);
354 				continue;
355 			} else if (strcmp("accel_user_host", obuf) == 0) {
356 				if (strcmp(abuf, "yes") == 0)
357 					config.accelusrhost = 1;
358 				else
359 					config.accelusrhost = 0;
360 				continue;
361 			} else if (strcmp("use_keep_alive", obuf) == 0) {
362 				if (strcmp(abuf, "yes") == 0)
363 					config.kalive = 1;
364 				else
365 					config.kalive = 0;
366 				continue;
367 			} else if (strcmp("unrestricted_connect", obuf) == 0) {
368 				if (strcmp(abuf, "yes") == 0)
369 					config.unr_con = 1;
370 				else
371 					config.unr_con = 0;
372 				continue;
373 			} else if (strcmp("timeout_connect", obuf) == 0) {
374 				config.to_con = atoi(abuf);
375 				continue;
376 			} else if (!config.first) {
377 				continue;
378 			} else if (*obuf != '#') {
379 				warn("unknown option in config file %s:  %s", config.file, obuf);
380 				continue;
381 			}
382 		}
383 		(void) fclose(fp);
384 	} else {
385 		if (strcmp(config.file, CFGFILE) == 0)
386 			info("default config file (%s) not available, not using config file", CFGFILE);
387 		else
388 			fatal("unable to open config file %s", config.file);
389 	}
390 	config.first = 0;
391 }
392 
393 #define ZHWRONG(a, o, v)	if(a < 1 || a > v) fatal_n("%s is set < 1 or value is too high (maximum:  %d, current:  %d)", o, v, a);
394 #define HWRONG(a, o, v)		if(a < 0 || a > v) fatal_n("Value of %s is set too high or negative (maximum:  %d, current:  %d)", o, v, a);
395 #define HUWRONG(a, o, v)		if(a > v) fatal_n("Value of %s is set too high (maximum:  %d, current:  %d)", o, v, a);
396 
397 static void
verify_config(void)398 verify_config(void)
399 {
400 	extern struct cfg config;
401 
402 	HUWRONG(config.port, "port", MAX_PORTS);
403 	ZHWRONG(config.childs, "child_processes", MAX_CHILDS);
404 	HWRONG(config.backlog, "backlog_size", MAX_BACKLOG);
405 	HUWRONG(config.proxyport, "forward_proxy_port", MAX_PORTS);
406 	HUWRONG(config.uid, "uid", MAX_UID);
407 	HUWRONG(config.gid, "gid", MAX_GID);
408 	HUWRONG(config.accelport, "accel_port", MAX_PORTS);
409 
410 	if ((config.uid && !config.gid) || (!config.uid && config.gid))
411 		fatal_n("Only one of uid and gid is set to non-zero.\nYou have to use both or none of them");
412 	if (*config.accelhost && config.accelport)
413 		config.accel = 1;
414 	else
415 		config.accel = 0;
416 	if (!config.bind_ipv4 && !config.bind_ipv6)
417 		fatal_n("Both IPv4 and IPv6 binding disabled.  This makes no sense");
418 }
419