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