/* * ezbounce.cpp * * (C) 1998-2004 Murat Deligonul * * 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 "autoconf.h" #include #include #include #include #include #include #include #include "server.h" #include "general.h" #include "ezbounce.h" #include "config.h" #include "debug.h" time_t start_time; static const char * const ezbounce_banner = EZBOUNCE_VERSION "\n(c) 1998-2004 by Murat Deligonul (druglord@erupt.com)\n\n"; struct proxy_options pcfg; list * users; list * shitlist; strlist * vhosts; static void sig_handler(int); static void setup_signals(); static void usage(const char *); static bool parse_cmdline(int, char **, char **, bool *, int *, pid_t *); void redir_stdxxx(void); /* disable for now */ #undef HAVE_SET_NEW_HANDLER #ifdef HAVE_SET_NEW_HANDLER void new_failed(); #endif /* #define NOFORK */ static void setup_signals() { struct sigaction sv; sv.sa_flags = 0; sv.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sv, NULL); sv.sa_handler = &sig_handler; sigaction(SIGINT, &sv, NULL); sigaction(SIGTERM, &sv, NULL); sigaction(SIGHUP, &sv, NULL); sigaction(SIGALRM, &sv, NULL); /* sigaction(SIGSEGV, &sv, NULL); */ } int main(int argc, char *argv[]) { char tmp[20]; unsigned num_listening = 0; #ifndef NOFORK pid_t pid; #endif pid_t uid = 0; /* become this user */ bool nobg = 0; /* go to the back ground? */ char *iface_listen = NULL; /* interface to bind to for listening*/ int logindex = 0; /* argv[logindex] is the log */ #if defined(HAVE_NEW_H) && defined(HAVE_SET_NEW_HANDLER) set_new_handler(new_failed); #endif printf(ezbounce_banner); if (argc < 2 || !parse_cmdline(argc, argv, &iface_listen, &nobg, &logindex, &uid)) { usage(argv[0]); return 1; } printf("Reading config file %s ...\n", argv[logindex]); if ((argc = load_config_file(argv[logindex], &pcfg, &users, &shitlist, &vhosts)) < 0) { fprintf(stderr, "Error(s) while loading config file -- exiting.\n"); return 1; } else if (argc == 0) { perror("Unable to open config file"); return 1; } else printf("Config file successfully loaded.\n"); /* Init sock table */ printf("Socket table initialized: %d%s%d = %.02fkB\n", pollsocket::create_table(pcfg.max_sockets), "x", sizeof(pollsocket *), (float) sizeof(pollsocket *) * (float) pcfg.max_sockets / 1024.0); /* Do listen vhost crap */ if (iface_listen) { struct in_addr in; switch (fill_in_addr(iface_listen, &in)) { case 0: fprintf(stderr, "Can't listen on: %s: unknown host\n", iface_listen); return 1; case -1: fprintf(stderr, "Can't listen on: %s: %s\n", iface_listen, strerror(errno)); return 1; default: pcfg.iface_listen = in; } } printf("Listening on interface: %s\n", inet_ntoa(pcfg.iface_listen)); start_time = time(NULL); if(pcfg.ports) { argc = 1; printf("Listening on ports"); while (gettok(pcfg.ports, tmp, 20, ',', argc++)) { if (!ircproxy_listen((u_short)atoi(tmp), &pcfg.iface_listen,0)) printf(" (%s:%s)", tmp, strerror(errno)); else { printf(" %s", tmp); num_listening++; } } fputc('\n', stdout); } #ifdef _USE_SSL bool ssl_ready = pollsocket::init_ssl(pcfg.certfile); if (!ssl_ready) printf("SSL: cannot init SSL! (check your cert-file '%s')\n", pcfg.certfile); else printf("SSL: subsystem initialized\n"); #endif if(pcfg.sslports #ifdef _USE_SSL && ssl_ready #endif ) { argc = 1; printf("SSL: listening on ports"); #ifndef _USE_SSL printf(" [note: SSL support was NOT compiled]"); #endif while (gettok(pcfg.sslports, tmp, 20, ',', argc++)) { if (!ircproxy_listen((u_short)atoi(tmp), &pcfg.iface_listen,1)) printf(" (%s:%s)", tmp, strerror(errno)); else { printf(" %s", tmp); num_listening++; } } fputc('\n', stdout); } printf("Listening on total of %d ports\n", num_listening); if (!num_listening) { fprintf(stderr, "oops, I can't run without listening on any ports\n"); return 1; } /* change uid if needed */ if (uid) { if (!setuid(uid)) printf("Now operating under userid %d\n.", uid); else perror("setuid"); } /* start the log file */ if (!ircproxy_startlog(pcfg.logfile)) perror("WARNING: Unable to open log file"); #ifndef NOFORK if ((nobg) || ((pid = fork()) == 0)) { setsid(); /* redirect stderr to our silly little log file. close stdin & stdout. */ close(STDIN_FILENO); /* close(STDOUT_FILENO); -- config file reloading will output to stdout */ if (!nobg) ircproxy_redir_stdxxx(); #endif /* Write out pid-file if needed */ if (pcfg.pidfile) { int f = open(pcfg.pidfile, O_CREAT | O_WRONLY); fdprintf(f, "%d", getpid()); fchmod(f, 0644); close(f); } /* no longer needed */ delete[] pcfg.logfile; delete[] pcfg.pidfile; pcfg.logfile = NULL; pcfg.pidfile = NULL; setup_signals(); set_dns_timeout(pcfg.max_dns_wait_time); if (pcfg.userfile) { printf("Loading user preferences from disk (%s) .... \n", pcfg.userfile); ircproxy_load_prefs(::users, pcfg.userfile); } if (nobg) printf("Starting IRC proxy ...\n"); start_proxy(); /* Server is finished. */ ircproxy_closelog(); stop_proxy(); delete[] pcfg.configfile; delete[] pcfg.logfile; delete[] pcfg.pidfile; delete[] pcfg.motdfile; delete[] pcfg.userfile; delete[] pcfg.logdir; delete[] pcfg.ports; delete[] pcfg.dcc_ports; destroy_list(users, 0); destroy_list(shitlist, 0); destroy_list(vhosts, 1); delete users; delete shitlist; delete vhosts; return 0; #ifndef NOFORK } else if (pid < 0) perror("Can't go into the background"); else printf("IRC Proxy started -- pid: %d\n", pid); #endif return 0; } static void sig_handler(int sig) { switch (sig) { case SIGHUP: printlog("Got SIGHUP; reloading configuration file\n"); ircproxy_request_rehash(); break; case SIGTERM: printlog("Got terminate signal, killing server now\n"); ircproxy_die(1, "got terminate signal"); break; case SIGSEGV: printlog("Got SIGSEGV: goodbye :-(\n"); break; case SIGINT: printlog("Interrupt signal: waiting for server to shut down\n"); ircproxy_die(0, "keyboard interrupt"); default: break; } } static void usage(const char *bname) { fprintf(stderr, "Usage:\n%s [options] \n", bname); fprintf(stderr, "Available options:\n"); fprintf(stderr, " -f don't go into the background\n"); fprintf(stderr, " -b listen for connections on a different interface\n"); fprintf(stderr, " -u become user uid after binding listen socket\n\n"); } static bool parse_cmdline(int argc, char **argv, char **pIface_listen, bool * pNobg, int *pIndex, pid_t * uid) { for (int z = 1; z < argc; z++) { switch (argv[z][0]) { /* a -flag, next char must be -B or -F */ case '-': switch (argv[z][1]) { case 'u': case 'U': *uid = atoi(argv[++z]); continue; case 'f': case 'F': *pNobg = 1; break; case 'b': case 'B': if (z + 1 < argc) *pIface_listen = argv[++z]; continue; case '-': if (strcasecmp(&argv[z][2], "help") != 0) continue; case 'h': return 0; default: break; } default: *pIndex = z; } } return (*pIndex != 0); } #ifdef HAVE_SET_NEW_HANDLER void new_failed() { printlog("fatal memory allocation error :(\n"); printlog("notify author\n"); abort(); } #endif