1 /*
2  * ffproxy (c) 2002-2005 Niklas Olmes <niklas@noxa.de>
3  * http://faith.eu.org
4  *
5  * $Id: main.c,v 2.2 2005/01/05 15:12:49 niklas Exp niklas $
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 
29 #ifdef HAVE_STDLIB_H
30 # include <stdlib.h>
31 #endif
32 #include <string.h>
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif
36 #include <time.h>
37 #include <pwd.h>
38 #include <grp.h>
39 
40 #include "cfg.h"
41 #include "print.h"
42 #include "socket.h"
43 #include "db.h"
44 #include "dns.h"
45 #include "signals.h"
46 
47 #if !defined(HAVE_DAEMON) || defined(NEED_DAEMON) || defined(__sun__)
48 #ifdef HAVE_SYS_TYPES_H
49 # include <sys/types.h>
50 #endif
51 #ifdef HAVE_SYS_STAT_H
52 # include <sys/stat.h>
53 #endif
54 #ifdef HAVE_FCNTL_H
55 # include <fcntl.h>
56 #endif
57 
58 static int daemon(int, int);
59 int
daemon(int nochdir,int noclose)60 daemon(int nochdir, int noclose)
61 {
62 	int f;
63 	f = open("/dev/null", O_RDWR);
64 	(void) dup2(STDIN_FILENO, f);
65 	(void) dup2(STDERR_FILENO, f);
66 	(void) dup2(STDOUT_FILENO, f);
67 	if (fork())
68 		_exit(0);
69 	return 0;
70 }
71 #endif
72 
73 static void     usage(void);
74 static void     drop_privileges(void);
75 
76 static const char version[] = "1.6";
77 static const char rcsid[] = "$Id: main.c,v 2.2 2005/01/05 15:12:49 niklas Exp niklas $";
78 char            loop_header[100];
79 
80 struct cfg      config;
81 
82 int
main(int argc,char * argv[])83 main(int argc, char *argv[])
84 {
85 	int             c, nowarn;
86 	char		*prgname;
87 
88 	prgname = argv[0];
89 	nowarn = 0;
90 
91 	(void) memset(&config, 0, sizeof(config));
92 	*config.ipv4 = '\0';
93 	*config.ipv6 = '\0';
94 	config.port = 0;
95 	config.daemon = 0;
96 	config.childs = 10;
97 	config.ccount = 0;
98 	config.backlog = 4;
99 	config.uid = 0L;
100 	config.gid = 0L;
101 	*config.chroot = '\0';
102 	(void) strncpy(config.dbdir, DATADIR, sizeof(config.dbdir) - 1);
103 	config.dbdir[sizeof(config.dbdir) - 1] = '\0';
104 	(void) strncpy(config.file, CFGFILE, sizeof(config.file) - 1);
105 	config.file[sizeof(config.file) - 1] = '\0';
106 	*config.proxyhost = '\0';
107 	config.proxyport = 0;
108 	config.syslog = 1;
109 	config.logrequests = 0;
110 	config.use_ipv6 = 1;
111 	config.aux_proxy_ipv6 = 1;
112 	config.bind_ipv6 = 1;
113 	config.bind_ipv4 = 1;
114 	config.accel = 0;
115 	config.accelusrhost = 1;
116 	*config.accelhost = '\0';
117 	config.accelport = 80;
118 	config.kalive = 1;
119 	config.unr_con = 0;
120 	config.to_con = 5;
121 	config.first = 1;
122 
123 	while ((c = getopt(argc, argv, "vdbBc:C:p:x:X:l:u:g:r:D:F:f:s4a:A:h")) != -1) {
124 		switch (c) {
125 		case 'v':
126 			(void) printf("ffproxy version %s, %s\n",
127 				      version, rcsid);
128 			exit(0);
129 			break;
130 		case 'd':
131 			config.daemon = 1;
132 			break;
133 		case 'b':
134 			config.bind_ipv4 = 0;
135 			break;
136 		case 'B':
137 			config.bind_ipv6 = 0;
138 			break;
139 		case 'c':
140 			(void) strncpy(config.ipv4, optarg, sizeof(config.ipv4) - 1);
141 			config.ipv4[sizeof(config.ipv4) - 1] = '\0';
142 			break;
143 		case 'C':
144 			(void) strncpy(config.ipv6, optarg, sizeof(config.ipv6) - 1);
145 			config.ipv6[sizeof(config.ipv6) - 1] = '\0';
146 			break;
147 		case 'p':
148 			config.port = atoi(optarg);
149 			if (config.port > MAX_PORTS || !config.port) {
150 				(void) fprintf(stderr, "Invalid port number (-p %s)\n", optarg);
151 				exit(1);
152 			}
153 			break;
154 		case 'x':
155 			if (strlen(optarg) > sizeof(config.proxyhost) - 1 ) {
156 				(void) fprintf(stderr, "Proxy host name too long\n");
157 				exit(1);
158 			}
159 			(void) strncpy(config.proxyhost, optarg, sizeof(config.proxyhost) - 1);
160 			config.proxyhost[sizeof(config.proxyhost) - 1] = '\0';
161 			break;
162 		case 'X':
163 			config.proxyport = atoi(optarg);
164 			if (config.proxyport > MAX_PORTS || !config.proxyport) {
165 				(void) fprintf(stderr, "Invalid port number (-X %s)\n", optarg);
166 				exit(1);
167 			}
168 			break;
169 		case 'l':
170 			config.childs = atoi(optarg);
171 			if (!config.childs || config.childs > MAX_CHILDS) {
172 				(void) fprintf(stderr, "Invalid limit of child processes (-l %s)\n", optarg);
173 				exit(1);
174 			}
175 			break;
176 		case 'u':
177 			if (!(config.uid = atoi(optarg))) {
178 				struct passwd *pwd;
179 				if ((pwd = getpwnam(optarg)))
180 					config.uid = (unsigned long) pwd->pw_uid;
181 				else {
182 					(void) fprintf(stderr, "UID %s not found\n", optarg);
183 					exit(1);
184 				}
185 			}
186 			break;
187 		case 'g':
188 			if (!(config.gid = atoi(optarg))) {
189 				struct group *grp;
190 				if ((grp = getgrnam(optarg)))
191 					config.gid = (unsigned long) grp->gr_gid;
192 				else {
193 					(void) fprintf(stderr, "GID %s not found\n", optarg);
194 					exit(1);
195 				}
196 			}
197 			break;
198 		case 'r':
199 			if (strlen(optarg) > sizeof(config.chroot) - 1 ) {
200 				(void) fprintf(stderr, "chroot directory too long\n");
201 				exit(1);
202 			}
203 			(void) strncpy(config.chroot, optarg, sizeof(config.chroot) - 1);
204 			config.chroot[sizeof(config.chroot) - 1] = '\0';
205 			break;
206 		case 'D':
207 			if (strlen(optarg) > sizeof(config.dbdir) - 1 ) {
208 				(void) fprintf(stderr, "dbdir directory too long\n");
209 				exit(1);
210 			}
211 			(void) strncpy(config.dbdir, optarg, sizeof(config.dbdir) - 1);
212 			config.dbdir[sizeof(config.dbdir) - 1] = '\0';
213 			break;
214 		case 'F':
215 			nowarn = 1;
216 		case 'f':
217 			if (strlen(optarg) > sizeof(config.file) - 1 ) {
218 				(void) fprintf(stderr, "config file name too long\n");
219 				exit(1);
220 			}
221 			(void) strncpy(config.file, optarg, sizeof(config.file) - 1);
222 			config.file[sizeof(config.file) - 1] = '\0';
223 			if (*config.file && !nowarn && strcmp(config.file, "/dev/null") != 0)
224 				(void) fprintf(stdout, "Using config file (%s).\nPlease note that due to design, config file overwrites command line options.\nUse -F instead of -f to omit this warning message.\n", config.file);
225 			break;
226 		case 's':
227 			config.syslog = 0;
228 			break;
229 		case '4':
230 			config.use_ipv6 = 0;
231 			break;
232 		case 'a':
233 			(void) strncpy(config.accelhost, optarg, sizeof(config.accelhost) - 1);
234 			config.accelhost[sizeof(config.accelhost) - 1] = '\0';
235 			break;
236 		case 'A':
237 			config.accelport = atoi(optarg);
238 			break;
239 		case 'h':
240 			usage();
241 			/* NOTREACHED */
242 			break;
243 		default:
244 			(void) fprintf(stderr, "Error, type `%s -h' for help on usage\n", prgname);
245 			exit(1);
246 			break;
247 		}
248 	}
249 	argc -= optind;
250 	argv += optind;
251 
252 	if (*argv) {
253 		(void) fprintf(stderr, "Unknown argument left (%s)\nType `%s -h' for usage\n", *argv, prgname);
254 		exit(1);
255 	}
256 
257 	setup_log_master();
258 	info("started, initializing");
259 	load_databases();
260 	(void) resolve("localhost");
261 	drop_privileges();
262 
263 	if (config.daemon) {
264 		FILE           *fp;
265 
266 		if (daemon(1, 0) != 0)
267 			fatal("daemon() failed");
268 		(void) close(0);
269 		(void) close(1);
270 		(void) close(2);
271 
272 		(void) chdir(config.dbdir);
273 		if ((fp = fopen("ffproxy.pid", "w")) == NULL)
274 			fatal("cannot create pid file ffproxy.pid in %s", config.dbdir);
275 
276 		(void) fprintf(fp, "%ld", (long) getpid());
277 		(void) fclose(fp);
278 	}
279 	(void) snprintf(loop_header, sizeof(loop_header), "X-Loop-%d-%d: true", getpid(), (int) time(NULL));
280 
281 	init_sighandlers();
282 	open_socket();
283 
284 	/* NOTREACHED */
285 	return 0;
286 }
287 
288 static void
usage(void)289 usage(void)
290 {
291 	(void) fprintf(stderr, "ffproxy %s -- (c) 2002-2005 Niklas Olmes <niklas@noxa.de>\n", version);
292 	(void) fprintf(stderr, "   GNU GPL.  Website: http://faith.eu.org/programs.html\n");
293 	(void) fprintf(stderr,
294 		       "usage: ffproxy [-vhds4bB] [-c host|ip] [-C host|ip] [-p port]\n"
295                        "       [-x host|ip -X port] [-l max] [-u uid|usr -g gid|grp] [-r dir]\n"
296                        "       [-D dir] [-f file] [-a host|ip] [-A port]\n"
297 		       " -v  print version number       -h  usage (this screen)\n"
298 		       " -d  become daemon              -s  silent.  don't log to syslog.\n"
299 		       " -4  use IPv4 only.  don't try contacting via IPv6.\n"
300 		       " -b  do *not* bind to IPv4\n"
301 		       " -B  do *not* bind to IPv6\n"
302 		       " -c host|ip   bind to IPv4 address (default is any)\n");
303 	(void) fprintf(stderr,
304 		       " -C host|ip   bind to IPv6 address (default is any)\n"
305 		       " -p port      bind to port\n"
306 		       " -x host|ip   auxiliary forward proxy\n"
307 		       " -X port      auxiliary forward proxy port\n"
308 		       " -l max       maximum number of concurrent requests\n"
309 		       " -u uid|user  change uid\n"
310 		       " -g gid|group change gid\n"
311 		       " -r dir       chroot to dir\n"
312 		       " -D dir       databases are in dir (default is %s)\n"
313 		       " -f file      use config file (default is %s; *overwrites*)\n"
314 		       " -a host|ip   auxiliary forward server to use\n"
315 		       " -A port      auxiliary forward server port (default is 80)\n",
316 			DATADIR, CFGFILE);
317 	exit(1);
318 }
319 
320 static void
drop_privileges(void)321 drop_privileges(void)
322 {
323 	extern struct cfg config;
324 	struct passwd  *pwd;
325 
326 	if (config.uid == 0 || config.gid == 0) {
327 		info("not changing UID/GID");
328 	} else {
329 		if ((pwd = getpwuid(config.uid)) == NULL)
330 			fatal("getpwuid() failed (non-existent UID entry?)");
331 
332 		if (*config.chroot != '\0') {
333 			if (chdir(config.chroot) != 0)
334 				fatal("chdir() failed");
335 			if (chroot(config.chroot) != 0)
336 				fatal("chroot() failed");
337 			info("chroot()ed to %s\n", config.chroot);
338 		}
339 		if (setgid(config.gid) != 0)
340 			fatal("setgid() failed");
341 		if (initgroups(pwd->pw_name, config.gid) != 0)
342 			fatal("initgroups() failed");
343 
344 		if (setuid(config.uid) != 0)
345 			fatal("setuid() failed");
346 
347 	}
348 
349 	info("=> UID(%d), EUID(%d), GID(%d), EGID(%d)", getuid(), geteuid(), getgid(), getegid());
350 }
351