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"
75 #ifdef HAVE_LUA_RECORDS
76 #include "minicurl.hh"
77 #endif /* HAVE_LUA_RECORDS */
81 string s_programname
="pdns"; // used in packethandler.cc
83 const char *funnytext
=
84 "*****************************************************************************\n"\
85 "Ok, you just ran pdns_server through 'strings' hoping to find funny messages.\n"\
86 "Well, you found one. \n"\
87 "Two ions are flying through their particle accelerator, says the one to the\n"
88 "other 'I think I've lost an electron!' \n"\
89 "So the other one says, 'Are you sure?'. 'YEAH! I'M POSITIVE!'\n"\
90 " the pdns crew - pdns@powerdns.com\n"
91 "*****************************************************************************\n";
99 \brief The main loop of powerdns
101 This file is where it all happens - main is here, as are the two pivotal threads qthread() and athread()
111 int i
=open("/dev/null",O_RDWR
); /* open stdin */
113 g_log
<<Logger::Critical
<<"Unable to open /dev/null: "<<stringerror()<<endl
;
115 dup2(i
,0); /* stdin */
116 dup2(i
,1); /* stderr */
117 dup2(i
,2); /* stderr */
123 static void takedown(int i
)
126 g_log
<<Logger::Error
<<"Guardian is killed, taking down children with us"<<endl
;
132 static void writePid(void)
134 if(!::arg().mustDo("write-pid"))
137 string fname
=::arg()["socket-dir"];
138 if (::arg()["socket-dir"].empty()) {
139 if (::arg()["chroot"].empty())
140 fname
= LOCALSTATEDIR
;
142 fname
= ::arg()["chroot"] + "/";
143 } else if (!::arg()["socket-dir"].empty() && !::arg()["chroot"].empty()) {
144 fname
= ::arg()["chroot"] + ::arg()["socket-dir"];
147 fname
+= + "/" + s_programname
+ ".pid";
148 ofstream
of(fname
.c_str());
152 g_log
<<Logger::Error
<<"Writing pid for "<<getpid()<<" to "<<fname
<<" failed: "<<stringerror()<<endl
;
155 int g_fd1
[2], g_fd2
[2];
157 pthread_mutex_t g_guardian_lock
= PTHREAD_MUTEX_INITIALIZER
;
159 // The next two methods are not in dynhandler.cc because they use a few items declared in this file.
160 static string
DLCycleHandler(const vector
<string
>&parts
, pid_t ppid
)
162 kill(cpid
, SIGKILL
); // why?
163 kill(cpid
, SIGKILL
); // why?
168 static string
DLRestHandler(const vector
<string
>&parts
, pid_t ppid
)
172 for(vector
<string
>::const_iterator i
=parts
.begin();i
!=parts
.end();++i
) {
179 Lock
l(&g_guardian_lock
);
182 writen2(g_fd1
[1],line
.c_str(),line
.size()+1);
184 catch(PDNSException
&ae
) {
185 return "Error communicating with instance: "+ae
.reason
;
189 while(fgets(mesg
,sizeof(mesg
),g_fp
)) {
194 boost::trim_right(response
);
200 static int guardian(int argc
, char **argv
)
207 DynListener
dlg(s_programname
);
208 dlg
.registerFunc("QUIT",&DLQuitHandler
, "quit daemon");
209 dlg
.registerFunc("CYCLE",&DLCycleHandler
, "restart instance");
210 dlg
.registerFunc("PING",&DLPingHandler
, "ping guardian");
211 dlg
.registerFunc("STATUS",&DLStatusHandler
, "get instance status from guardian");
212 dlg
.registerRestFunc(&DLRestHandler
);
214 string progname
=argv
[0];
219 pthread_mutex_lock(&g_guardian_lock
);
223 setStatus("Launching child");
225 if(pipe(g_fd1
)<0 || pipe(g_fd2
)<0) {
226 g_log
<<Logger::Critical
<<"Unable to open pipe for coprocess: "<<stringerror()<<endl
;
230 if(!(g_fp
=fdopen(g_fd2
[0],"r"))) {
231 g_log
<<Logger::Critical
<<"Unable to associate a file pointer with pipe: "<<stringerror()<<endl
;
234 setbuf(g_fp
,0); // no buffering please, confuses select
236 if(!(pid
=fork())) { // child
237 signal(SIGTERM
, SIG_DFL
);
239 signal(SIGHUP
, SIG_DFL
);
240 signal(SIGUSR1
, SIG_DFL
);
241 signal(SIGUSR2
, SIG_DFL
);
243 char **const newargv
=new char*[argc
+2];
246 if(::arg()["config-name"]!="") {
247 progname
+="-"+::arg()["config-name"];
248 g_log
<<Logger::Error
<<"Virtual configuration name: "<<::arg()["config-name"]<<endl
;
251 newargv
[0]=strdup(const_cast<char *>((progname
+"-instance").c_str()));
252 for(n
=1;n
<argc
;n
++) {
257 g_log
<<Logger::Error
<<"Guardian is launching an instance"<<endl
;
259 fclose(g_fp
); // this closes g_fd2[0] for us
261 if(g_fd1
[0]!= infd
) {
262 dup2(g_fd1
[0], infd
);
266 if(g_fd2
[1]!= outfd
) {
267 dup2(g_fd2
[1], outfd
);
270 if(execvp(argv
[0], newargv
)<0) {
271 g_log
<<Logger::Error
<<"Unable to execvp '"<<argv
[0]<<"': "<<stringerror()<<endl
;
274 g_log
<<Logger::Error
<<*p
++<<endl
;
278 g_log
<<Logger::Error
<<"execvp returned!!"<<endl
;
281 else if(pid
>0) { // parent
287 signal(SIGTERM
, takedown
);
289 signal(SIGHUP
, SIG_IGN
);
290 signal(SIGUSR1
, SIG_IGN
);
291 signal(SIGUSR2
, SIG_IGN
);
295 pthread_mutex_unlock(&g_guardian_lock
);
299 int ret
=waitpid(pid
,&status
,WNOHANG
);
302 g_log
<<Logger::Error
<<"In guardian loop, waitpid returned error: "<<stringerror()<<endl
;
303 g_log
<<Logger::Error
<<"Dying"<<endl
;
306 else if(ret
) // something exited
308 else { // child is alive
309 // execute some kind of ping here
311 takedown(1); // needs a parameter..
312 setStatus("Child running on pid "+itoa(pid
));
317 pthread_mutex_lock(&g_guardian_lock
);
322 if(WIFEXITED(status
)) {
323 int ret
=WEXITSTATUS(status
);
326 g_log
<<Logger::Error
<<"Child requested a stop, exiting"<<endl
;
329 setStatus("Child died with code "+itoa(ret
));
330 g_log
<<Logger::Error
<<"Our pdns instance exited with code "<<ret
<<", respawning"<<endl
;
335 if(WIFSIGNALED(status
)) {
336 int sig
=WTERMSIG(status
);
337 setStatus("Child died because of signal "+itoa(sig
));
338 g_log
<<Logger::Error
<<"Our pdns instance ("<<pid
<<") exited after signal "<<sig
<<endl
;
340 if(WCOREDUMP(status
))
341 g_log
<<Logger::Error
<<"Dumped core"<<endl
;
344 g_log
<<Logger::Error
<<"Respawning"<<endl
;
348 g_log
<<Logger::Error
<<"No clue what happened! Respawning"<<endl
;
351 g_log
<<Logger::Error
<<"Unable to fork: "<<stringerror()<<endl
;
357 #if defined(__GLIBC__) && !defined(__UCLIBC__)
358 #include <execinfo.h>
359 static void tbhandler(int num
)
361 g_log
<<Logger::Critical
<<"Got a signal "<<num
<<", attempting to print trace: "<<endl
;
362 void *array
[20]; //only care about last 17 functions (3 taken with tracing support)
367 size
= backtrace (array
, 20);
368 strings
= backtrace_symbols (array
, size
); //Need -rdynamic gcc (linker) flag for this to work
370 for (i
= 0; i
< size
; i
++) //skip useless functions
371 g_log
<<Logger::Error
<<strings
[i
]<<endl
;
374 signal(SIGABRT
, SIG_DFL
);
375 abort();//hopefully will give core
380 //! The main function of pdns, the pdns process
381 int main(int argc
, char **argv
)
383 versionSetProduct(ProductAuthoritative
);
384 reportAllTypes(); // init MOADNSParser
386 s_programname
="pdns";
389 #if defined(__GLIBC__) && !defined(__UCLIBC__)
390 signal(SIGSEGV
,tbhandler
);
391 signal(SIGFPE
,tbhandler
);
392 signal(SIGABRT
,tbhandler
);
393 signal(SIGILL
,tbhandler
);
396 std::ios_base::sync_with_stdio(false);
398 g_log
.toConsole(Logger::Warning
);
402 ::arg().laxParse(argc
,argv
); // do a lax parse
404 if(::arg().mustDo("version")) {
405 showProductVersion();
406 showBuildConfiguration();
410 if(::arg()["config-name"]!="")
411 s_programname
+="-"+::arg()["config-name"];
413 g_log
.setName(s_programname
);
415 string configname
=::arg()["config-dir"]+"/"+s_programname
+".conf";
416 cleanSlashes(configname
);
418 if(!::arg().mustDo("config") && !::arg().mustDo("no-config")) // "config" == print a configuration file
419 ::arg().laxFile(configname
.c_str());
421 ::arg().laxParse(argc
,argv
); // reparse so the commandline still wins
422 if(!::arg()["logging-facility"].empty()) {
423 int val
=logFacilityToLOG(::arg().asNum("logging-facility") );
425 g_log
.setFacility(val
);
427 g_log
<<Logger::Error
<<"Unknown logging facility "<<::arg().asNum("logging-facility") <<endl
;
430 g_log
.setLoglevel((Logger::Urgency
)(::arg().asNum("loglevel")));
431 g_log
.disableSyslog(::arg().mustDo("disable-syslog"));
432 g_log
.setTimestamps(::arg().mustDo("log-timestamp"));
433 g_log
.toConsole((Logger::Urgency
)(::arg().asNum("loglevel")));
435 if(::arg().mustDo("help") || ::arg().mustDo("config")) {
436 ::arg().set("daemon")="no";
437 ::arg().set("guardian")="no";
440 if(::arg().mustDo("guardian") && !isGuarded(argv
)) {
441 if(::arg().mustDo("daemon")) {
442 g_log
.toConsole(Logger::Critical
);
445 guardian(argc
, argv
);
446 // never get here, guardian will reinvoke process
447 cerr
<<"Um, we did get here!"<<endl
;
451 // we really need to do work - either standalone or as an instance
453 #if defined(__GLIBC__) && !defined(__UCLIBC__)
454 if(!::arg().mustDo("traceback-handler")) {
455 g_log
<<Logger::Warning
<<"Disabling traceback handler"<<endl
;
456 signal(SIGSEGV
,SIG_DFL
);
457 signal(SIGFPE
,SIG_DFL
);
458 signal(SIGABRT
,SIG_DFL
);
459 signal(SIGILL
,SIG_DFL
);
463 #ifdef HAVE_LIBSODIUM
464 if (sodium_init() == -1) {
465 cerr
<<"Unable to initialize sodium crypto library"<<endl
;
470 openssl_thread_setup();
475 #ifdef HAVE_LUA_RECORDS
477 #endif /* HAVE_LUA_RECORDS */
479 if(!::arg()["load-modules"].empty()) {
480 vector
<string
> modules
;
482 stringtok(modules
,::arg()["load-modules"], ", ");
483 if (!UeberBackend::loadModules(modules
, ::arg()["module-dir"])) {
488 BackendMakers().launch(::arg()["launch"]); // vrooooom!
490 if(!::arg().getCommands().empty()) {
491 cerr
<<"Fatal: non-option";
492 if (::arg().getCommands().size() > 1) {
497 for (auto const c
: ::arg().getCommands()) {
504 cerr
<<") on the command line, perhaps a '--setting=123' statement missed the '='?"<<endl
;
508 if(::arg().mustDo("help")) {
509 cout
<<"syntax:"<<endl
<<endl
;
510 cout
<<::arg().helpstring(::arg()["help"])<<endl
;
514 if(::arg().mustDo("config")) {
515 cout
<<::arg().configstring()<<endl
;
519 if(::arg().mustDo("list-modules")) {
520 auto modules
= BackendMakers().getModules();
521 cout
<<"Modules available:"<<endl
;
522 for(const auto& m
: modules
)
528 if(!::arg().asNum("local-port")) {
529 g_log
<<Logger::Error
<<"Unable to launch, binding to no port or port 0 makes no sense"<<endl
;
530 exit(99); // this isn't going to fix itself either
532 if(!BackendMakers().numLauncheable()) {
533 g_log
<<Logger::Error
<<"Unable to launch, no backends configured for querying"<<endl
;
534 exit(99); // this isn't going to fix itself either
536 if(::arg().mustDo("daemon")) {
537 g_log
.toConsole(Logger::None
);
542 if(isGuarded(argv
)) {
543 g_log
<<Logger::Warning
<<"This is a guarded instance of pdns"<<endl
;
544 dl
=new DynListener
; // listens on stdin
547 g_log
<<Logger::Warning
<<"This is a standalone pdns"<<endl
;
549 if(::arg().mustDo("control-console"))
550 dl
=new DynListener();
552 dl
=new DynListener(s_programname
);
556 DynListener::registerFunc("SHOW",&DLShowHandler
, "show a specific statistic or * to get a list", "<statistic>");
557 DynListener::registerFunc("RPING",&DLPingHandler
, "ping instance");
558 DynListener::registerFunc("QUIT",&DLRQuitHandler
, "quit daemon");
559 DynListener::registerFunc("UPTIME",&DLUptimeHandler
, "get instance uptime");
560 DynListener::registerFunc("NOTIFY-HOST",&DLNotifyHostHandler
, "notify host for specific domain", "<domain> <host>");
561 DynListener::registerFunc("NOTIFY",&DLNotifyHandler
, "queue a notification", "<domain>");
562 DynListener::registerFunc("RELOAD",&DLReloadHandler
, "reload all zones");
563 DynListener::registerFunc("REDISCOVER",&DLRediscoverHandler
, "discover any new zones");
564 DynListener::registerFunc("VERSION",&DLVersionHandler
, "get instance version");
565 DynListener::registerFunc("PURGE",&DLPurgeHandler
, "purge entries from packet cache", "[<record>]");
566 DynListener::registerFunc("CCOUNTS",&DLCCHandler
, "get cache statistics");
567 DynListener::registerFunc("QTYPES", &DLQTypesHandler
, "get QType statistics");
568 DynListener::registerFunc("RESPSIZES", &DLRSizesHandler
, "get histogram of response sizes");
569 DynListener::registerFunc("REMOTES", &DLRemotesHandler
, "get top remotes");
570 DynListener::registerFunc("SET",&DLSettingsHandler
, "set config variables", "<var> <value>");
571 DynListener::registerFunc("RETRIEVE",&DLNotifyRetrieveHandler
, "retrieve slave domain", "<domain>");
572 DynListener::registerFunc("CURRENT-CONFIG",&DLCurrentConfigHandler
, "retrieve the current configuration");
573 DynListener::registerFunc("LIST-ZONES",&DLListZones
, "show list of zones", "[master|slave|native]");
574 DynListener::registerFunc("TOKEN-LOGIN", &DLTokenLogin
, "Login to a PKCS#11 token", "<module> <slot> <pin>");
576 if(!::arg()["tcp-control-address"].empty()) {
577 DynListener
* dlTCP
=new DynListener(ComboAddress(::arg()["tcp-control-address"], ::arg().asNum("tcp-control-port")));
581 // reparse, with error checking
582 if(!::arg().mustDo("no-config"))
583 ::arg().file(configname
.c_str());
584 ::arg().parse(argc
,argv
);
586 if(::arg()["server-id"].empty()) {
588 if(gethostname(tmp
, sizeof(tmp
)-1) == 0) {
589 ::arg().set("server-id")=tmp
;
591 g_log
<<Logger::Warning
<<"Unable to get the hostname, NSID and id.server values will be empty: "<<stringerror()<<endl
;
596 N
=std::make_shared
<UDPNameserver
>(); // this fails when we are not root, throws exception
597 g_udpReceivers
.push_back(N
);
599 size_t rthreads
= ::arg().asNum("receiver-threads", 1);
600 if (rthreads
> 1 && N
->canReusePort()) {
601 g_udpReceivers
.resize(rthreads
);
603 for (size_t idx
= 1; idx
< rthreads
; idx
++) {
605 g_udpReceivers
[idx
] = std::make_shared
<UDPNameserver
>(true);
607 catch(const PDNSException
& e
) {
608 g_log
<<Logger::Error
<<"Unable to reuse port, falling back to original bind"<<endl
;
614 TN
= make_unique
<TCPNameserver
>();
616 catch(const ArgException
&A
) {
617 g_log
<<Logger::Error
<<"Fatal error: "<<A
.reason
<<endl
;
622 S
.blacklist("special-memory-usage");
624 DLOG(g_log
<<Logger::Warning
<<"Verbose logging in effect"<<endl
);
626 showProductVersion();
631 catch(PDNSException
&AE
) {
632 if(!::arg().mustDo("daemon"))
633 cerr
<<"Exiting because: "<<AE
.reason
<<endl
;
634 g_log
<<Logger::Error
<<"Exiting because: "<<AE
.reason
<<endl
;
636 catch(std::exception
&e
) {
637 if(!::arg().mustDo("daemon"))
638 cerr
<<"Exiting because of STL error: "<<e
.what()<<endl
;
639 g_log
<<Logger::Error
<<"Exiting because of STL error: "<<e
.what()<<endl
;
642 cerr
<<"Uncaught exception of unknown type - sorry"<<endl
;