/* main.c - Command line parsing, intialisation and server start Copyright (C) 2000, 2001 Thomas Moestl Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010 Paul A. Rombouts This file is part of the pdnsd package. pdnsd 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 3 of the License, or (at your option) any later version. pdnsd 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 pdnsd; see the file COPYING. If not, see . */ /* in order to use O_NOFOLLOW on Linux: */ /* #define _GNU_SOURCE */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "consts.h" #include "cache.h" #include "status.h" #include "servers.h" #include "dns_answer.h" #include "dns_query.h" #include "error.h" #include "helpers.h" #include "icmp.h" #include "hash.h" #if DEBUG>0 short int debug_p=0; #endif short int stat_pipe=0; /* int sigr=0; */ #if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) short int run_ipv4=DEFAULT_IPV4; short int cmdlineipv=0; #endif cmdlineflags_t cmdline={0}; pthread_t main_thrid,servstat_thrid,statsock_thrid,tcps_thrid,udps_thrid; uid_t init_uid; #if DEBUG>0 FILE *dbg_file=NULL; #endif volatile int tcp_socket=-1; volatile int udp_socket=-1; sigset_t sigs_msk; char *conf_file=CONFDIR"/pdnsd.conf"; /* version and licensing information */ static const char info_message[] = "pdnsd - dns proxy daemon, version " VERSION "\n\n" "Copyright (C) 2000, 2001 Thomas Moestl\n" "Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010 Paul A. Rombouts\n\n" "pdnsd is free software; you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License as published by\n" "the Free Software Foundation; either version 3 of the License, or\n" "(at your option) any later version.\n\n" "pdnsd is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n\n" "You should have received a copy of the GNU General Public License\n" "along with pdsnd; see the file COPYING. If not, see\n" ".\n"; /* the help page */ static const char help_message[] = "\n\nUsage: pdnsd [-h] [-V] [-s] [-d] [-g] [-t] [-p file] [-vn] [-mxx] [-c file]" #ifdef ENABLE_IPV4 " [-4]" #endif #ifdef ENABLE_IPV6 " [-6] [-i prefix]" #endif #if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) " [-a]" #endif "\n\n" "Options:\n" "-h\t\t--or--\n" "--help\t\tprint this help page and exit.\n" "-V\t\t--or--\n" "--version\tprint version and license information and exit.\n" "--pdnsd-user\tprint the user pdnsd will run as and exit.\n" "-s\t\t--or--\n" "--status\tEnable status control socket in the cache directory.\n" "-d\t\t--or--\n" "--daemon\tStart pdnsd in daemon mode (as background process.)\n" "-g\t\t--or--\n" "--debug\t\tPrint some debug messages on the console or to the\n" "\t\tfile pdnsd.debug in your cache directory (in daemon mode).\n" "-t\t\t--or--\n" "--tcp\t\tEnables the TCP server thread. pdnsd will then serve\n" "\t\tTCP and UDP queries.\n" "-p\t\tWrites the pid the server runs as to a specified filename.\n" "\t\tWorks only in daemon mode.\n" "-vn\t\tsets the verbosity of pdnsd. n is a numeric argument from 0\n" "\t\t(normal operation) to 9 (many messages for debugging).\n" "\t\tUse like -v2\n" "-mxx\t\tsets the query method pdnsd uses. Possible values for xx are:\n" "\t\tuo (UDP only), to (TCP only), tu (TCP or, if the server\n" "\t\tdoes not support this, UDP) and ut (UDP and, if the reply was\n" "\t\ttruncated, TCP). Use like -muo. Preset: " #if M_PRESET==UDP_ONLY "-muo" #elif M_PRESET==TCP_ONLY "-mto" #elif M_PRESET==TCP_UDP "-mtu" #else "-mut" #endif "\n" "-c\t\t--or--\n" "--config-file\tspecifies the file the configuration is read from.\n" "\t\tDefault is " CONFDIR "/pdnsd.conf\n" #ifdef ENABLE_IPV4 "-4\t\tswitches to IPv4 mode.\n" "\t\t" # if DEFAULT_IPV4 "On" # else "Off" # endif " by default.\n" #endif #ifdef ENABLE_IPV6 "-6\t\tswitches to IPv6 mode.\n" "\t\t" # if DEFAULT_IPV4 "Off" # else "On" # endif " by default.\n" "-i\t\t--or--\n" "--ipv4_6_prefix\tspecifies the prefix pdnsd uses to map IPv4 to IPv6\n" "\t\taddresses. Must be a valid IPv6 address.\n" "\t\tDefault is " DEFAULT_IPV4_6_PREFIX "\n" #endif #if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) "-a\t\tWith this option, pdnsd will try to detect automatically if\n" "\t\tthe system supports IPv6, and revert to IPv4 otherwise.\n" #endif "\n\n\"no\" can be prepended to the --status, --daemon, --debug and --tcp\n" "options (e.g. --notcp) to reverse their effect.\n"; /* These are some init steps we have to call before we get daemon on linux, but need * to call after daemonizing on other OSes. * Theay are also the last steps before we drop privileges. */ int final_init() { #ifndef NO_TCP_SERVER if (!global.notcp) tcp_socket=init_tcp_socket(); #endif udp_socket=init_udp_socket(); if (tcp_socket==-1 && udp_socket==-1) { log_error("tcp and udp initialization failed. Exiting."); return 0; } if (global.strict_suid) { if (!run_as(global.run_as)) { return 0; } } return 1; } #if defined(ENABLE_IPV4) && defined(ENABLE_IPV6) /* Check if IPv6 is available. * With thanks to Juliusz Chroboczek. */ static int check_ipv6() { int fd; fd = socket(PF_INET6, SOCK_STREAM, 0); if(fd < 0) { if(errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT || errno == EINVAL) return 0; return -1; } close(fd); return 1; } #endif /* * Argument parsing, init, server startup */ int main(int argc,char *argv[]) { int i,sig,pfd=-1; /* Initialized to inhibit compiler warning */ main_thrid=pthread_self(); servstat_thrid=main_thrid; statsock_thrid=main_thrid; tcps_thrid=main_thrid; udps_thrid=main_thrid; init_uid=getuid(); #ifdef ENABLE_IPV6 { int err; if((err=inet_pton(AF_INET6,DEFAULT_IPV4_6_PREFIX,&global.ipv4_6_prefix))<=0) { fprintf(stderr,"Error: inet_pton() wont accept default prefix %s in %s, line %d\n", DEFAULT_IPV4_6_PREFIX,__FILE__,__LINE__); if(err) perror("inet_pton"); exit(1); } } #endif /* Parse the command line. Remember which options were specified here, because the command-line options shall override the ones given in the config file */ for (i=1;ipw_name); else printf("%i\n",uid); } exit(0); } if(!global.cache_dir) global.cache_dir = CACHEDIR; if(!global.scheme_file) global.scheme_file = "/var/lib/pcmcia/scheme"; stat_pipe=global.stat_pipe; if (!(global.run_as[0] && global.strict_suid)) { for (i=0; iuptest==C_EXEC && sp->uptest_usr[0]=='\0') { uid_t uid=getuid(); struct passwd *pws=getpwuid(uid); /* No explicit uptest user given. If we run_as and strict_suid, we assume that * this is safe. If not - warn. */ fprintf(stderr,"Warning: uptest command \"%s\" will implicitly be executed as user ", sp->uptest_cmd); if (pws) fprintf(stderr,"%s\n",pws->pw_name); else fprintf(stderr,"%i\n",uid); } } } if (global.daemon && global.pidfile) { if (unlink(global.pidfile)!=0 && errno!=ENOENT) { log_error("Error: could not unlink pid file %s: %s",global.pidfile, strerror(errno)); exit(1); } if ((pfd=open(global.pidfile,O_WRONLY|O_CREAT|O_EXCL #ifdef O_NOFOLLOW |O_NOFOLLOW #else /* * No O_NOFOLLOW. Nevertheless, this not a hole, since the * directory for pidfiles should not be world writeable. * OS's that do not support O_NOFOLLOW are currently not * supported, this is just-in-case code. */ #endif , 0600))==-1) { log_error("Error: could not open pid file %s: %s",global.pidfile, strerror(errno)); exit(1); } } for (i=0;i0 if (global.debug) { char dbgpath[strlen(global.cache_dir)+sizeof("/pdnsd.debug")]; stpcpy(stpcpy(dbgpath,global.cache_dir),"/pdnsd.debug"); if (!(dbg_file=fopen(dbgpath,"w"))) log_warn("Warning: could not open debug file %s: %s",dbgpath, strerror(errno)); } #endif } else { #if DEBUG>0 dbg_file=stdout; #endif } #if DEBUG>0 debug_p= (global.debug && dbg_file); #endif log_info(0,"pdnsd-%s starting.\n",VERSION); DEBUG_MSG("Debug messages activated\n"); #if (TARGET!=TARGET_LINUX) if (!final_init()) _exit(1); #endif DEBUG_MSG(SEL_IPVER("Using IPv4.\n", "Using IPv6.\n")); /* initialize attribute for creating detached threads */ pthread_attr_init(&attr_detached); pthread_attr_setdetachstate(&attr_detached,PTHREAD_CREATE_DETACHED); read_disk_cache(); /* This must be done before any other thread is started to avoid races. */ if (stat_pipe) init_stat_sock(); /* Before this point, logging and cache accesses are not locked because we are single-threaded. */ init_log_lock(); init_cache_lock(); sigemptyset(&sigs_msk); sigaddset(&sigs_msk,SIGHUP); sigaddset(&sigs_msk,SIGINT); #ifndef THREADLIB_NPTL sigaddset(&sigs_msk,SIGILL); #endif sigaddset(&sigs_msk,SIGABRT); sigaddset(&sigs_msk,SIGFPE); #ifndef THREADLIB_NPTL sigaddset(&sigs_msk,SIGSEGV); #endif sigaddset(&sigs_msk,SIGTERM); /* if (!daemon_p) { sigaddset(&sigs_msk,SIGQUIT); } */ #if (TARGET==TARGET_LINUX) pthread_sigmask(SIG_BLOCK,&sigs_msk,NULL); #endif #if DEBUG>0 { int err; /* Generate a key for storing our thread id's */ if ((err=pthread_key_create(&thrid_key, NULL)) != 0) { log_error("pthread_key_create failed: %s",strerror(err)); _exit(1); } } #endif { #if DEBUG>0 int thrdsucc=1; # define thrdfail (thrdsucc=0) #else # define thrdfail #endif if(start_servstat_thread()) thrdfail; #if (TARGET==TARGET_LINUX) if (!global.strict_suid) { if (!run_as(global.run_as)) { _exit(1); } } #endif if (stat_pipe) if(start_stat_sock()) thrdfail; start_dns_servers(); #if DEBUG>0 if(thrdsucc) { DEBUG_MSG("All threads started successfully.\n"); } #endif #undef thrdfail } #if (TARGET==TARGET_LINUX) && !defined(THREADLIB_NPTL) pthread_sigmask(SIG_BLOCK,&sigs_msk,NULL); waiting=1; #endif { int err; while ((err=sigwait(&sigs_msk,&sig))) { if (err!=EINTR) { log_error("sigwait failed: %s",strerror(err)); sig=0; break; } } } if(sig) DEBUG_MSG("Signal %i caught.\n",sig); write_disk_cache(); destroy_cache(); if(sig) log_warn("Caught signal %i. Exiting.",sig); if (sig==SIGSEGV || sig==SIGILL || sig==SIGBUS) crash_msg("This is a fatal signal probably triggered by a bug."); if (ping_isocket!=-1) close(ping_isocket); #ifdef ENABLE_IPV6 if (ping6_isocket!=-1) close(ping6_isocket); #endif /* Close and delete the status socket */ if(stat_pipe) close(stat_sock); if (sock_path && unlink(sock_path)) log_warn("Failed to unlink %s: %s",sock_path, strerror(errno)); free_rng(); #if DEBUG>0 if (debug_p && global.daemon) if(fclose(dbg_file)<0) { log_warn("Could not close debug file: %s", strerror(errno)); } #endif _exit(0); }