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
;
354 #include <execinfo.h>
355 static void tbhandler(int num
)
357 g_log
<<Logger::Critical
<<"Got a signal "<<num
<<", attempting to print trace: "<<endl
;
358 void *array
[20]; //only care about last 17 functions (3 taken with tracing support)
363 size
= backtrace (array
, 20);
364 strings
= backtrace_symbols (array
, size
); //Need -rdynamic gcc (linker) flag for this to work
366 for (i
= 0; i
< size
; i
++) //skip useless functions
367 g_log
<<Logger::Error
<<strings
[i
]<<endl
;
370 signal(SIGABRT
, SIG_DFL
);
371 abort();//hopefully will give core
376 //! The main function of pdns, the pdns process
377 int main(int argc
, char **argv
)
379 versionSetProduct(ProductAuthoritative
);
380 reportAllTypes(); // init MOADNSParser
382 s_programname
="pdns";
386 signal(SIGSEGV
,tbhandler
);
387 signal(SIGFPE
,tbhandler
);
388 signal(SIGABRT
,tbhandler
);
389 signal(SIGILL
,tbhandler
);
392 std::ios_base::sync_with_stdio(false);
394 g_log
.toConsole(Logger::Warning
);
398 ::arg().laxParse(argc
,argv
); // do a lax parse
400 if(::arg().mustDo("version")) {
401 showProductVersion();
402 showBuildConfiguration();
406 if(::arg()["config-name"]!="")
407 s_programname
+="-"+::arg()["config-name"];
409 g_log
.setName(s_programname
);
411 string configname
=::arg()["config-dir"]+"/"+s_programname
+".conf";
412 cleanSlashes(configname
);
414 if(!::arg().mustDo("config") && !::arg().mustDo("no-config")) // "config" == print a configuration file
415 ::arg().laxFile(configname
.c_str());
417 ::arg().laxParse(argc
,argv
); // reparse so the commandline still wins
418 if(!::arg()["logging-facility"].empty()) {
419 int val
=logFacilityToLOG(::arg().asNum("logging-facility") );
421 g_log
.setFacility(val
);
423 g_log
<<Logger::Error
<<"Unknown logging facility "<<::arg().asNum("logging-facility") <<endl
;
426 g_log
.setLoglevel((Logger::Urgency
)(::arg().asNum("loglevel")));
427 g_log
.disableSyslog(::arg().mustDo("disable-syslog"));
428 g_log
.setTimestamps(::arg().mustDo("log-timestamp"));
429 g_log
.toConsole((Logger::Urgency
)(::arg().asNum("loglevel")));
431 if(::arg().mustDo("help") || ::arg().mustDo("config")) {
432 ::arg().set("daemon")="no";
433 ::arg().set("guardian")="no";
436 if(::arg().mustDo("guardian") && !isGuarded(argv
)) {
437 if(::arg().mustDo("daemon")) {
438 g_log
.toConsole(Logger::Critical
);
441 guardian(argc
, argv
);
442 // never get here, guardian will reinvoke process
443 cerr
<<"Um, we did get here!"<<endl
;
447 // we really need to do work - either standalone or as an instance
450 if(!::arg().mustDo("traceback-handler")) {
451 g_log
<<Logger::Warning
<<"Disabling traceback handler"<<endl
;
452 signal(SIGSEGV
,SIG_DFL
);
453 signal(SIGFPE
,SIG_DFL
);
454 signal(SIGABRT
,SIG_DFL
);
455 signal(SIGILL
,SIG_DFL
);
459 #ifdef HAVE_LIBSODIUM
460 if (sodium_init() == -1) {
461 cerr
<<"Unable to initialize sodium crypto library"<<endl
;
466 openssl_thread_setup();
471 if(!::arg()["load-modules"].empty()) {
472 vector
<string
> modules
;
474 stringtok(modules
,::arg()["load-modules"], ", ");
475 if (!UeberBackend::loadModules(modules
, ::arg()["module-dir"])) {
480 BackendMakers().launch(::arg()["launch"]); // vrooooom!
482 if(!::arg().getCommands().empty()) {
483 cerr
<<"Fatal: non-option on the command line, perhaps a '--setting=123' statement missed the '='?"<<endl
;
487 if(::arg().mustDo("help")) {
488 cout
<<"syntax:"<<endl
<<endl
;
489 cout
<<::arg().helpstring(::arg()["help"])<<endl
;
493 if(::arg().mustDo("config")) {
494 cout
<<::arg().configstring()<<endl
;
498 if(::arg().mustDo("list-modules")) {
499 auto modules
= BackendMakers().getModules();
500 cout
<<"Modules available:"<<endl
;
501 for(const auto& m
: modules
)
507 if(!::arg().asNum("local-port")) {
508 g_log
<<Logger::Error
<<"Unable to launch, binding to no port or port 0 makes no sense"<<endl
;
509 exit(99); // this isn't going to fix itself either
511 if(!BackendMakers().numLauncheable()) {
512 g_log
<<Logger::Error
<<"Unable to launch, no backends configured for querying"<<endl
;
513 exit(99); // this isn't going to fix itself either
515 if(::arg().mustDo("daemon")) {
516 g_log
.toConsole(Logger::None
);
521 if(isGuarded(argv
)) {
522 g_log
<<Logger::Warning
<<"This is a guarded instance of pdns"<<endl
;
523 dl
=new DynListener
; // listens on stdin
526 g_log
<<Logger::Warning
<<"This is a standalone pdns"<<endl
;
528 if(::arg().mustDo("control-console"))
529 dl
=new DynListener();
531 dl
=new DynListener(s_programname
);
535 DynListener::registerFunc("SHOW",&DLShowHandler
, "show a specific statistic or * to get a list", "<statistic>");
536 DynListener::registerFunc("RPING",&DLPingHandler
, "ping instance");
537 DynListener::registerFunc("QUIT",&DLRQuitHandler
, "quit daemon");
538 DynListener::registerFunc("UPTIME",&DLUptimeHandler
, "get instance uptime");
539 DynListener::registerFunc("NOTIFY-HOST",&DLNotifyHostHandler
, "notify host for specific domain", "<domain> <host>");
540 DynListener::registerFunc("NOTIFY",&DLNotifyHandler
, "queue a notification", "<domain>");
541 DynListener::registerFunc("RELOAD",&DLReloadHandler
, "reload all zones");
542 DynListener::registerFunc("REDISCOVER",&DLRediscoverHandler
, "discover any new zones");
543 DynListener::registerFunc("VERSION",&DLVersionHandler
, "get instance version");
544 DynListener::registerFunc("PURGE",&DLPurgeHandler
, "purge entries from packet cache", "[<record>]");
545 DynListener::registerFunc("CCOUNTS",&DLCCHandler
, "get cache statistics");
546 DynListener::registerFunc("QTYPES", &DLQTypesHandler
, "get QType statistics");
547 DynListener::registerFunc("RESPSIZES", &DLRSizesHandler
, "get histogram of response sizes");
548 DynListener::registerFunc("REMOTES", &DLRemotesHandler
, "get top remotes");
549 DynListener::registerFunc("SET",&DLSettingsHandler
, "set config variables", "<var> <value>");
550 DynListener::registerFunc("RETRIEVE",&DLNotifyRetrieveHandler
, "retrieve slave domain", "<domain>");
551 DynListener::registerFunc("CURRENT-CONFIG",&DLCurrentConfigHandler
, "retrieve the current configuration");
552 DynListener::registerFunc("LIST-ZONES",&DLListZones
, "show list of zones", "[master|slave|native]");
553 DynListener::registerFunc("TOKEN-LOGIN", &DLTokenLogin
, "Login to a PKCS#11 token", "<module> <slot> <pin>");
555 if(!::arg()["tcp-control-address"].empty()) {
556 DynListener
* dlTCP
=new DynListener(ComboAddress(::arg()["tcp-control-address"], ::arg().asNum("tcp-control-port")));
560 // reparse, with error checking
561 if(!::arg().mustDo("no-config"))
562 ::arg().file(configname
.c_str());
563 ::arg().parse(argc
,argv
);
565 if(::arg()["server-id"].empty()) {
567 if(gethostname(tmp
, sizeof(tmp
)-1) == 0) {
568 ::arg().set("server-id")=tmp
;
570 g_log
<<Logger::Warning
<<"Unable to get the hostname, NSID and id.server values will be empty: "<<strerror(errno
)<<endl
;
575 N
=std::make_shared
<UDPNameserver
>(); // this fails when we are not root, throws exception
576 g_udpReceivers
.push_back(N
);
578 size_t rthreads
= ::arg().asNum("receiver-threads", 1);
579 if (rthreads
> 1 && N
->canReusePort()) {
580 g_udpReceivers
.resize(rthreads
);
582 for (size_t idx
= 1; idx
< rthreads
; idx
++) {
584 g_udpReceivers
[idx
] = std::make_shared
<UDPNameserver
>(true);
586 catch(const PDNSException
& e
) {
587 g_log
<<Logger::Error
<<"Unable to reuse port, falling back to original bind"<<endl
;
593 if(!::arg().mustDo("disable-tcp"))
594 TN
=new TCPNameserver
;
596 catch(const ArgException
&A
) {
597 g_log
<<Logger::Error
<<"Fatal error: "<<A
.reason
<<endl
;
602 DLOG(g_log
<<Logger::Warning
<<"Verbose logging in effect"<<endl
);
604 showProductVersion();
609 catch(PDNSException
&AE
) {
610 if(!::arg().mustDo("daemon"))
611 cerr
<<"Exiting because: "<<AE
.reason
<<endl
;
612 g_log
<<Logger::Error
<<"Exiting because: "<<AE
.reason
<<endl
;
614 catch(std::exception
&e
) {
615 if(!::arg().mustDo("daemon"))
616 cerr
<<"Exiting because of STL error: "<<e
.what()<<endl
;
617 g_log
<<Logger::Error
<<"Exiting because of STL error: "<<e
.what()<<endl
;
620 cerr
<<"Uncaught exception of unknown type - sorry"<<endl
;