/****************************************************************************
* Copyright (C) 1998 WIDE Project. All rights reserved.
* Copyright (C) 1999,2000,2001,2002 University of Tromso. All rights reserved.
* Copyright (C) 2002 Invenia Innovation AS. All rights reserved.
*
* Author: Feike W. Dillema, feico@pasta.cs.uit.no.
* based on newbie code by Yusuke DOI, Keio Univ. Murai Lab.
****************************************************************************/
/*
* <$Id: totd.c,v 3.74 2005/02/02 11:10:31 dillema Exp $>
*/
#include "totd.h"
struct ToT T;
char *version = "Trick or Treat Daemon (totd) version 1.5";
void usage () {
printf ("%s\n\
Usage: totd [-6|-no6|-4|-no4|-64|-dn|-v|-q|-p <prefix>|-c <filename>]\n\
\n\
-[no]6 : enable[disable] IPv6 service functionality\n\
-[no]4 : enable[disable] IPv4 service functionality\n\
-64 : alias to -6 -4\n\
-dn : debug mode (no fork / loglevel = n)\n\
-v : verbose\n\
-q : quiet\n\
-u <user> : username or uid totd should run at, after startup\n\
-g <group> : groupname or gid totd should run at, after startup\n\
-t <dir> : put totd in <dir> chroot() cage\n\
-p <prefix> : a prefix to use for totd tricks; more than one allowed\n\
-http-port <port> : port we listen on for http requests (default = 6464)\n\
-c <file> : specify alternative totd configfile, default=%s\n\
\n\
default : IPv6 is %s and IPv4 is %s\n\n\
totd %s use IPv6 because it was compiled %s USE_INET6 option in config.h\n\
totd %s use IPv4 because it was compiled %s USE_INET4 option in config.h\n",
version,
TOTCONF,
(T.ip6) ? "enabled" : "disabled",
(T.ip4) ? "enabled" : "disabled",
(V4 (1) + 0) ? "can" : "can not",
(V4 (1) + 0) ? "with" : "without",
(V6 (1) + 0) ? "can" : "can not",
(V6 (1) + 0) ? "with" : "without"
);
totd_exit (EXIT_FAILURE);
}
const char *hex = "0123456789abcdef";
int main (int argc, char **argv) {
struct passwd *pwd_p;
int i;
/* initialize global totd structure */
T.uid = getuid();
T.gid = getgid();
T.user = NULL;
T.group = NULL;
T.rootdir = NULL;
/* fill in default config values */
T.ip4 = V4 (1) + 0; /* bit ugly, but short */
T.ip6 = V6 (1) + 0;
T.use_mapped = 0;
T.quiet = 0;
T.debug = 0;
T.prefixnum = 0;
T.rescan_iflist = 1;
T.stf = 0;
T.retry_interval = 300;
T.port = PORT_SRV;
T.http_port = 0;
T.wildcard = 1;
T.Fwd_list = NULL;
T.pidfile = TOT_PID_FILE;
T.configfile = TOTCONF;
T.current_fwd = NULL;
/* make sure these start out empty */
for (i = 0; i < MAXPREFIXES; i++) {
T.prefix[i][0] = '\0';
}
/* list of forwarders */
T.Fwd_list = list_init ();
if (!T.Fwd_list)
exit(1);
/* parse command line arguments */
for (i = 1; i < argc; i++) {
if (!strncmp (argv[i], "-d", 2) ) {
/* debug option */
T.debug = atoi(&argv[i][2]);
syslog (LOG_INFO, "debug level %d enabled", T.debug);
} else if (!strcmp (argv[i], "-6"))
T.ip6 = 1;
else if (!strcmp (argv[i], "-4"))
T.ip4 = 1;
else if (!strcmp (argv[i], "-46") || !strcmp (argv[i], "-64"))
T.ip4 = 1, T.ip6 = 1;
else if (!strcmp (argv[i], "-no4"))
T.ip4 = 0;
else if (!strcmp (argv[i], "-no6"))
T.ip6 = 0;
else if (!strcmp (argv[i], "-c"))
T.configfile = strdup(argv[++i]);
else if (!strcmp (argv[i], "-p")) {
if (conv_trick_conf((u_char *)argv[++i])) {
syslog (LOG_ERR, "invalid prefix on command line: %s", argv[++i]);
usage(1);
}
}
else if (!strcmp (argv[i], "-http-port")) {
T.http_port = atoi(argv[++i]);
if (!T.http_port) {
syslog (LOG_ERR, "invalid portnumer: %s", argv[++i]);
usage(1);
}
} else if (!strcmp (argv[i], "-u"))
T.user = strdup(argv[++i]);
else if (!strcmp (argv[i], "-g"))
T.group = strdup(argv[++i]);
else if (!strcmp (argv[i], "-t"))
T.rootdir = strdup(argv[++i]);
else if (!strcmp (argv[i], "-h"))
usage (1);
else if (!strcmp (argv[i], "-v"))
T.quiet = -1;
else if (!strcmp (argv[i], "-q"))
T.quiet = 1;
else {
syslog (LOG_ERR, "unknown option %s", argv[i]);
usage (1);
}
}
#ifndef LOG_PERROR
#define LOG_PERROR 0
#endif /* not LOG_PERROR */
if (T.debug > 0)
openlog("totd", LOG_PID | LOG_NDELAY | LOG_CONS | LOG_PERROR, LOG_DAEMON);
else
openlog ("totd", LOG_PID | LOG_NDELAY | LOG_CONS, LOG_DAEMON);
syslog (LOG_NOTICE, "%s", version);
if (T.debug) {
if (T.quiet > 0)
setlogmask (LOG_UPTO (LOG_ERR));
else if (!T.quiet)
setlogmask (LOG_UPTO (LOG_INFO));
else
setlogmask (LOG_UPTO (LOG_DEBUG));
} else {
if (T.quiet > 0)
setlogmask (LOG_UPTO (LOG_ERR));
else if (!T.quiet)
setlogmask (LOG_UPTO (LOG_WARNING));
else if (T.quiet < 0)
setlogmask (LOG_UPTO (LOG_INFO));
}
if (T.user) {
if (isdigit(T.user[0])) {
T.uid = atoi(T.user);
pwd_p = NULL;
} else {
pwd_p = getpwnam (T.user);
}
} else
pwd_p = getpwuid (T.uid);
if (pwd_p) {
syslog (LOG_INFO, "Found user record of %s; uid: %d gid: %d",
pwd_p->pw_name, pwd_p->pw_uid, pwd_p->pw_gid);
if (T.uid && T.uid != pwd_p->pw_uid) {
syslog (LOG_ERR, "Need root privileges to change user \
to: %s", T.user);
totd_exit (EXIT_FAILURE);
} else {
T.uid = pwd_p->pw_uid;
}
} else {
syslog (LOG_INFO, "can't find user record of %s", T.user);
}
if (T.group) {
if (isdigit(T.group[0])) {
T.gid = atoi(T.group);
} else {
struct group *grp_p;
grp_p = getgrnam(T.group);
if (!grp_p) {
syslog(LOG_ERR, "group `%s' unknown", T.group);
totd_exit (EXIT_FAILURE);
}
T.gid = grp_p->gr_gid;
}
} else {
if (pwd_p)
T.gid = pwd_p->pw_gid;
else
T.gid = getgid();
}
/* close any open descriptors */
endpwent();
endgrent();
/* chroot() cage totd if asked for */
if (T.rootdir) {
if (chroot(T.rootdir) < 0) {
syslog (LOG_ERR, "chroot %s failed: %m", T.rootdir);
totd_exit(EXIT_FAILURE);
}
syslog (LOG_INFO, "chrooted to %s", T.rootdir);
if (chdir("/") < 0) {
syslog (LOG_ERR, "chdir(\"/\") failed: %m");
totd_exit(EXIT_FAILURE);
}
}
#ifdef SWILL
/* Initialize the SWILL server */
if (T.http_port) {
swill_init(T.http_port);
syslog (LOG_INFO, "Listening on [*]:%d for http requests", T.http_port);
swill_deny("");
}
#endif
/*
* read in config file
* possibly overriding defaults and command line options
*/
if (read_config (T.configfile)) {
syslog (LOG_ERR, "Configuration failure");
syslog (LOG_INFO, "Check log, or try -d option for debug mode...");
totd_exit (EXIT_FAILURE);
}
/* we cannot rebind to reserved ports if we are not root */
if (T.rescan_iflist == 1 && T.uid && T.port < IPPORT_RESERVED) {
syslog(LOG_WARNING, "Disabling rescanning of network interfaces");
T.rescan_iflist = 0;
}
/* Check for pidfile conflicts */
if (T.pidfile) {
FILE *pid_fp;
pid_t pid;
/* check if pid file exists */
pid_fp = fopen (T.pidfile, "r");
if (pid_fp) {
if (fscanf (pid_fp, "%d", &pid) != 1) {
syslog (LOG_NOTICE, "Removing bogus lockfile");
unlink (T.pidfile);
} else if (kill (pid, 0) == -1 && !unlink (T.pidfile)) {
syslog (LOG_NOTICE, "Removed stale lockfile");
} else {
syslog (LOG_ERR, "PID file %s already exists",
T.pidfile);
syslog (LOG_INFO, "There can be another totd \
running. Please make sure there's no totd already running. Then delete the \
file %s and try again.", T.pidfile);
fclose (pid_fp);
totd_exit (EXIT_FAILURE);
}
fclose (pid_fp);
}
}
if (!T.ip4 && T.ip6) {
/* If user specifies we should *not* accept IPv4
* requests on Linux, we will bail out with an error.
* totd does not filter incoming requests on their source
* address. It is not totd's task to do so IMHO, and I will
* probably never implement that.
*/
#ifdef NEEDSV4MAPPED
syslog(LOG_ERR, "Cannot disable IPv4 when IPv6 is enabled on\
this OS, due to IPv4 mapped addresses");
syslog(LOG_INFO, "Will always accept IPv4 *and* IPv6 requests.\
Bailing out, so that you can explicitly tell me to do so");
totd_exit (EXIT_FAILURE);
#endif
}
if (T.ip4 && T.ip6) {
#ifdef WILDCARDONLY
if (!T.wildcard) {
syslog(LOG_ERR, "On this OS we only support \
wildcard binding when IPv6 is enabled.");
syslog(LOG_INFO, "Please remove `interfaces' \
specification in your config file.");
totd_exit (EXIT_FAILURE);
}
#endif
#ifdef NEEDSV4MAPPED
syslog(LOG_DEBUG, "IPv6 wildcard socket with IPv4 mapped, \
will not bind to wildcard IPv4 socket.");
T.use_mapped = 1;
#endif
}
if (T.ip4) {
#ifdef USE_INET4
syslog (LOG_INFO, "IPv4 activated");
#else
syslog (LOG_ERR, "IPv4 support is not compiled in");
totd_exit (EXIT_FAILURE);
#endif
}
if (T.ip6) {
#ifdef USE_INET6
syslog (LOG_INFO, "IPv6 activated");
#else
syslog (LOG_ERR, "IPv6 support is not compiled in");
totd_exit (EXIT_FAILURE);
#endif
}
if (!T.ip4 && !T.ip6) {
syslog (LOG_ERR, "all supported protocols are deactivated; \
what do you want me to do then?");
totd_exit (EXIT_FAILURE);
}
#ifdef SCOPED_REWRITE
if (T.wildcard && T.scoped_prefixes) {
syslog (LOG_ERR, "Scoped address rewriting currently not \
implemented when wildcard sockets are used. Please use `interfaces' keyword \
in your config file or remove `scoped' keyword");
totd_exit (EXIT_FAILURE);
}
#endif
fwd_init();
fwd_select();
if (!T.current_fwd) {
syslog (LOG_ERR, "no forwarder available, what should we do then?");
return -1;
}
/* initialize each event routine */
ev_dup_init ();
if (ev_signal_init () < 0) {
syslog (LOG_ERR, "Signal event handling initialize failed");
totd_exit (EXIT_FAILURE);
}
if (ev_to_init () < 0) {
syslog (LOG_ERR, "Timeout event handling initialize failed");
totd_exit (EXIT_FAILURE);
}
if (ev_tcp_conn_in_init () < 0) {
syslog (LOG_ERR, "TCP connection initialize failed");
totd_exit (EXIT_FAILURE);
}
if (net_init_socketlist(T.port) < 0) {
syslog (LOG_ERR, "Init list of sockets failed");
totd_exit (EXIT_FAILURE);
}
if (net_bind_socketlist() <= 0) {
if (!T.rescan_iflist) {
syslog (LOG_ERR, "Could not open any sockets");
totd_exit (EXIT_FAILURE);
} else {
syslog (LOG_WARNING, "Could not open any sockets");
syslog (LOG_WARNING, "Maybe later??? Continuing");
}
}
if (ev_udp_in_init () < 0) {
syslog (LOG_ERR, "UDP initialize failed");
totd_exit (EXIT_FAILURE);
}
if (ev_tcp_out_init () < 0) {
syslog (LOG_ERR, "TCP output routine initialize failed");
totd_exit (EXIT_FAILURE);
}
/* drop root privs */
if (setgid(T.gid) < 0) {
syslog (LOG_ERR, "setgid to %d failed", T.gid);
totd_exit (EXIT_FAILURE);
}
if (setuid(T.uid) < 0) {
syslog (LOG_ERR, "setuid to %d failed", T.uid);
totd_exit (EXIT_FAILURE);
}
if (T.rescan_iflist) {
if (ev_to_register_ifcheck () < 0) {
syslog (LOG_ERR, "Registering Interface Check Event failed");
totd_exit (EXIT_FAILURE);
}
}
if (!T.debug) {
if (daemon(0,0))
totd_exit (EXIT_FAILURE);
else
syslog (LOG_INFO, "totd successfully daemonized");
}
/* as detached child, we can now write pid file */
if (T.pidfile) {
FILE *pid_fp;
pid_fp = fopen (T.pidfile, "w");
if (!pid_fp) {
syslog (LOG_ERR, "can't open pid file \"%s\"", T.pidfile);
totd_exit (EXIT_FAILURE);
}
fprintf (pid_fp, "%d", getpid ());
syslog (LOG_INFO, "wrote pid %d to file %s", getpid (), T.pidfile);
fclose (pid_fp);
}
#ifdef SWILL
if (T.http_port) {
if (T.debug) swill_log(stderr);
swill_title("Trick or Treat DNS Proxy");
swill_handle("index.html", print_stats, 0);
swill_handle("add-prefix.html", add_prefix, 0);
swill_handle("del-prefix.html", del_prefix, 0);
}
#endif
syslog (LOG_INFO, "totd started");
totd_eventloop ();
return (totd_exit (0));
}
void totd_eventloop (void) {
const char *fn = "totd_eventloop()";
struct timeval tv_out, *tvp;
fd_set fd_read, fd_write;
int max_fd, fdnum, i;
time_t next_timeout;
#ifdef DBMALLOC
unsigned long histid1, histid2, orig_size, current_size;
/* Malloc debugging */
if (T.debug)
malloc_dump (2);
orig_size = malloc_inuse (&histid1);
syslog (LOG_DEBUG, "Malloc Size: %ld", orig_size);
#endif
while (1) { /* main loop */
if (T.debug > 2)
syslog (LOG_DEBUG, "main loop: start");
/* pick a proper forwarder */
fwd_select ();
#ifdef DBMALLOC
/* Malloc debugging */
current_size = malloc_inuse (&histid2);
if (current_size != orig_size) {
syslog (LOG_DEBUG, "Malloc Size: %ld", current_size);
if (T.debug)
malloc_list (2, histid1, histid2);
orig_size = current_size;
/* histid1 = histid2; */
}
#endif
#ifdef SWILL
if (T.http_port) swill_poll();
#endif
/* signal event */
ev_signal_process ();
/* timeout event */
#ifdef SWILL
if (T.http_port) {
tv_out.tv_usec = 500;
tv_out.tv_sec = 0;
tvp = &tv_out;
} else
#endif
{
next_timeout = ev_timeout_process ();
if (!next_timeout) {
if (T.debug > 2)
syslog (LOG_DEBUG, "no timeouts at present");
tvp = NULL;
} else {
tv_out.tv_usec = 0;
tv_out.tv_sec = next_timeout - time (NULL);
tvp = &tv_out;
if (T.debug > 2)
syslog (LOG_DEBUG, "next timeout after %ld s.",
(long)tv_out.tv_sec);
}
}
/* get FD_SET for now */
max_fd = 0;
FD_ZERO (&fd_read);
FD_ZERO (&fd_write);
if (T.debug > 3)
syslog (LOG_DEBUG, "check for UDP fds...");
nia_fds_set (&fd_read, &max_fd);
if (T.debug > 3)
syslog (LOG_DEBUG, "check for TCP-in fds...");
i = ev_tcp_conn_in_fds (&fd_read);
max_fd = MAXNUM (i, max_fd);
if (T.debug > 3)
syslog (LOG_DEBUG, "check for TCP-out fds...");
i = ev_tcp_out_fds (&fd_write);
max_fd = MAXNUM (i, max_fd);
if (T.debug > 3)
syslog (LOG_DEBUG, "%s: max_fd = %d", fn, max_fd);
/* select */
if (T.debug > 2)
syslog (LOG_DEBUG, "main loop: select");
fdnum = select (max_fd + 1, &fd_read, &fd_write, NULL, tvp);
if (fdnum < 0) {
if (errno == EINTR) {
syslog (LOG_DEBUG, "%s: select() interrupted",
fn);
continue; /* while(1) */
} else {
syslog (LOG_ERR, "%s: select(): %m", fn);
if (net_reinit_socketlist (T.port, 1) < 0)
totd_exit (EXIT_FAILURE);
sleep (1);
}
} else {
int sock;
switch (nia_fds_isset (&fd_read, &sock)) {
case 0: /* UDP */
if (ev_udp_in_read (sock) < 0)
syslog (LOG_INFO, "udp service error");
continue;
case 1: /* TCP */
if (ev_tcp_srv_accept (sock) < 0)
syslog (LOG_INFO, "tcp service error");
continue;
default: /* not found */
if (ev_tcp_out_fd_check (&fd_write) < 0)
syslog (LOG_INFO, "tcp output failed");
if (ev_tcp_conn_in_fd_check (&fd_read) < 0)
syslog (LOG_INFO, "tcp input failed");
}
} /* if(...select...) */
} /* while(1) */
}
int totd_exit (int status) {
/* finish in reverse order of initialize */
ev_tcp_out_finish ();
ev_udp_in_finish ();
ev_tcp_conn_in_finish ();
/* ev_tcp_srv_in_finish() -- no such func */
ev_to_finish ();
ev_signal_finish ();
if (T.Fwd_list)
list_destroy (T.Fwd_list, fwd_freev);
if (T.pidfile)
unlink (T.pidfile);
if (status != EXIT_SUCCESS) {
syslog (LOG_ERR, "terminated with error");
fprintf (stderr, "totd terminated with error, \
check system logs for details or run totd in debug mode.\n");
}
exit (status);
}
syntax highlighted by Code2HTML, v. 0.9.1