/* * ffproxy (c) 2002-2005 Niklas Olmes * http://faith.eu.org * * $Id: main.c,v 2.2 2005/01/05 15:12:49 niklas Exp niklas $ * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 675 * Mass Ave, Cambridge, MA 02139, USA. */ #include "configure.h" #ifdef HAVE_SYS_TYPES_H # include #endif #include #ifdef HAVE_STDLIB_H # include #endif #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include "cfg.h" #include "print.h" #include "socket.h" #include "db.h" #include "dns.h" #include "signals.h" #if !defined(HAVE_DAEMON) || defined(NEED_DAEMON) || defined(__sun__) #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_FCNTL_H # include #endif static int daemon(int, int); int daemon(int nochdir, int noclose) { int f; f = open("/dev/null", O_RDWR); (void) dup2(STDIN_FILENO, f); (void) dup2(STDERR_FILENO, f); (void) dup2(STDOUT_FILENO, f); if (fork()) _exit(0); return 0; } #endif static void usage(void); static void drop_privileges(void); static const char version[] = "1.6"; static const char rcsid[] = "$Id: main.c,v 2.2 2005/01/05 15:12:49 niklas Exp niklas $"; char loop_header[100]; struct cfg config; int main(int argc, char *argv[]) { int c, nowarn; char *prgname; prgname = argv[0]; nowarn = 0; (void) memset(&config, 0, sizeof(config)); *config.ipv4 = '\0'; *config.ipv6 = '\0'; config.port = 0; config.daemon = 0; config.childs = 10; config.ccount = 0; config.backlog = 4; config.uid = 0L; config.gid = 0L; *config.chroot = '\0'; (void) strncpy(config.dbdir, DATADIR, sizeof(config.dbdir) - 1); config.dbdir[sizeof(config.dbdir) - 1] = '\0'; (void) strncpy(config.file, CFGFILE, sizeof(config.file) - 1); config.file[sizeof(config.file) - 1] = '\0'; *config.proxyhost = '\0'; config.proxyport = 0; config.syslog = 1; config.logrequests = 0; config.use_ipv6 = 1; config.aux_proxy_ipv6 = 1; config.bind_ipv6 = 1; config.bind_ipv4 = 1; config.accel = 0; config.accelusrhost = 1; *config.accelhost = '\0'; config.accelport = 80; config.kalive = 1; config.unr_con = 0; config.to_con = 5; config.first = 1; while ((c = getopt(argc, argv, "vdbBc:C:p:x:X:l:u:g:r:D:F:f:s4a:A:h")) != -1) { switch (c) { case 'v': (void) printf("ffproxy version %s, %s\n", version, rcsid); exit(0); break; case 'd': config.daemon = 1; break; case 'b': config.bind_ipv4 = 0; break; case 'B': config.bind_ipv6 = 0; break; case 'c': (void) strncpy(config.ipv4, optarg, sizeof(config.ipv4) - 1); config.ipv4[sizeof(config.ipv4) - 1] = '\0'; break; case 'C': (void) strncpy(config.ipv6, optarg, sizeof(config.ipv6) - 1); config.ipv6[sizeof(config.ipv6) - 1] = '\0'; break; case 'p': config.port = atoi(optarg); if (config.port > MAX_PORTS || !config.port) { (void) fprintf(stderr, "Invalid port number (-p %s)\n", optarg); exit(1); } break; case 'x': if (strlen(optarg) > sizeof(config.proxyhost) - 1 ) { (void) fprintf(stderr, "Proxy host name too long\n"); exit(1); } (void) strncpy(config.proxyhost, optarg, sizeof(config.proxyhost) - 1); config.proxyhost[sizeof(config.proxyhost) - 1] = '\0'; break; case 'X': config.proxyport = atoi(optarg); if (config.proxyport > MAX_PORTS || !config.proxyport) { (void) fprintf(stderr, "Invalid port number (-X %s)\n", optarg); exit(1); } break; case 'l': config.childs = atoi(optarg); if (!config.childs || config.childs > MAX_CHILDS) { (void) fprintf(stderr, "Invalid limit of child processes (-l %s)\n", optarg); exit(1); } break; case 'u': if (!(config.uid = atoi(optarg))) { struct passwd *pwd; if ((pwd = getpwnam(optarg))) config.uid = (unsigned long) pwd->pw_uid; else { (void) fprintf(stderr, "UID %s not found\n", optarg); exit(1); } } break; case 'g': if (!(config.gid = atoi(optarg))) { struct group *grp; if ((grp = getgrnam(optarg))) config.gid = (unsigned long) grp->gr_gid; else { (void) fprintf(stderr, "GID %s not found\n", optarg); exit(1); } } break; case 'r': if (strlen(optarg) > sizeof(config.chroot) - 1 ) { (void) fprintf(stderr, "chroot directory too long\n"); exit(1); } (void) strncpy(config.chroot, optarg, sizeof(config.chroot) - 1); config.chroot[sizeof(config.chroot) - 1] = '\0'; break; case 'D': if (strlen(optarg) > sizeof(config.dbdir) - 1 ) { (void) fprintf(stderr, "dbdir directory too long\n"); exit(1); } (void) strncpy(config.dbdir, optarg, sizeof(config.dbdir) - 1); config.dbdir[sizeof(config.dbdir) - 1] = '\0'; break; case 'F': nowarn = 1; case 'f': if (strlen(optarg) > sizeof(config.file) - 1 ) { (void) fprintf(stderr, "config file name too long\n"); exit(1); } (void) strncpy(config.file, optarg, sizeof(config.file) - 1); config.file[sizeof(config.file) - 1] = '\0'; if (*config.file && !nowarn && strcmp(config.file, "/dev/null") != 0) (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); break; case 's': config.syslog = 0; break; case '4': config.use_ipv6 = 0; break; case 'a': (void) strncpy(config.accelhost, optarg, sizeof(config.accelhost) - 1); config.accelhost[sizeof(config.accelhost) - 1] = '\0'; break; case 'A': config.accelport = atoi(optarg); break; case 'h': usage(); /* NOTREACHED */ break; default: (void) fprintf(stderr, "Error, type `%s -h' for help on usage\n", prgname); exit(1); break; } } argc -= optind; argv += optind; if (*argv) { (void) fprintf(stderr, "Unknown argument left (%s)\nType `%s -h' for usage\n", *argv, prgname); exit(1); } setup_log_master(); info("started, initializing"); load_databases(); (void) resolve("localhost"); drop_privileges(); if (config.daemon) { FILE *fp; if (daemon(1, 0) != 0) fatal("daemon() failed"); (void) close(0); (void) close(1); (void) close(2); (void) chdir(config.dbdir); if ((fp = fopen("ffproxy.pid", "w")) == NULL) fatal("cannot create pid file ffproxy.pid in %s", config.dbdir); (void) fprintf(fp, "%ld", (long) getpid()); (void) fclose(fp); } (void) snprintf(loop_header, sizeof(loop_header), "X-Loop-%d-%d: true", getpid(), (int) time(NULL)); init_sighandlers(); open_socket(); /* NOTREACHED */ return 0; } static void usage(void) { (void) fprintf(stderr, "ffproxy %s -- (c) 2002-2005 Niklas Olmes \n", version); (void) fprintf(stderr, " GNU GPL. Website: http://faith.eu.org/programs.html\n"); (void) fprintf(stderr, "usage: ffproxy [-vhds4bB] [-c host|ip] [-C host|ip] [-p port]\n" " [-x host|ip -X port] [-l max] [-u uid|usr -g gid|grp] [-r dir]\n" " [-D dir] [-f file] [-a host|ip] [-A port]\n" " -v print version number -h usage (this screen)\n" " -d become daemon -s silent. don't log to syslog.\n" " -4 use IPv4 only. don't try contacting via IPv6.\n" " -b do *not* bind to IPv4\n" " -B do *not* bind to IPv6\n" " -c host|ip bind to IPv4 address (default is any)\n"); (void) fprintf(stderr, " -C host|ip bind to IPv6 address (default is any)\n" " -p port bind to port\n" " -x host|ip auxiliary forward proxy\n" " -X port auxiliary forward proxy port\n" " -l max maximum number of concurrent requests\n" " -u uid|user change uid\n" " -g gid|group change gid\n" " -r dir chroot to dir\n" " -D dir databases are in dir (default is %s)\n" " -f file use config file (default is %s; *overwrites*)\n" " -a host|ip auxiliary forward server to use\n" " -A port auxiliary forward server port (default is 80)\n", DATADIR, CFGFILE); exit(1); } static void drop_privileges(void) { extern struct cfg config; struct passwd *pwd; if (config.uid == 0 || config.gid == 0) { info("not changing UID/GID"); } else { if ((pwd = getpwuid(config.uid)) == NULL) fatal("getpwuid() failed (non-existent UID entry?)"); if (*config.chroot != '\0') { if (chdir(config.chroot) != 0) fatal("chdir() failed"); if (chroot(config.chroot) != 0) fatal("chroot() failed"); info("chroot()ed to %s\n", config.chroot); } if (setgid(config.gid) != 0) fatal("setgid() failed"); if (initgroups(pwd->pw_name, config.gid) != 0) fatal("initgroups() failed"); if (setuid(config.uid) != 0) fatal("setuid() failed"); } info("=> UID(%d), EUID(%d), GID(%d), EGID(%d)", getuid(), geteuid(), getgid(), getegid()); }