diff options
Diffstat (limited to '')
-rw-r--r-- | src/tinyproxy.c | 478 |
1 files changed, 478 insertions, 0 deletions
diff --git a/src/tinyproxy.c b/src/tinyproxy.c new file mode 100644 index 0000000..1425492 --- /dev/null +++ b/src/tinyproxy.c @@ -0,0 +1,478 @@ +/* $Id: tinyproxy.c,v 1.1.1.1 2000-02-16 17:32:23 sdyoung Exp $ + * + * The initialize routine. Basically sets up all the initial stuff (logfile, + * listening socket, config options, etc.) and then sits there and loops + * over the new connections until the daemon is closed. Also has additional + * functions to handle the "user friendly" aspects of a program (usage, + * stats, etc.) Like any good program, most of the work is actually done + * elsewhere. + * + * Copyright (C) 1998 Steven Young + * Copyright (C) 1999 Robert James Kaes (rjkaes@flarenet.com) + * Copyright (C) 2000 Chris Lightfoot (chris@ex-parrot.com> + * + * 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, 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. + */ + +#ifdef HAVE_CONFIG_H +#include <defines.h> +#endif + +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <signal.h> +#include <stdlib.h> +#include <sysexits.h> +#include <pwd.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <time.h> +#include <sys/time.h> +#include <syslog.h> +#include <unistd.h> +#include <fcntl.h> + +/* chris - need this for asynchronous DNS resolution */ +#include <adns.h> + +adns_state adns; + +#include "config.h" +#include "tinyproxy.h" +#include "utils.h" +#include "log.h" +#include "sock.h" +#include "conns.h" +#include "reqs.h" +#include "buffer.h" +#include "filter.h" + +void takesig(int sig); + +/* + * Global Structures + */ +struct config_s config = { + NULL, /* Log file handle */ + DEFAULT_LOG, /* Logfile name */ + FALSE, /* Use syslog instead? */ + DEFAULT_CUTOFFLOAD, /* Cut off load */ + DEFAULT_PORT, /* Listen on this port */ + DEFAULT_STATHOST, /* URL of stats host */ + FALSE, /* Quit? */ + DEFAULT_USER, /* Name of user to change to */ + FALSE, /* Run anonymous by default? */ + NULL, /* String containing the subnet allowed */ + NULL, /* IP address to listen on */ +#ifdef FILTER_ENABLE + NULL, /* Location of filter file */ +#endif /* FILTER_ENABLE */ + FALSE, /* Restrict the log to only errors */ +#ifdef XTINYPROXY + NULL /* The name of this domain */ +#endif +}; + +struct stat_s stats; +float load = 0.00; +struct allowedhdr_s *allowedhdrs = NULL; + +/* + * Dump info to the logfile + */ +static void dumpdebug(void) +{ + struct conn_s *connptr = connections; + long clients = 0, waiting = 0, relaying = 0, closing = 0, finished = 0; + + log("SIGUSR1 received, debug dump follows."); + + while (connptr) { + switch (connptr->type) { + case NEWCONN: + clients++; + break; + case WAITCONN: + waiting++; + break; + case RELAYCONN: + relaying++; + break; + case CLOSINGCONN: + closing++; + break; + case FINISHCONN: + finished++; + break; + default: + break; + } + connptr = connptr->next; + } + log("clients: %d, waiting: %d, relaying: %d," \ + "closing: %d, finished: %d", + clients, waiting, relaying, closing, finished); + log("total requests handled: %lu", stats.num_reqs); + log("total connections handled: %lu", stats.num_cons); + log("total sockets listened: %lu", stats.num_listens); + log("total sockets opened: %lu", stats.num_opens); + log("total bad opens: %lu", stats.num_badcons); + log("total bytes tx: %lu", stats.num_tx); + log("total bytes rx: %lu", stats.num_rx); + log("connections refused due to load: %lu", stats.num_refused); + log("garbage collections: %lu", stats.num_garbage); + log("idle connections killed: %lu", stats.num_idles); + log("end debug dump."); +} + +/* + * Handle a signal + */ +void takesig(int sig) +{ + switch (sig) { + case SIGUSR1: + dumpdebug(); + break; + case SIGHUP: + if (config.logf) + ftruncate(fileno(config.logf), 0); + + log("SIGHUP received, cleaning up..."); + conncoll(); + garbcoll(); + +#ifdef FILTER_ENABLE + if (config.filter) { + filter_destroy(); + filter_init(); + } + log("Re-reading filter file."); +#endif /* FILTER_ENABLE */ + log("Finished cleaning memory/connections."); + break; + case SIGTERM: +#ifdef FILTER_ENABLE + if (config.filter) + filter_destroy(); +#endif /* FILTER_ENABLE */ + config.quit = TRUE; + break; + case SIGALRM: + calcload(); + alarm(LOAD_RECALCTIMER); + break; + } + if (sig != SIGTERM) + signal(sig, takesig); + signal(SIGPIPE, SIG_IGN); +} + +/* + * Display usage to the user on stderr. + */ +static void usagedisp(void) +{ + printf("tinyproxy version " VERSION "\n"); + printf("Copyright 1998 Steven Young (sdyoung@well.com)\n"); + printf + ("Copyright 1998-1999 Robert James Kaes (rjkaes@flarenet.com)\n\n"); + printf("Copyright 2000 Chris Lightfoot (chris@ex-parrot.com)\n"); + + printf + ("This software is licensed under the GNU General Public License (GPL).\n"); + printf("See the file 'COPYING' included with tinyproxy source.\n\n"); + + printf("Compiled with Ian Jackson's adns:\n"); + printf(" http://www.chiark.greenend.org.uk/~ian/adns/\n\n"); + + printf("Usage: tinyproxy [args]\n"); + printf("Options:\n"); + printf("\t-v\t\tdisplay version number\n"); + printf("\t-h\t\tdisplay usage\n"); + printf("\t-d\t\tdo not daemonize\n"); + printf + ("\t-n ip_address\tallow access from only this subnet. (i.e. 192.168.0.)\n"); + printf("\t-i ip_address\tonly listen on this address\n"); + printf("\t-p port\t\tlisten on 'port'\n"); + printf("\t-l logfile\tlog to 'logfile'\n"); +#ifdef HAVE_SYSLOG_H + printf("\t-S\t\tlog using the syslog instead\n"); +#endif + printf("\t-r\t\trestrict the log to only errors\n"); + printf + ("\t-w load\t\tstop accepting new connections at 'load'. 0 disables\n"); + printf("\t-s stathost\tset stathost to 'stathost'\n"); + printf("\t-u user\t\tchange to user after startup. \"\" disables\n"); + printf("\t-a header\tallow 'header' through the anon block\n"); +#ifdef FILTER_ENABLE + printf("\t-f filterfile\tblock sites specified in filterfile\n"); +#endif /* FILTER_ENABLE */ +#ifdef XTINYPROXY + printf + ("\t-x domain\tAdd a XTinyproxy header with the peer's IP address\n"); +#endif + + /* Display the modes compiled into tinyproxy */ + printf("\nFeatures Compiled In:\n"); +#ifdef XTINYPROXY + printf(" XTinyproxy Header\n"); +#endif /* XTINYPROXY */ +#ifdef FILTER_ENABLE + printf(" Filtering\n"); + printf(" * with Regular Expression support\n"); +#endif /* FILTER_ENABLE */ +#ifndef NDEBUG + printf(" Debuggin code\n"); +#endif /* NDEBUG */ +} + +int main(int argc, char **argv) +{ + int optch; + flag usage = FALSE, godaemon = TRUE, changeid = FALSE; + struct passwd *thisuser = NULL; + + struct allowedhdr_s **rpallowedptr = &allowedhdrs; + struct allowedhdr_s *allowedptr = allowedhdrs, *newallowed; + + while ((optch = getopt(argc, argv, "vh?dp:l:Sa:w:s:u:n:i:rx:f:")) != + EOF) { + switch (optch) { + case 'v': + fprintf(stderr, "tinyproxy version " VERSION "\n"); + exit(EX_OK); + break; + case 'p': + if (!(config.port = atoi(optarg))) { + log + ("bad port on commandline, defaulting to %d", + DEFAULT_PORT); + config.port = DEFAULT_PORT; + } + break; + case 'l': + if (!(config.logf_name = xstrdup(optarg))) { + log("bad log file, defaulting to %s", + DEFAULT_LOG); + config.logf_name = DEFAULT_LOG; + } + break; +#ifdef HAVE_SYSLOG_H + case 'S': /* Use the syslog function to handle logging */ + config.syslog = TRUE; + break; +#endif + case 'd': + godaemon = FALSE; + break; + case 'w': + sscanf(optarg, "%f", &config.cutoffload); + break; + case 's': + if (!(config.stathost = xstrdup(optarg))) { + log("bad stathost, defaulting to %s", + DEFAULT_STATHOST); + config.stathost = DEFAULT_STATHOST; + } + break; + case 'u': + if (!(config.changeuser = xstrdup(optarg))) { + log("bad user name, defaulting to %s", + DEFAULT_USER); + config.changeuser = DEFAULT_USER; + } + break; + case 'a': + config.anonymous = TRUE; + + while (allowedptr) { + rpallowedptr = &allowedptr->next; + allowedptr = allowedptr->next; + } + + if (! + (newallowed = + xmalloc(sizeof(struct allowedhdr_s)))) { + log("tinyproxy: cannot allocate headers"); + exit(EX_SOFTWARE); + } + + if (!(newallowed->hdrname = xstrdup(optarg))) { + log("tinyproxy: cannot duplicate string"); + exit(EX_SOFTWARE); + } + + *rpallowedptr = newallowed; + newallowed->next = allowedptr; + + break; + case 'n': + if (!(config.subnet = xstrdup(optarg))) { + log("tinyproxy: could not allocate memory"); + exit(EX_SOFTWARE); + } + break; + case 'i': + if (!(config.ipAddr = xstrdup(optarg))) { + log("tinyproxy: could not allocate memory"); + exit(EX_SOFTWARE); + } + break; +#ifdef FILTER_ENABLE + case 'f': + if (!(config.filter = xstrdup(optarg))) { + log("tinyproxy: could not allocate memory"); + } + break; +#endif /* FILTER_ENABLE */ + case 'r': + config.restricted = TRUE; + break; +#ifdef XTINYPROXY + case 'x': + if (!(config.my_domain = xstrdup(optarg))) { + log("tinyproxy: could not allocate memory"); + exit(EX_SOFTWARE); + } + break; +#endif + case '?': + case 'h': + default: + usage = TRUE; + break; + } + } + + if (usage == TRUE) { + usagedisp(); + exit(EX_OK); + } + + /* chris - Initialise asynchronous DNS */ + if (adns_init(&adns, 0, 0)) { + log("tinyproxy: could not initialise ADNS"); + exit(EX_SOFTWARE); + } + + /* Open the log file if not using syslog */ + if (config.syslog == FALSE) { + if (!(config.logf = fopen(config.logf_name, "a"))) { + fprintf(stderr, + "Unable to open logfile %s for appending!\n", + config.logf_name); + exit(EX_CANTCREAT); + } + } else { + if (godaemon == TRUE) + openlog("tinyproxy", LOG_PID, LOG_DAEMON); + else + openlog("tinyproxy", LOG_PID, LOG_USER); + } + + log(PACKAGE " " VERSION " starting..."); + + if (strlen(config.changeuser)) { + if ((getuid() != 0) && (geteuid() != 0)) { + log + ("not running as root, therefore not changing uid/gid."); + } else { + changeid = TRUE; + if (!(thisuser = getpwnam(config.changeuser))) { + log("unable to find user \"%s\"!", + config.changeuser); + exit(EX_NOUSER); + } + log("changing to user \"%s\" (%d/%d).", + config.changeuser, thisuser->pw_uid, + thisuser->pw_gid); + } + } +#ifdef NDEBUG + if (godaemon == TRUE) + makedaemon(); +#else + printf("Debugging is enabled, so you can not go daemon.\n"); +#endif + + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { + fprintf(stderr, "Could not set SIGPIPE\n"); + exit(EX_OSERR); + } + if (signal(SIGUSR1, takesig) == SIG_ERR) { + fprintf(stderr, "Could not set SIGUSR1\n"); + exit(EX_OSERR); + } + if (signal(SIGTERM, takesig) == SIG_ERR) { + fprintf(stderr, "Could not set SIGTERM\n"); + exit(EX_OSERR); + } + if (signal(SIGHUP, takesig) == SIG_ERR) { + fprintf(stderr, "Could not set SIGHUP\n"); + exit(EX_OSERR); + } + if (signal(SIGALRM, takesig) == SIG_ERR) { + fprintf(stderr, "Could not set SIGALRM\n"); + exit(EX_OSERR); + } + alarm(LOAD_RECALCTIMER); + calcload(); + + if (init_listen_sock(config.port) < 0) { + log("unable to bind port %d!", config.port); + exit(EX_UNAVAILABLE); + } + if (changeid == TRUE) { + setuid(thisuser->pw_uid); + setgid(thisuser->pw_gid); + } + log("now accepting connections."); + +#ifdef FILTER_ENABLE + if (config.filter) + filter_init(); +#endif /* FILTER_ENABLE */ + + while (config.quit == FALSE) { + if (getreqs() < 0) + break; + } + +#ifdef FILTER_ENABLE + if (config.filter) + filter_destroy(); +#endif /* FILTER_ENABLE */ + + log("shutting down."); + de_init_listen_sock(); + + if (config.syslog == FALSE) + fclose(config.logf); + else + closelog(); + + allowedptr = allowedhdrs; + while (allowedptr) { + struct allowedhdr_s *delptr = NULL; + delptr = allowedptr; + safefree(delptr->hdrname); + allowedptr = delptr->next; + safefree(delptr); + } + + /* finsih up ADNS */ + adns_finish(adns); + + exit(EX_OK); +} |