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";
364 static void loadModules()
366 if(!::arg()["load-modules"].empty()) {
367 vector
<string
>modules
;
369 stringtok(modules
,::arg()["load-modules"],", ");
371 for(vector
<string
>::const_iterator i
=modules
.begin();i
!=modules
.end();++i
) {
373 const string
&module
=*i
;
375 if(module
.find(".")==string::npos
)
376 res
=UeberBackend::loadmodule(::arg()["module-dir"]+"/lib"+module
+"backend.so");
377 else if(module
[0]=='/' || (module
[0]=='.' && module
[1]=='/') || (module
[0]=='.' && module
[1]=='.')) // absolute or current path
378 res
=UeberBackend::loadmodule(module
);
380 res
=UeberBackend::loadmodule(::arg()["module-dir"]+"/"+module
);
383 g_log
<<Logger::Error
<<"Receiver unable to load module "<<module
<<endl
;
391 #include <execinfo.h>
392 static void tbhandler(int num
)
394 g_log
<<Logger::Critical
<<"Got a signal "<<num
<<", attempting to print trace: "<<endl
;
395 void *array
[20]; //only care about last 17 functions (3 taken with tracing support)
400 size
= backtrace (array
, 20);
401 strings
= backtrace_symbols (array
, size
); //Need -rdynamic gcc (linker) flag for this to work
403 for (i
= 0; i
< size
; i
++) //skip useless functions
404 g_log
<<Logger::Error
<<strings
[i
]<<endl
;
407 signal(SIGABRT
, SIG_DFL
);
408 abort();//hopefully will give core
413 //! The main function of pdns, the pdns process
414 int main(int argc
, char **argv
)
416 versionSetProduct(ProductAuthoritative
);
417 reportAllTypes(); // init MOADNSParser
419 s_programname
="pdns";
423 signal(SIGSEGV
,tbhandler
);
424 signal(SIGFPE
,tbhandler
);
425 signal(SIGABRT
,tbhandler
);
426 signal(SIGILL
,tbhandler
);
429 std::ios_base::sync_with_stdio(false);
431 g_log
.toConsole(Logger::Warning
);
434 UNIX_declareArguments();
436 ::arg().laxParse(argc
,argv
); // do a lax parse
438 if(::arg().mustDo("version")) {
439 showProductVersion();
440 showBuildConfiguration();
444 if(::arg()["config-name"]!="")
445 s_programname
+="-"+::arg()["config-name"];
447 g_log
.setName(s_programname
);
449 string configname
=::arg()["config-dir"]+"/"+s_programname
+".conf";
450 cleanSlashes(configname
);
452 if(!::arg().mustDo("config") && !::arg().mustDo("no-config")) // "config" == print a configuration file
453 ::arg().laxFile(configname
.c_str());
455 ::arg().laxParse(argc
,argv
); // reparse so the commandline still wins
456 if(!::arg()["logging-facility"].empty()) {
457 int val
=logFacilityToLOG(::arg().asNum("logging-facility") );
459 g_log
.setFacility(val
);
461 g_log
<<Logger::Error
<<"Unknown logging facility "<<::arg().asNum("logging-facility") <<endl
;
464 g_log
.setLoglevel((Logger::Urgency
)(::arg().asNum("loglevel")));
465 g_log
.disableSyslog(::arg().mustDo("disable-syslog"));
466 g_log
.setTimestamps(::arg().mustDo("log-timestamp"));
467 g_log
.toConsole((Logger::Urgency
)(::arg().asNum("loglevel")));
469 if(::arg().mustDo("help") || ::arg().mustDo("config")) {
470 ::arg().set("daemon")="no";
471 ::arg().set("guardian")="no";
474 if(::arg().mustDo("guardian") && !isGuarded(argv
)) {
475 if(::arg().mustDo("daemon")) {
476 g_log
.toConsole(Logger::Critical
);
479 guardian(argc
, argv
);
480 // never get here, guardian will reinvoke process
481 cerr
<<"Um, we did get here!"<<endl
;
485 // we really need to do work - either standalone or as an instance
488 if(!::arg().mustDo("traceback-handler")) {
489 g_log
<<Logger::Warning
<<"Disabling traceback handler"<<endl
;
490 signal(SIGSEGV
,SIG_DFL
);
491 signal(SIGFPE
,SIG_DFL
);
492 signal(SIGABRT
,SIG_DFL
);
493 signal(SIGILL
,SIG_DFL
);
497 #ifdef HAVE_LIBSODIUM
498 if (sodium_init() == -1) {
499 cerr
<<"Unable to initialize sodium crypto library"<<endl
;
504 openssl_thread_setup();
510 BackendMakers().launch(::arg()["launch"]); // vrooooom!
512 if(!::arg().getCommands().empty()) {
513 cerr
<<"Fatal: non-option on the command line, perhaps a '--setting=123' statement missed the '='?"<<endl
;
517 if(::arg().mustDo("help")) {
518 cout
<<"syntax:"<<endl
<<endl
;
519 cout
<<::arg().helpstring(::arg()["help"])<<endl
;
523 if(::arg().mustDo("config")) {
524 cout
<<::arg().configstring()<<endl
;
528 if(::arg().mustDo("list-modules")) {
529 auto modules
= BackendMakers().getModules();
530 cout
<<"Modules available:"<<endl
;
531 for(const auto& m
: modules
)
537 if(!::arg().asNum("local-port")) {
538 g_log
<<Logger::Error
<<"Unable to launch, binding to no port or port 0 makes no sense"<<endl
;
539 exit(99); // this isn't going to fix itself either
541 if(!BackendMakers().numLauncheable()) {
542 g_log
<<Logger::Error
<<"Unable to launch, no backends configured for querying"<<endl
;
543 exit(99); // this isn't going to fix itself either
545 if(::arg().mustDo("daemon")) {
546 g_log
.toConsole(Logger::None
);
551 if(isGuarded(argv
)) {
552 g_log
<<Logger::Warning
<<"This is a guarded instance of pdns"<<endl
;
553 dl
=new DynListener
; // listens on stdin
556 g_log
<<Logger::Warning
<<"This is a standalone pdns"<<endl
;
558 if(::arg().mustDo("control-console"))
559 dl
=new DynListener();
561 dl
=new DynListener(s_programname
);
565 DynListener::registerFunc("SHOW",&DLShowHandler
, "show a specific statistic or * to get a list", "<statistic>");
566 DynListener::registerFunc("RPING",&DLPingHandler
, "ping instance");
567 DynListener::registerFunc("QUIT",&DLRQuitHandler
, "quit daemon");
568 DynListener::registerFunc("UPTIME",&DLUptimeHandler
, "get instance uptime");
569 DynListener::registerFunc("NOTIFY-HOST",&DLNotifyHostHandler
, "notify host for specific domain", "<domain> <host>");
570 DynListener::registerFunc("NOTIFY",&DLNotifyHandler
, "queue a notification", "<domain>");
571 DynListener::registerFunc("RELOAD",&DLReloadHandler
, "reload all zones");
572 DynListener::registerFunc("REDISCOVER",&DLRediscoverHandler
, "discover any new zones");
573 DynListener::registerFunc("VERSION",&DLVersionHandler
, "get instance version");
574 DynListener::registerFunc("PURGE",&DLPurgeHandler
, "purge entries from packet cache", "[<record>]");
575 DynListener::registerFunc("CCOUNTS",&DLCCHandler
, "get cache statistics");
576 DynListener::registerFunc("QTYPES", &DLQTypesHandler
, "get QType statistics");
577 DynListener::registerFunc("RESPSIZES", &DLRSizesHandler
, "get histogram of response sizes");
578 DynListener::registerFunc("REMOTES", &DLRemotesHandler
, "get top remotes");
579 DynListener::registerFunc("SET",&DLSettingsHandler
, "set config variables", "<var> <value>");
580 DynListener::registerFunc("RETRIEVE",&DLNotifyRetrieveHandler
, "retrieve slave domain", "<domain>");
581 DynListener::registerFunc("CURRENT-CONFIG",&DLCurrentConfigHandler
, "retrieve the current configuration");
582 DynListener::registerFunc("LIST-ZONES",&DLListZones
, "show list of zones", "[master|slave|native]");
583 DynListener::registerFunc("TOKEN-LOGIN", &DLTokenLogin
, "Login to a PKCS#11 token", "<module> <slot> <pin>");
585 if(!::arg()["tcp-control-address"].empty()) {
586 DynListener
* dlTCP
=new DynListener(ComboAddress(::arg()["tcp-control-address"], ::arg().asNum("tcp-control-port")));
590 // reparse, with error checking
591 if(!::arg().mustDo("no-config"))
592 ::arg().file(configname
.c_str());
593 ::arg().parse(argc
,argv
);
595 if(::arg()["server-id"].empty()) {
597 gethostname(tmp
, sizeof(tmp
)-1);
598 ::arg().set("server-id")=tmp
;
602 N
=std::make_shared
<UDPNameserver
>(); // this fails when we are not root, throws exception
603 g_udpReceivers
.push_back(N
);
605 size_t rthreads
= ::arg().asNum("receiver-threads", 1);
606 if (rthreads
> 1 && N
->canReusePort()) {
607 g_udpReceivers
.resize(rthreads
);
609 for (size_t idx
= 1; idx
< rthreads
; idx
++) {
611 g_udpReceivers
[idx
] = std::make_shared
<UDPNameserver
>(true);
613 catch(const PDNSException
& e
) {
614 g_log
<<Logger::Error
<<"Unable to reuse port, falling back to original bind"<<endl
;
620 if(!::arg().mustDo("disable-tcp"))
621 TN
=new TCPNameserver
;
623 catch(const ArgException
&A
) {
624 g_log
<<Logger::Error
<<"Fatal error: "<<A
.reason
<<endl
;
629 DLOG(g_log
<<Logger::Warning
<<"Verbose logging in effect"<<endl
);
631 showProductVersion();
636 catch(PDNSException
&AE
) {
637 if(!::arg().mustDo("daemon"))
638 cerr
<<"Exiting because: "<<AE
.reason
<<endl
;
639 g_log
<<Logger::Error
<<"Exiting because: "<<AE
.reason
<<endl
;
641 catch(std::exception
&e
) {
642 if(!::arg().mustDo("daemon"))
643 cerr
<<"Exiting because of STL error: "<<e
.what()<<endl
;
644 g_log
<<Logger::Error
<<"Exiting because of STL error: "<<e
.what()<<endl
;
647 cerr
<<"Uncaught exception of unknown type - sorry"<<endl
;