2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "packetcache.hh"
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
40 #include <sys/types.h>
48 #include <boost/algorithm/string.hpp>
52 #include "opensslsigners.hh"
55 #include "dnsbackend.hh"
56 #include "ueberbackend.hh"
57 #include "dnspacket.hh"
58 #include "nameserver.hh"
59 #include "distributor.hh"
61 #include "arguments.hh"
62 #include "packethandler.hh"
64 #include "tcpreceiver.hh"
66 #include "dynlistener.hh"
67 #include "dynhandler.hh"
68 #include "communicator.hh"
69 #include "dnsproxy.hh"
71 #include "common_startup.hh"
72 #include "dnsrecords.hh"
77 string s_programname
="pdns"; // used in packethandler.cc
79 const char *funnytext
=
80 "*****************************************************************************\n"\
81 "Ok, you just ran pdns_server through 'strings' hoping to find funny messages.\n"\
82 "Well, you found one. \n"\
83 "Two ions are flying through their particle accelerator, says the one to the\n"
84 "other 'I think I've lost an electron!' \n"\
85 "So the other one says, 'Are you sure?'. 'YEAH! I'M POSITIVE!'\n"\
86 " the pdns crew - pdns@powerdns.com\n"
87 "*****************************************************************************\n";
95 \brief The main loop of powerdns
97 This file is where it all happens - main is here, as are the two pivotal threads qthread() and athread()
107 int i
=open("/dev/null",O_RDWR
); /* open stdin */
109 g_log
<<Logger::Critical
<<"Unable to open /dev/null: "<<stringerror()<<endl
;
111 dup2(i
,0); /* stdin */
112 dup2(i
,1); /* stderr */
113 dup2(i
,2); /* stderr */
119 static void takedown(int i
)
122 g_log
<<Logger::Error
<<"Guardian is killed, taking down children with us"<<endl
;
128 static void writePid(void)
130 if(!::arg().mustDo("write-pid"))
133 string fname
=::arg()["socket-dir"];
134 if (::arg()["socket-dir"].empty()) {
135 if (::arg()["chroot"].empty())
136 fname
= LOCALSTATEDIR
;
138 fname
= ::arg()["chroot"] + "/";
139 } else if (!::arg()["socket-dir"].empty() && !::arg()["chroot"].empty()) {
140 fname
= ::arg()["chroot"] + ::arg()["socket-dir"];
143 fname
+= + "/" + s_programname
+ ".pid";
144 ofstream
of(fname
.c_str());
148 g_log
<<Logger::Error
<<"Writing pid for "<<getpid()<<" to "<<fname
<<" failed: "<<strerror(errno
)<<endl
;
151 int g_fd1
[2], g_fd2
[2];
153 pthread_mutex_t g_guardian_lock
= PTHREAD_MUTEX_INITIALIZER
;
155 // The next two methods are not in dynhandler.cc because they use a few items declared in this file.
156 static string
DLCycleHandler(const vector
<string
>&parts
, pid_t ppid
)
158 kill(cpid
, SIGKILL
); // why?
159 kill(cpid
, SIGKILL
); // why?
164 static string
DLRestHandler(const vector
<string
>&parts
, pid_t ppid
)
168 for(vector
<string
>::const_iterator i
=parts
.begin();i
!=parts
.end();++i
) {
175 Lock
l(&g_guardian_lock
);
178 writen2(g_fd1
[1],line
.c_str(),line
.size()+1);
180 catch(PDNSException
&ae
) {
181 return "Error communicating with instance: "+ae
.reason
;
185 while(fgets(mesg
,sizeof(mesg
),g_fp
)) {
190 boost::trim_right(response
);
196 static int guardian(int argc
, char **argv
)
203 DynListener
dlg(s_programname
);
204 dlg
.registerFunc("QUIT",&DLQuitHandler
, "quit daemon");
205 dlg
.registerFunc("CYCLE",&DLCycleHandler
, "restart instance");
206 dlg
.registerFunc("PING",&DLPingHandler
, "ping guardian");
207 dlg
.registerFunc("STATUS",&DLStatusHandler
, "get instance status from guardian");
208 dlg
.registerRestFunc(&DLRestHandler
);
210 string progname
=argv
[0];
215 pthread_mutex_lock(&g_guardian_lock
);
219 setStatus("Launching child");
221 if(pipe(g_fd1
)<0 || pipe(g_fd2
)<0) {
222 g_log
<<Logger::Critical
<<"Unable to open pipe for coprocess: "<<strerror(errno
)<<endl
;
226 if(!(g_fp
=fdopen(g_fd2
[0],"r"))) {
227 g_log
<<Logger::Critical
<<"Unable to associate a file pointer with pipe: "<<stringerror()<<endl
;
230 setbuf(g_fp
,0); // no buffering please, confuses select
232 if(!(pid
=fork())) { // child
233 signal(SIGTERM
, SIG_DFL
);
235 signal(SIGHUP
, SIG_DFL
);
236 signal(SIGUSR1
, SIG_DFL
);
237 signal(SIGUSR2
, SIG_DFL
);
239 char **const newargv
=new char*[argc
+2];
242 if(::arg()["config-name"]!="") {
243 progname
+="-"+::arg()["config-name"];
244 g_log
<<Logger::Error
<<"Virtual configuration name: "<<::arg()["config-name"]<<endl
;
247 newargv
[0]=strdup(const_cast<char *>((progname
+"-instance").c_str()));
248 for(n
=1;n
<argc
;n
++) {
253 g_log
<<Logger::Error
<<"Guardian is launching an instance"<<endl
;
255 fclose(g_fp
); // this closes g_fd2[0] for us
257 if(g_fd1
[0]!= infd
) {
258 dup2(g_fd1
[0], infd
);
262 if(g_fd2
[1]!= outfd
) {
263 dup2(g_fd2
[1], outfd
);
266 if(execvp(argv
[0], newargv
)<0) {
267 g_log
<<Logger::Error
<<"Unable to execvp '"<<argv
[0]<<"': "<<strerror(errno
)<<endl
;
270 g_log
<<Logger::Error
<<*p
++<<endl
;
274 g_log
<<Logger::Error
<<"execvp returned!!"<<endl
;
277 else if(pid
>0) { // parent
283 signal(SIGTERM
, takedown
);
285 signal(SIGHUP
, SIG_IGN
);
286 signal(SIGUSR1
, SIG_IGN
);
287 signal(SIGUSR2
, SIG_IGN
);
291 pthread_mutex_unlock(&g_guardian_lock
);
295 int ret
=waitpid(pid
,&status
,WNOHANG
);
298 g_log
<<Logger::Error
<<"In guardian loop, waitpid returned error: "<<strerror(errno
)<<endl
;
299 g_log
<<Logger::Error
<<"Dying"<<endl
;
302 else if(ret
) // something exited
304 else { // child is alive
305 // execute some kind of ping here
307 takedown(1); // needs a parameter..
308 setStatus("Child running on pid "+itoa(pid
));
313 pthread_mutex_lock(&g_guardian_lock
);
318 if(WIFEXITED(status
)) {
319 int ret
=WEXITSTATUS(status
);
322 g_log
<<Logger::Error
<<"Child requested a stop, exiting"<<endl
;
325 setStatus("Child died with code "+itoa(ret
));
326 g_log
<<Logger::Error
<<"Our pdns instance exited with code "<<ret
<<", respawning"<<endl
;
331 if(WIFSIGNALED(status
)) {
332 int sig
=WTERMSIG(status
);
333 setStatus("Child died because of signal "+itoa(sig
));
334 g_log
<<Logger::Error
<<"Our pdns instance ("<<pid
<<") exited after signal "<<sig
<<endl
;
336 if(WCOREDUMP(status
))
337 g_log
<<Logger::Error
<<"Dumped core"<<endl
;
340 g_log
<<Logger::Error
<<"Respawning"<<endl
;
344 g_log
<<Logger::Error
<<"No clue what happened! Respawning"<<endl
;
347 g_log
<<Logger::Error
<<"Unable to fork: "<<strerror(errno
)<<endl
;
353 static void UNIX_declareArguments()
355 ::arg().set("config-dir","Location of configuration directory (pdns.conf)")=SYSCONFDIR
;
356 ::arg().set("config-name","Name of this virtual configuration - will rename the binary image")="";
357 ::arg().set("socket-dir",string("Where the controlsocket will live, ")+LOCALSTATEDIR
+" when unset and not chrooted" )="";
358 ::arg().set("module-dir","Default directory for modules")=PKGLIBDIR
;
359 ::arg().set("chroot","If set, chroot to this directory for more security")="";
360 ::arg().set("logging-facility","Log under a specific facility")="";
361 ::arg().set("daemon","Operate as a daemon")="no";
365 #include <execinfo.h>
366 static void tbhandler(int num
)
368 g_log
<<Logger::Critical
<<"Got a signal "<<num
<<", attempting to print trace: "<<endl
;
369 void *array
[20]; //only care about last 17 functions (3 taken with tracing support)
374 size
= backtrace (array
, 20);
375 strings
= backtrace_symbols (array
, size
); //Need -rdynamic gcc (linker) flag for this to work
377 for (i
= 0; i
< size
; i
++) //skip useless functions
378 g_log
<<Logger::Error
<<strings
[i
]<<endl
;
381 signal(SIGABRT
, SIG_DFL
);
382 abort();//hopefully will give core
387 //! The main function of pdns, the pdns process
388 int main(int argc
, char **argv
)
390 versionSetProduct(ProductAuthoritative
);
391 reportAllTypes(); // init MOADNSParser
393 s_programname
="pdns";
397 signal(SIGSEGV
,tbhandler
);
398 signal(SIGFPE
,tbhandler
);
399 signal(SIGABRT
,tbhandler
);
400 signal(SIGILL
,tbhandler
);
403 std::ios_base::sync_with_stdio(false);
405 g_log
.toConsole(Logger::Warning
);
408 UNIX_declareArguments();
410 ::arg().laxParse(argc
,argv
); // do a lax parse
412 if(::arg().mustDo("version")) {
413 showProductVersion();
414 showBuildConfiguration();
418 if(::arg()["config-name"]!="")
419 s_programname
+="-"+::arg()["config-name"];
421 g_log
.setName(s_programname
);
423 string configname
=::arg()["config-dir"]+"/"+s_programname
+".conf";
424 cleanSlashes(configname
);
426 if(!::arg().mustDo("config") && !::arg().mustDo("no-config")) // "config" == print a configuration file
427 ::arg().laxFile(configname
.c_str());
429 ::arg().laxParse(argc
,argv
); // reparse so the commandline still wins
430 if(!::arg()["logging-facility"].empty()) {
431 int val
=logFacilityToLOG(::arg().asNum("logging-facility") );
433 g_log
.setFacility(val
);
435 g_log
<<Logger::Error
<<"Unknown logging facility "<<::arg().asNum("logging-facility") <<endl
;
438 g_log
.setLoglevel((Logger::Urgency
)(::arg().asNum("loglevel")));
439 g_log
.disableSyslog(::arg().mustDo("disable-syslog"));
440 g_log
.setTimestamps(::arg().mustDo("log-timestamp"));
441 g_log
.toConsole((Logger::Urgency
)(::arg().asNum("loglevel")));
443 if(::arg().mustDo("help") || ::arg().mustDo("config")) {
444 ::arg().set("daemon")="no";
445 ::arg().set("guardian")="no";
448 if(::arg().mustDo("guardian") && !isGuarded(argv
)) {
449 if(::arg().mustDo("daemon")) {
450 g_log
.toConsole(Logger::Critical
);
453 guardian(argc
, argv
);
454 // never get here, guardian will reinvoke process
455 cerr
<<"Um, we did get here!"<<endl
;
459 // we really need to do work - either standalone or as an instance
462 if(!::arg().mustDo("traceback-handler")) {
463 g_log
<<Logger::Warning
<<"Disabling traceback handler"<<endl
;
464 signal(SIGSEGV
,SIG_DFL
);
465 signal(SIGFPE
,SIG_DFL
);
466 signal(SIGABRT
,SIG_DFL
);
467 signal(SIGILL
,SIG_DFL
);
471 #ifdef HAVE_LIBSODIUM
472 if (sodium_init() == -1) {
473 cerr
<<"Unable to initialize sodium crypto library"<<endl
;
478 openssl_thread_setup();
483 if(!::arg()["load-modules"].empty()) {
484 vector
<string
> modules
;
486 stringtok(modules
,::arg()["load-modules"], ", ");
487 if (!UeberBackend::loadModules(modules
, ::arg()["module-dir"])) {
492 BackendMakers().launch(::arg()["launch"]); // vrooooom!
494 if(!::arg().getCommands().empty()) {
495 cerr
<<"Fatal: non-option on the command line, perhaps a '--setting=123' statement missed the '='?"<<endl
;
499 if(::arg().mustDo("help")) {
500 cout
<<"syntax:"<<endl
<<endl
;
501 cout
<<::arg().helpstring(::arg()["help"])<<endl
;
505 if(::arg().mustDo("config")) {
506 cout
<<::arg().configstring()<<endl
;
510 if(::arg().mustDo("list-modules")) {
511 auto modules
= BackendMakers().getModules();
512 cout
<<"Modules available:"<<endl
;
513 for(const auto& m
: modules
)
519 if(!::arg().asNum("local-port")) {
520 g_log
<<Logger::Error
<<"Unable to launch, binding to no port or port 0 makes no sense"<<endl
;
521 exit(99); // this isn't going to fix itself either
523 if(!BackendMakers().numLauncheable()) {
524 g_log
<<Logger::Error
<<"Unable to launch, no backends configured for querying"<<endl
;
525 exit(99); // this isn't going to fix itself either
527 if(::arg().mustDo("daemon")) {
528 g_log
.toConsole(Logger::None
);
533 if(isGuarded(argv
)) {
534 g_log
<<Logger::Warning
<<"This is a guarded instance of pdns"<<endl
;
535 dl
=new DynListener
; // listens on stdin
538 g_log
<<Logger::Warning
<<"This is a standalone pdns"<<endl
;
540 if(::arg().mustDo("control-console"))
541 dl
=new DynListener();
543 dl
=new DynListener(s_programname
);
547 DynListener::registerFunc("SHOW",&DLShowHandler
, "show a specific statistic or * to get a list", "<statistic>");
548 DynListener::registerFunc("RPING",&DLPingHandler
, "ping instance");
549 DynListener::registerFunc("QUIT",&DLRQuitHandler
, "quit daemon");
550 DynListener::registerFunc("UPTIME",&DLUptimeHandler
, "get instance uptime");
551 DynListener::registerFunc("NOTIFY-HOST",&DLNotifyHostHandler
, "notify host for specific domain", "<domain> <host>");
552 DynListener::registerFunc("NOTIFY",&DLNotifyHandler
, "queue a notification", "<domain>");
553 DynListener::registerFunc("RELOAD",&DLReloadHandler
, "reload all zones");
554 DynListener::registerFunc("REDISCOVER",&DLRediscoverHandler
, "discover any new zones");
555 DynListener::registerFunc("VERSION",&DLVersionHandler
, "get instance version");
556 DynListener::registerFunc("PURGE",&DLPurgeHandler
, "purge entries from packet cache", "[<record>]");
557 DynListener::registerFunc("CCOUNTS",&DLCCHandler
, "get cache statistics");
558 DynListener::registerFunc("QTYPES", &DLQTypesHandler
, "get QType statistics");
559 DynListener::registerFunc("RESPSIZES", &DLRSizesHandler
, "get histogram of response sizes");
560 DynListener::registerFunc("REMOTES", &DLRemotesHandler
, "get top remotes");
561 DynListener::registerFunc("SET",&DLSettingsHandler
, "set config variables", "<var> <value>");
562 DynListener::registerFunc("RETRIEVE",&DLNotifyRetrieveHandler
, "retrieve slave domain", "<domain>");
563 DynListener::registerFunc("CURRENT-CONFIG",&DLCurrentConfigHandler
, "retrieve the current configuration");
564 DynListener::registerFunc("LIST-ZONES",&DLListZones
, "show list of zones", "[master|slave|native]");
565 DynListener::registerFunc("TOKEN-LOGIN", &DLTokenLogin
, "Login to a PKCS#11 token", "<module> <slot> <pin>");
567 if(!::arg()["tcp-control-address"].empty()) {
568 DynListener
* dlTCP
=new DynListener(ComboAddress(::arg()["tcp-control-address"], ::arg().asNum("tcp-control-port")));
572 // reparse, with error checking
573 if(!::arg().mustDo("no-config"))
574 ::arg().file(configname
.c_str());
575 ::arg().parse(argc
,argv
);
577 if(::arg()["server-id"].empty()) {
579 gethostname(tmp
, sizeof(tmp
)-1);
580 ::arg().set("server-id")=tmp
;
584 N
=std::make_shared
<UDPNameserver
>(); // this fails when we are not root, throws exception
585 g_udpReceivers
.push_back(N
);
587 size_t rthreads
= ::arg().asNum("receiver-threads", 1);
588 if (rthreads
> 1 && N
->canReusePort()) {
589 g_udpReceivers
.resize(rthreads
);
591 for (size_t idx
= 1; idx
< rthreads
; idx
++) {
593 g_udpReceivers
[idx
] = std::make_shared
<UDPNameserver
>(true);
595 catch(const PDNSException
& e
) {
596 g_log
<<Logger::Error
<<"Unable to reuse port, falling back to original bind"<<endl
;
602 if(!::arg().mustDo("disable-tcp"))
603 TN
=new TCPNameserver
;
605 catch(const ArgException
&A
) {
606 g_log
<<Logger::Error
<<"Fatal error: "<<A
.reason
<<endl
;
611 DLOG(g_log
<<Logger::Warning
<<"Verbose logging in effect"<<endl
);
613 showProductVersion();
618 catch(PDNSException
&AE
) {
619 if(!::arg().mustDo("daemon"))
620 cerr
<<"Exiting because: "<<AE
.reason
<<endl
;
621 g_log
<<Logger::Error
<<"Exiting because: "<<AE
.reason
<<endl
;
623 catch(std::exception
&e
) {
624 if(!::arg().mustDo("daemon"))
625 cerr
<<"Exiting because of STL error: "<<e
.what()<<endl
;
626 g_log
<<Logger::Error
<<"Exiting because of STL error: "<<e
.what()<<endl
;
629 cerr
<<"Uncaught exception of unknown type - sorry"<<endl
;