| 1 | /* |
|---|
| 2 | PowerDNS Versatile Database Driven Nameserver |
|---|
| 3 | Copyright (C) 2005 - 2010 PowerDNS.COM BV |
|---|
| 4 | |
|---|
| 5 | This program is free software; you can redistribute it and/or modify |
|---|
| 6 | it under the terms of the GNU General Public License version 2 |
|---|
| 7 | as published by the Free Software Foundation |
|---|
| 8 | |
|---|
| 9 | |
|---|
| 10 | This program is distributed in the hope that it will be useful, |
|---|
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 13 | GNU General Public License for more details. |
|---|
| 14 | |
|---|
| 15 | You should have received a copy of the GNU General Public License |
|---|
| 16 | along with this program; if not, write to the Free Software |
|---|
| 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|---|
| 18 | */ |
|---|
| 19 | #include "common_startup.hh" |
|---|
| 20 | |
|---|
| 21 | typedef Distributor<DNSPacket,DNSPacket,PacketHandler> DNSDistributor; |
|---|
| 22 | |
|---|
| 23 | |
|---|
| 24 | ArgvMap theArg; |
|---|
| 25 | StatBag S; //!< Statistics are gathered accross PDNS via the StatBag class S |
|---|
| 26 | PacketCache PC; //!< This is the main PacketCache, shared accross all threads |
|---|
| 27 | DNSProxy *DP; |
|---|
| 28 | DynListener *dl; |
|---|
| 29 | CommunicatorClass Communicator; |
|---|
| 30 | UDPNameserver *N; |
|---|
| 31 | int avg_latency; |
|---|
| 32 | TCPNameserver *TN; |
|---|
| 33 | |
|---|
| 34 | ArgvMap &arg() |
|---|
| 35 | { |
|---|
| 36 | return theArg; |
|---|
| 37 | } |
|---|
| 38 | |
|---|
| 39 | void declareArguments() |
|---|
| 40 | { |
|---|
| 41 | ::arg().set("local-port","The port on which we listen")="53"; |
|---|
| 42 | ::arg().setSwitch("log-failed-updates","If PDNS should log failed update requests")=""; |
|---|
| 43 | ::arg().setSwitch("log-dns-details","If PDNS should log DNS non-erroneous details")=""; |
|---|
| 44 | ::arg().setSwitch("allow-recursion-override","Set this so that local data fully overrides the recursor")="no"; |
|---|
| 45 | ::arg().set("urlredirector","Where we send hosts to that need to be url redirected")="127.0.0.1"; |
|---|
| 46 | ::arg().set("smtpredirector","Our smtpredir MX host")="a.misconfigured.powerdns.smtp.server"; |
|---|
| 47 | ::arg().set("local-address","Local IP addresses to which we bind")="0.0.0.0"; |
|---|
| 48 | ::arg().set("local-ipv6","Local IP address to which we bind")=""; |
|---|
| 49 | ::arg().set("query-local-address","Source IP address for sending queries")="0.0.0.0"; |
|---|
| 50 | ::arg().set("query-local-address6","Source IPv6 address for sending queries")="::"; |
|---|
| 51 | ::arg().set("max-queue-length","Maximum queuelength before considering situation lost")="5000"; |
|---|
| 52 | ::arg().set("soa-serial-offset","Make sure that no SOA serial is less than this number")="0"; |
|---|
| 53 | |
|---|
| 54 | ::arg().set("retrieval-threads", "Number of AXFR-retrieval threads for slave operation")="2"; |
|---|
| 55 | |
|---|
| 56 | ::arg().setCmd("help","Provide a helpful message"); |
|---|
| 57 | ::arg().setCmd("version","Output version and compilation date"); |
|---|
| 58 | ::arg().setCmd("config","Provide configuration file on standard output"); |
|---|
| 59 | ::arg().setCmd("list-modules","Lists all modules available"); |
|---|
| 60 | ::arg().setCmd("no-config","Don't parse configuration file"); |
|---|
| 61 | |
|---|
| 62 | ::arg().set("version-string","PowerDNS version in packets - full, anonymous, powerdns or custom")="full"; |
|---|
| 63 | ::arg().set("control-console","Debugging switch - don't use")="no"; // but I know you will! |
|---|
| 64 | ::arg().set("fancy-records","Process URL and MBOXFW records")="no"; |
|---|
| 65 | ::arg().set("wildcard-url","Process URL and MBOXFW records")="no"; |
|---|
| 66 | ::arg().set("wildcards","Honor wildcards in the database")=""; |
|---|
| 67 | ::arg().set("loglevel","Amount of logging. Higher is more. Do not set below 3")="4"; |
|---|
| 68 | ::arg().set("default-soa-name","name to insert in the SOA record if none set in the backend")="a.misconfigured.powerdns.server"; |
|---|
| 69 | ::arg().set("distributor-threads","Default number of Distributor (backend) threads to start")="3"; |
|---|
| 70 | ::arg().set("signing-threads","Default number of signer threads to start")="3"; |
|---|
| 71 | ::arg().set("receiver-threads","Default number of Distributor (backend) threads to start")="1"; |
|---|
| 72 | ::arg().set("queue-limit","Maximum number of milliseconds to queue a query")="1500"; |
|---|
| 73 | ::arg().set("recursor","If recursion is desired, IP address of a recursing nameserver")="no"; |
|---|
| 74 | ::arg().set("lazy-recursion","Only recurse if question cannot be answered locally")="yes"; |
|---|
| 75 | ::arg().set("allow-recursion","List of subnets that are allowed to recurse")="0.0.0.0/0"; |
|---|
| 76 | ::arg().set("pipebackend-abi-version","Version of the pipe backend ABI")="1"; |
|---|
| 77 | |
|---|
| 78 | ::arg().set("disable-tcp","Do not listen to TCP queries")="no"; |
|---|
| 79 | ::arg().set("disable-axfr","Do not allow zone transfers")="no"; |
|---|
| 80 | |
|---|
| 81 | ::arg().set("config-name","Name of this virtual configuration - will rename the binary image")=""; |
|---|
| 82 | |
|---|
| 83 | ::arg().set("load-modules","Load this module - supply absolute or relative path")=""; |
|---|
| 84 | ::arg().set("launch","Which backends to launch and order to query them in")=""; |
|---|
| 85 | ::arg().setSwitch("disable-axfr","Disable zonetransfers but do allow TCP queries")="no"; |
|---|
| 86 | ::arg().set("allow-axfr-ips","Allow zonetransfers only to these subnets")="0.0.0.0/0,::/0"; |
|---|
| 87 | ::arg().set("slave-cycle-interval","Reschedule failed SOA serial checks once every .. seconds")="60"; |
|---|
| 88 | |
|---|
| 89 | ::arg().set("tcp-control-address","If set, PowerDNS can be controlled over TCP on this address")=""; |
|---|
| 90 | ::arg().set("tcp-control-port","If set, PowerDNS can be controlled over TCP on this address")="53000"; |
|---|
| 91 | ::arg().set("tcp-control-secret","If set, PowerDNS can be controlled over TCP after passing this secret")=""; |
|---|
| 92 | ::arg().set("tcp-control-range","If set, remote control of PowerDNS is possible over these networks only")="127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10"; |
|---|
| 93 | |
|---|
| 94 | ::arg().setSwitch("slave","Act as a slave")="no"; |
|---|
| 95 | ::arg().setSwitch("master","Act as a master")="no"; |
|---|
| 96 | ::arg().setSwitch("guardian","Run within a guardian process")="no"; |
|---|
| 97 | ::arg().setSwitch("skip-cname","Do not perform CNAME indirection for each query")="no"; |
|---|
| 98 | ::arg().setSwitch("strict-rfc-axfrs","Perform strictly rfc compliant axfrs (very slow)")="no"; |
|---|
| 99 | ::arg().setSwitch("send-root-referral","Send out old-fashioned root-referral instead of ServFail in case of no authority")="no"; |
|---|
| 100 | |
|---|
| 101 | ::arg().setSwitch("webserver","Start a webserver for monitoring")="no"; |
|---|
| 102 | ::arg().setSwitch("webserver-print-arguments","If the webserver should print arguments")="no"; |
|---|
| 103 | ::arg().set("webserver-address","IP Address of webserver to listen on")="127.0.0.1"; |
|---|
| 104 | ::arg().set("webserver-port","Port of webserver to listen on")="8081"; |
|---|
| 105 | ::arg().set("webserver-password","Password required for accessing the webserver")=""; |
|---|
| 106 | |
|---|
| 107 | ::arg().setSwitch("out-of-zone-additional-processing","Do out of zone additional processing")="yes"; |
|---|
| 108 | ::arg().setSwitch("do-ipv6-additional-processing", "Do AAAA additional processing")="no"; |
|---|
| 109 | ::arg().setSwitch("query-logging","Hint backends that queries should be logged")="no"; |
|---|
| 110 | |
|---|
| 111 | ::arg().set("cache-ttl","Seconds to store packets in the PacketCache")="20"; |
|---|
| 112 | ::arg().set("recursive-cache-ttl","Seconds to store packets in the PacketCache")="10"; |
|---|
| 113 | ::arg().set("negquery-cache-ttl","Seconds to store packets in the PacketCache")="60"; |
|---|
| 114 | ::arg().set("query-cache-ttl","Seconds to store packets in the PacketCache")="20"; |
|---|
| 115 | ::arg().set("soa-minimum-ttl","Default SOA mininum ttl")="3600"; |
|---|
| 116 | ::arg().set("server-id", "Returned when queried for 'server.id' TXT or NSID, defaults to hostname")=""; |
|---|
| 117 | ::arg().set("soa-refresh-default","Default SOA refresh")="10800"; |
|---|
| 118 | ::arg().set("soa-retry-default","Default SOA retry")="3600"; |
|---|
| 119 | ::arg().set("soa-expire-default","Default SOA expire")="604800"; |
|---|
| 120 | |
|---|
| 121 | ::arg().set("trusted-notification-proxy", "IP address of incoming notification proxy")=""; |
|---|
| 122 | ::arg().set("slave-renotify", "If we should send out notifications for slaved updates")="no"; |
|---|
| 123 | |
|---|
| 124 | ::arg().set("default-ttl","Seconds a result is valid if not set otherwise")="3600"; |
|---|
| 125 | ::arg().set("max-tcp-connections","Maximum number of TCP connections")="10"; |
|---|
| 126 | ::arg().setSwitch("no-shuffle","Set this to prevent random shuffling of answers - for regression testing")="off"; |
|---|
| 127 | ::arg().setSwitch("per-zone-axfr-acls","When set, backends that implement it perform per-zone AXFL ACL checks")="off"; |
|---|
| 128 | |
|---|
| 129 | ::arg().setSwitch( "use-logfile", "Use a log file (Windows only)" )= "no"; |
|---|
| 130 | ::arg().set( "logfile", "Logfile to use (Windows only)" )= "pdns.log"; |
|---|
| 131 | ::arg().set("setuid","If set, change user id to this uid for more security")=""; |
|---|
| 132 | ::arg().set("setgid","If set, change group id to this gid for more security")=""; |
|---|
| 133 | |
|---|
| 134 | ::arg().set("max-cache-entries", "Maximum number of cache entries")="1000000"; |
|---|
| 135 | ::arg().set("entropy-source", "If set, read entropy from this file")="/dev/urandom"; |
|---|
| 136 | } |
|---|
| 137 | |
|---|
| 138 | void declareStats(void) |
|---|
| 139 | { |
|---|
| 140 | S.declare("udp-queries","Number of UDP queries received"); |
|---|
| 141 | S.declare("udp-answers","Number of answers sent out over UDP"); |
|---|
| 142 | |
|---|
| 143 | S.declare("udp4-answers","Number of IPv4 answers sent out over UDP"); |
|---|
| 144 | S.declare("udp4-queries","Number of IPv4UDP queries received"); |
|---|
| 145 | S.declare("udp6-answers","Number of IPv6 answers sent out over UDP"); |
|---|
| 146 | S.declare("udp6-queries","Number of IPv6 UDP queries received"); |
|---|
| 147 | |
|---|
| 148 | S.declare("recursing-answers","Number of recursive answers sent out"); |
|---|
| 149 | S.declare("recursing-questions","Number of questions sent to recursor"); |
|---|
| 150 | S.declare("corrupt-packets","Number of corrupt packets received"); |
|---|
| 151 | |
|---|
| 152 | S.declare("tcp-queries","Number of TCP queries received"); |
|---|
| 153 | S.declare("tcp-answers","Number of answers sent out over TCP"); |
|---|
| 154 | |
|---|
| 155 | S.declare("qsize-q","Number of questions waiting for database attention"); |
|---|
| 156 | |
|---|
| 157 | S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance"); |
|---|
| 158 | S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance"); |
|---|
| 159 | |
|---|
| 160 | S.declare("query-cache-hit","Number of hits on the query cache"); |
|---|
| 161 | S.declare("query-cache-miss","Number of misses on the query cache"); |
|---|
| 162 | |
|---|
| 163 | |
|---|
| 164 | S.declare("servfail-packets","Number of times a server-failed packet was sent out"); |
|---|
| 165 | S.declare("latency","Average number of microseconds needed to answer a question"); |
|---|
| 166 | S.declare("timedout-packets","Number of packets which weren't answered within timeout set"); |
|---|
| 167 | |
|---|
| 168 | S.declareRing("queries","UDP Queries Received"); |
|---|
| 169 | S.declareRing("nxdomain-queries","Queries for non-existent records within existent domains"); |
|---|
| 170 | S.declareRing("noerror-queries","Queries for existing records, but for type we don't have"); |
|---|
| 171 | S.declareRing("servfail-queries","Queries that could not be answered due to backend errors"); |
|---|
| 172 | S.declareRing("unauth-queries","Queries for domains that we are not authoritative for"); |
|---|
| 173 | S.declareRing("logmessages","Log Messages"); |
|---|
| 174 | S.declareRing("remotes","Remote server IP addresses"); |
|---|
| 175 | S.declareRing("remotes-unauth","Remote hosts querying domains for which we are not auth"); |
|---|
| 176 | S.declareRing("remotes-corrupt","Remote hosts sending corrupt packets"); |
|---|
| 177 | |
|---|
| 178 | } |
|---|
| 179 | |
|---|
| 180 | |
|---|
| 181 | int isGuarded(char **argv) |
|---|
| 182 | { |
|---|
| 183 | char *p=strstr(argv[0],"-instance"); |
|---|
| 184 | |
|---|
| 185 | return !!p; |
|---|
| 186 | } |
|---|
| 187 | |
|---|
| 188 | |
|---|
| 189 | void sendout(const DNSDistributor::AnswerData &AD) |
|---|
| 190 | { |
|---|
| 191 | static unsigned int &numanswered=*S.getPointer("udp-answers"); |
|---|
| 192 | static unsigned int &numanswered4=*S.getPointer("udp4-answers"); |
|---|
| 193 | static unsigned int &numanswered6=*S.getPointer("udp6-answers"); |
|---|
| 194 | |
|---|
| 195 | if(!AD.A) |
|---|
| 196 | return; |
|---|
| 197 | |
|---|
| 198 | N->send(AD.A); |
|---|
| 199 | numanswered++; |
|---|
| 200 | |
|---|
| 201 | if(AD.A->remote.getSocklen()==sizeof(sockaddr_in)) |
|---|
| 202 | numanswered4++; |
|---|
| 203 | else |
|---|
| 204 | numanswered6++; |
|---|
| 205 | |
|---|
| 206 | int diff=AD.A->d_dt.udiff(); |
|---|
| 207 | avg_latency=(int)(1023*avg_latency/1024+diff/1024); |
|---|
| 208 | |
|---|
| 209 | delete AD.A; |
|---|
| 210 | } |
|---|
| 211 | |
|---|
| 212 | static DNSDistributor* g_distributor; |
|---|
| 213 | static pthread_mutex_t d_distributorlock =PTHREAD_MUTEX_INITIALIZER; |
|---|
| 214 | static bool g_mustlockdistributor; |
|---|
| 215 | |
|---|
| 216 | //! The qthread receives questions over the internet via the Nameserver class, and hands them to the Distributor for futher processing |
|---|
| 217 | void *qthread(void *number) |
|---|
| 218 | { |
|---|
| 219 | DNSPacket *P; |
|---|
| 220 | |
|---|
| 221 | DNSPacket question; |
|---|
| 222 | DNSPacket cached; |
|---|
| 223 | |
|---|
| 224 | unsigned int &numreceived=*S.getPointer("udp-queries"); |
|---|
| 225 | unsigned int &numanswered=*S.getPointer("udp-answers"); |
|---|
| 226 | |
|---|
| 227 | unsigned int &numreceived4=*S.getPointer("udp4-queries"); |
|---|
| 228 | unsigned int &numanswered4=*S.getPointer("udp4-answers"); |
|---|
| 229 | |
|---|
| 230 | unsigned int &numreceived6=*S.getPointer("udp6-queries"); |
|---|
| 231 | unsigned int &numanswered6=*S.getPointer("udp6-answers"); |
|---|
| 232 | numreceived=-1; |
|---|
| 233 | int diff; |
|---|
| 234 | bool logDNSDetails= ::arg().mustDo("log-dns-details"); |
|---|
| 235 | |
|---|
| 236 | for(;;) { |
|---|
| 237 | if(number==0) { // only run on main thread |
|---|
| 238 | if(!((numreceived++)%250)) { // maintenance tasks |
|---|
| 239 | S.set("latency",(int)avg_latency); |
|---|
| 240 | int qcount, acount; |
|---|
| 241 | g_distributor->getQueueSizes(qcount, acount); |
|---|
| 242 | S.set("qsize-q",qcount); |
|---|
| 243 | } |
|---|
| 244 | } |
|---|
| 245 | |
|---|
| 246 | if(!(P=N->receive(&question))) { // receive a packet inline |
|---|
| 247 | continue; // packet was broken, try again |
|---|
| 248 | } |
|---|
| 249 | |
|---|
| 250 | if(P->remote.getSocklen()==sizeof(sockaddr_in)) |
|---|
| 251 | numreceived4++; |
|---|
| 252 | else |
|---|
| 253 | numreceived6++; |
|---|
| 254 | |
|---|
| 255 | S.ringAccount("queries", P->qdomain+"/"+P->qtype.getName()); |
|---|
| 256 | S.ringAccount("remotes",P->getRemote()); |
|---|
| 257 | if(logDNSDetails) |
|---|
| 258 | L << Logger::Notice<<"Remote "<< P->remote.toString() <<" wants '" << P->qdomain<<"|"<<P->qtype.getName() << |
|---|
| 259 | "', do = " <<P->d_dnssecOk <<", bufsize = "<< P->getMaxReplyLen()<<": "; |
|---|
| 260 | |
|---|
| 261 | if((P->d.opcode != Opcode::Notify) && P->couldBeCached() && PC.get(P, &cached)) { // short circuit - does the PacketCache recognize this question? |
|---|
| 262 | if(logDNSDetails) |
|---|
| 263 | L<<"packetcache HIT"<<endl; |
|---|
| 264 | cached.setRemote(&P->remote); // inlined |
|---|
| 265 | cached.setSocket(P->getSocket()); // inlined |
|---|
| 266 | cached.setMaxReplyLen(P->getMaxReplyLen()); |
|---|
| 267 | cached.d.rd=P->d.rd; // copy in recursion desired bit |
|---|
| 268 | cached.d.id=P->d.id; |
|---|
| 269 | cached.commitD(); // commit d to the packet inlined |
|---|
| 270 | |
|---|
| 271 | N->send(&cached); // answer it then inlined |
|---|
| 272 | diff=P->d_dt.udiff(); |
|---|
| 273 | avg_latency=(int)(0.999*avg_latency+0.001*diff); // 'EWMA' |
|---|
| 274 | |
|---|
| 275 | numanswered++; |
|---|
| 276 | if(P->remote.sin4.sin_family==AF_INET) |
|---|
| 277 | numanswered4++; |
|---|
| 278 | else |
|---|
| 279 | numanswered6++; |
|---|
| 280 | |
|---|
| 281 | continue; |
|---|
| 282 | } |
|---|
| 283 | if(logDNSDetails) |
|---|
| 284 | L<<"packetcache MISS"<<endl; |
|---|
| 285 | if(g_mustlockdistributor) { |
|---|
| 286 | Lock l(&d_distributorlock); |
|---|
| 287 | g_distributor->question(P, &sendout); // otherwise, give to the distributor |
|---|
| 288 | } |
|---|
| 289 | else |
|---|
| 290 | g_distributor->question(P, &sendout); // otherwise, give to the distributor |
|---|
| 291 | } |
|---|
| 292 | return 0; |
|---|
| 293 | } |
|---|
| 294 | |
|---|
| 295 | void mainthread() |
|---|
| 296 | { |
|---|
| 297 | Utility::srandom(time(0)); |
|---|
| 298 | |
|---|
| 299 | int newgid=0; |
|---|
| 300 | if(!::arg()["setgid"].empty()) |
|---|
| 301 | newgid=Utility::makeGidNumeric(::arg()["setgid"]); |
|---|
| 302 | int newuid=0; |
|---|
| 303 | if(!::arg()["setuid"].empty()) |
|---|
| 304 | newuid=Utility::makeUidNumeric(::arg()["setuid"]); |
|---|
| 305 | #ifndef WIN32 |
|---|
| 306 | |
|---|
| 307 | if(!::arg()["chroot"].empty()) { |
|---|
| 308 | if(::arg().mustDo("master") || ::arg().mustDo("slave")) |
|---|
| 309 | gethostbyname("a.root-servers.net"); // this forces all lookup libraries to be loaded |
|---|
| 310 | if(chroot(::arg()["chroot"].c_str())<0 || chdir("/")<0) { |
|---|
| 311 | L<<Logger::Error<<"Unable to chroot to '"+::arg()["chroot"]+"': "<<strerror(errno)<<", exiting"<<endl; |
|---|
| 312 | exit(1); |
|---|
| 313 | } |
|---|
| 314 | else |
|---|
| 315 | L<<Logger::Error<<"Chrooted to '"<<::arg()["chroot"]<<"'"<<endl; |
|---|
| 316 | } |
|---|
| 317 | #endif |
|---|
| 318 | StatWebServer sws; |
|---|
| 319 | Utility::dropPrivs(newuid, newgid); |
|---|
| 320 | |
|---|
| 321 | if(::arg().mustDo("recursor")){ |
|---|
| 322 | DP=new DNSProxy(::arg()["recursor"]); |
|---|
| 323 | DP->onlyFrom(::arg()["allow-recursion"]); |
|---|
| 324 | DP->go(); |
|---|
| 325 | } |
|---|
| 326 | // NOW SAFE TO CREATE THREADS! |
|---|
| 327 | dl->go(); |
|---|
| 328 | |
|---|
| 329 | pthread_t qtid; |
|---|
| 330 | |
|---|
| 331 | |
|---|
| 332 | if(::arg().mustDo("webserver")) |
|---|
| 333 | sws.go(); |
|---|
| 334 | |
|---|
| 335 | if(::arg().mustDo("slave") || ::arg().mustDo("master")) |
|---|
| 336 | Communicator.go(); |
|---|
| 337 | |
|---|
| 338 | if(TN) |
|---|
| 339 | TN->go(); // tcp nameserver launch |
|---|
| 340 | |
|---|
| 341 | // fork(); (this worked :-)) |
|---|
| 342 | g_distributor = new DNSDistributor(::arg().asNum("distributor-threads")); // the big dispatcher! |
|---|
| 343 | if(::arg().asNum("receiver-threads") > 1) { |
|---|
| 344 | g_mustlockdistributor=true; |
|---|
| 345 | } |
|---|
| 346 | for(int n=0; n < ::arg().asNum("receiver-threads"); ++n) |
|---|
| 347 | pthread_create(&qtid,0,qthread, reinterpret_cast<void *>(n)); // receives packets |
|---|
| 348 | |
|---|
| 349 | void *p; |
|---|
| 350 | pthread_join(qtid, &p); |
|---|
| 351 | |
|---|
| 352 | L<<Logger::Error<<"Mainthread exiting - should never happen"<<endl; |
|---|
| 353 | } |
|---|
| 354 | |
|---|
| 355 | |
|---|
| 356 | |
|---|
| 357 | |
|---|