| 1 | /* |
|---|
| 2 | PowerDNS Versatile Database Driven Nameserver |
|---|
| 3 | Copyright (C) 2002 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 as published by |
|---|
| 7 | the Free Software Foundation; either version 2 of the License, or |
|---|
| 8 | (at your option) any later version. |
|---|
| 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|---|
| 18 | */ |
|---|
| 19 | #include "common_startup.hh" |
|---|
| 20 | #include "recbcomm.hh" |
|---|
| 21 | |
|---|
| 22 | typedef Distributor<DNSPacket,DNSPacket,PacketHandler> DNSDistributor; |
|---|
| 23 | |
|---|
| 24 | |
|---|
| 25 | ArgvMap theArg; |
|---|
| 26 | StatBag S; //!< Statistics are gathered accross PDNS via the StatBag class S |
|---|
| 27 | PacketCache PC; //!< This is the main PacketCache, shared accross all threads |
|---|
| 28 | DNSProxy *DP; |
|---|
| 29 | DynListener *dl; |
|---|
| 30 | CommunicatorClass Communicator; |
|---|
| 31 | UDPNameserver *N; |
|---|
| 32 | int avg_latency; |
|---|
| 33 | TCPNameserver *TN; |
|---|
| 34 | SyncresCommunicator* SRC; |
|---|
| 35 | |
|---|
| 36 | ArgvMap &arg() |
|---|
| 37 | { |
|---|
| 38 | return theArg; |
|---|
| 39 | } |
|---|
| 40 | |
|---|
| 41 | |
|---|
| 42 | void declareArguments() |
|---|
| 43 | { |
|---|
| 44 | arg().set("local-port","The port on which we listen")="53"; |
|---|
| 45 | arg().setSwitch("log-failed-updates","If PDNS should log failed update requests")=""; |
|---|
| 46 | arg().setSwitch("log-dns-details","If PDNS should log DNS non-erroneous details")=""; |
|---|
| 47 | arg().set("urlredirector","Where we send hosts to that need to be url redirected")="127.0.0.1"; |
|---|
| 48 | arg().set("smtpredirector","Our smtpredir MX host")="a.misconfigured.powerdns.smtp.server"; |
|---|
| 49 | arg().set("local-address","Local IP address to which we bind")="0.0.0.0"; |
|---|
| 50 | arg().set("local-ipv6","Local IP address to which we bind")=""; |
|---|
| 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 | arg().set("only-soa","Make sure that no SOA serial is less than this number")="org"; |
|---|
| 54 | arg().setCmd("help","Provide a helpful message"); |
|---|
| 55 | arg().setCmd("config","Provide a helpful message"); |
|---|
| 56 | arg().setCmd("list-modules","Lists all modules available"); |
|---|
| 57 | arg().setCmd("no-config","Don't parse configuration file"); |
|---|
| 58 | |
|---|
| 59 | arg().set("control-console","Debugging switch - don't use")="no"; // but I know you will! |
|---|
| 60 | arg().set("fancy-records","Process URL and MBOXFW records")="no"; |
|---|
| 61 | arg().set("wildcard-url","Process URL and MBOXFW records")="no"; |
|---|
| 62 | arg().set("wildcards","Honor wildcards in the database")=""; |
|---|
| 63 | arg().set("loglevel","Amount of logging. Higher is more. Do not set below 3")="4"; |
|---|
| 64 | arg().set("default-soa-name","name to insert in the SOA record if none set in the backend")="a.misconfigured.powerdns.server"; |
|---|
| 65 | arg().set("distributor-threads","Default number of Distributor (backend) threads to start")="3"; |
|---|
| 66 | arg().set("queue-limit","Maximum number of milliseconds to queue a query")="1500"; |
|---|
| 67 | arg().set("recursor","If recursion is desired, IP address of a recursing nameserver")="no"; |
|---|
| 68 | arg().set("lazy-recursion","Only recurse if question cannot be answered locally")="yes"; |
|---|
| 69 | arg().set("allow-recursion","List of netmasks that are allowed to recurse")=""; |
|---|
| 70 | |
|---|
| 71 | arg().set("disable-tcp","Do not listen to TCP queries")="no"; |
|---|
| 72 | arg().set("disable-axfr","Do not allow zone transfers")="no"; |
|---|
| 73 | |
|---|
| 74 | arg().set("config-name","Name of this virtual configuration - will rename the binary image")=""; |
|---|
| 75 | |
|---|
| 76 | arg().set("load-modules","Load this module - supply absolute or relative path")=""; |
|---|
| 77 | arg().set("launch","Which backends to launch and order to query them in")=""; |
|---|
| 78 | arg().setSwitch("disable-axfr","Disable zonetransfers but do allow TCP queries")="no"; |
|---|
| 79 | arg().set("allow-axfr-ips","If disabled, DO allow zonetransfers from these IP addresses")=""; |
|---|
| 80 | arg().set("slave-cycle-interval","Reschedule failed SOA serial checks once every .. seconds")="60"; |
|---|
| 81 | |
|---|
| 82 | arg().setSwitch("slave","Act as a slave")="no"; |
|---|
| 83 | arg().setSwitch("master","Act as a master")="no"; |
|---|
| 84 | arg().setSwitch("guardian","Run within a guardian process")="no"; |
|---|
| 85 | arg().setSwitch("skip-cname","Do not perform CNAME indirection for each query")="no"; |
|---|
| 86 | arg().setSwitch("strict-rfc-axfrs","Perform strictly rfc compliant axfrs (very slow)")="no"; |
|---|
| 87 | |
|---|
| 88 | arg().setSwitch("webserver","Start a webserver for monitoring")="no"; |
|---|
| 89 | arg().setSwitch("webserver-print-arguments","If the webserver should print arguments")="no"; |
|---|
| 90 | arg().set("webserver-address","IP Address of webserver to listen on")="127.0.0.1"; |
|---|
| 91 | arg().set("webserver-port","Port of webserver to listen on")="8081"; |
|---|
| 92 | arg().set("webserver-password","Password required for accessing the webserver")=""; |
|---|
| 93 | |
|---|
| 94 | arg().set("receiver-threads","Number of receiver threads to launch")="1"; |
|---|
| 95 | |
|---|
| 96 | arg().setSwitch("out-of-zone-additional-processing","Do out of zone additional processing")="no"; |
|---|
| 97 | arg().setSwitch("query-logging","Hint backends that queries should be logged")="no"; |
|---|
| 98 | |
|---|
| 99 | arg().set("cache-ttl","Seconds to store packets in the PacketCache")="20"; |
|---|
| 100 | arg().set("recursive-cache-ttl","Seconds to store packets in the PacketCache")="10"; |
|---|
| 101 | arg().set("negquery-cache-ttl","Seconds to store packets in the PacketCache")="60"; |
|---|
| 102 | arg().set("query-cache-ttl","Seconds to store packets in the PacketCache")="20"; |
|---|
| 103 | arg().set("soa-minimum-ttl","Default SOA mininum ttl")="3600"; |
|---|
| 104 | arg().set("max-tcp-connections","Maximum number of TCP connections")="10"; |
|---|
| 105 | |
|---|
| 106 | arg().setSwitch( "use-logfile", "Use a log file" )= "no"; |
|---|
| 107 | arg().set( "logfile", "Logfile to use" )= "pdns.log"; |
|---|
| 108 | arg().set("setuid","If set, change user id to this uid for more security")=""; |
|---|
| 109 | arg().set("setgid","If set, change group id to this gid for more security")=""; |
|---|
| 110 | } |
|---|
| 111 | |
|---|
| 112 | |
|---|
| 113 | |
|---|
| 114 | void declareStats(void) |
|---|
| 115 | { |
|---|
| 116 | S.declare("udp-queries","Number of UDP queries received"); |
|---|
| 117 | S.declare("udp-answers","Number of answers sent out over UDP"); |
|---|
| 118 | S.declare("recursing-answers","Number of recursive answers sent out"); |
|---|
| 119 | S.declare("recursing-questions","Number of questions sent to recursor"); |
|---|
| 120 | S.declare("corrupt-packets","Number of corrupt packets received"); |
|---|
| 121 | |
|---|
| 122 | S.declare("tcp-queries","Number of TCP queries received"); |
|---|
| 123 | S.declare("tcp-answers","Number of answers sent out over TCP"); |
|---|
| 124 | |
|---|
| 125 | S.declare("qsize-q","Number of questions waiting for database attention"); |
|---|
| 126 | |
|---|
| 127 | S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance"); |
|---|
| 128 | S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance"); |
|---|
| 129 | |
|---|
| 130 | S.declare("query-cache-hit","Number of hits on the query cache"); |
|---|
| 131 | S.declare("query-cache-miss","Number of misses on the query cache"); |
|---|
| 132 | |
|---|
| 133 | |
|---|
| 134 | S.declare("servfail-packets","Number of times a server-failed packet was sent out"); |
|---|
| 135 | S.declare("latency","Average number of microseconds needed to answer a question"); |
|---|
| 136 | S.declare("timedout-packets","Number of packets which weren't answered within timeout set"); |
|---|
| 137 | |
|---|
| 138 | S.declareRing("queries","UDP Queries Received"); |
|---|
| 139 | S.declareRing("nxdomain-queries","Queries for non-existent records within existent domains"); |
|---|
| 140 | S.declareRing("noerror-queries","Queries for existing records, but for type we don't have"); |
|---|
| 141 | S.declareRing("servfail-queries","Queries that could not be answered due to backend errors"); |
|---|
| 142 | S.declareRing("unauth-queries","Queries for domains that we are not authoritative for"); |
|---|
| 143 | S.declareRing("logmessages","Log Messages"); |
|---|
| 144 | S.declareRing("remotes","Remote server IP addresses"); |
|---|
| 145 | S.declareRing("remotes-unauth","Remote hosts querying domains for which we are not auth"); |
|---|
| 146 | S.declareRing("remotes-corrupt","Remote hosts sending corrupt packets"); |
|---|
| 147 | |
|---|
| 148 | } |
|---|
| 149 | |
|---|
| 150 | |
|---|
| 151 | int isGuarded(char **argv) |
|---|
| 152 | { |
|---|
| 153 | char *p=strstr(argv[0],"-instance"); |
|---|
| 154 | |
|---|
| 155 | return !!p; |
|---|
| 156 | } |
|---|
| 157 | |
|---|
| 158 | |
|---|
| 159 | void sendout(const DNSDistributor::AnswerData &AD) |
|---|
| 160 | { |
|---|
| 161 | static int &numanswered=*S.getPointer("udp-answers"); |
|---|
| 162 | if(!AD.A) |
|---|
| 163 | return; |
|---|
| 164 | |
|---|
| 165 | N->send(AD.A); |
|---|
| 166 | numanswered++; |
|---|
| 167 | int diff=AD.A->d_dt.udiff(); |
|---|
| 168 | avg_latency=(int)(0.999*avg_latency+0.001*diff); |
|---|
| 169 | |
|---|
| 170 | delete AD.A; |
|---|
| 171 | |
|---|
| 172 | |
|---|
| 173 | } |
|---|
| 174 | |
|---|
| 175 | |
|---|
| 176 | //! The qthread receives questions over the internet via the Nameserver class, and hands them to the Distributor for futher processing |
|---|
| 177 | void *qthread(void *p) |
|---|
| 178 | { |
|---|
| 179 | DNSDistributor *D=static_cast<DNSDistributor *>(p); |
|---|
| 180 | |
|---|
| 181 | DNSPacket *P; |
|---|
| 182 | |
|---|
| 183 | DNSPacket question; |
|---|
| 184 | DNSPacket cached; |
|---|
| 185 | |
|---|
| 186 | int &numreceived=*S.getPointer("udp-queries"); |
|---|
| 187 | int &numanswered=*S.getPointer("udp-answers"); |
|---|
| 188 | numreceived=-1; |
|---|
| 189 | int diff; |
|---|
| 190 | |
|---|
| 191 | for(;;) { |
|---|
| 192 | if(!((numreceived++)%50)) { // maintenance tasks |
|---|
| 193 | S.set("latency",(int)avg_latency); |
|---|
| 194 | int qcount, acount; |
|---|
| 195 | D->getQueueSizes(qcount, acount); |
|---|
| 196 | S.set("qsize-q",qcount); |
|---|
| 197 | } |
|---|
| 198 | |
|---|
| 199 | if(!(P=N->receive(&question))) { // receive a packet inline |
|---|
| 200 | continue; // packet was broken, try again |
|---|
| 201 | } |
|---|
| 202 | |
|---|
| 203 | |
|---|
| 204 | S.ringAccount("queries", P->qdomain+"/"+P->qtype.getName()); |
|---|
| 205 | S.ringAccount("remotes",P->getRemote()); |
|---|
| 206 | |
|---|
| 207 | if(PC.get(P,&cached)) { // short circuit - does the PacketCache recognize this question? |
|---|
| 208 | cached.setRemote((struct sockaddr *)(P->remote),P->d_socklen); // inlined |
|---|
| 209 | cached.setSocket(P->getSocket()); // inlined |
|---|
| 210 | cached.spoofID(P->d.id); // inlined |
|---|
| 211 | cached.d.rd=P->d.rd; // copy in recursion desired bit |
|---|
| 212 | cached.commitD(); // commit d to the packet inlined |
|---|
| 213 | |
|---|
| 214 | N->send(&cached); // answer it then inlined |
|---|
| 215 | diff=P->d_dt.udiff(); |
|---|
| 216 | avg_latency=(int)(0.999*avg_latency+0.001*diff); // 'EWMA' |
|---|
| 217 | |
|---|
| 218 | numanswered++; |
|---|
| 219 | continue; |
|---|
| 220 | } |
|---|
| 221 | |
|---|
| 222 | D->question(P, &sendout); // otherwise, give to the distributor |
|---|
| 223 | } |
|---|
| 224 | return 0; |
|---|
| 225 | } |
|---|
| 226 | |
|---|
| 227 | |
|---|
| 228 | void mainthread() |
|---|
| 229 | { |
|---|
| 230 | Utility::srandom(time(0)); |
|---|
| 231 | |
|---|
| 232 | int newgid=0; |
|---|
| 233 | if(!arg()["setgid"].empty()) |
|---|
| 234 | newgid=Utility::makeGidNumeric(arg()["setgid"]); |
|---|
| 235 | int newuid=0; |
|---|
| 236 | if(!arg()["setuid"].empty()) |
|---|
| 237 | newuid=Utility::makeUidNumeric(arg()["setuid"]); |
|---|
| 238 | #ifndef WIN32 |
|---|
| 239 | if(!arg()["chroot"].empty()) { |
|---|
| 240 | if(chroot(arg()["chroot"].c_str())<0) { |
|---|
| 241 | L<<Logger::Error<<"Unable to chroot to '"+arg()["chroot"]+"': "<<strerror(errno)<<", exiting"<<endl; |
|---|
| 242 | exit(1); |
|---|
| 243 | } |
|---|
| 244 | else |
|---|
| 245 | L<<Logger::Error<<"Chrooted to '"<<arg()["chroot"]<<"'"<<endl; |
|---|
| 246 | } |
|---|
| 247 | #endif |
|---|
| 248 | |
|---|
| 249 | Utility::dropPrivs(newuid, newgid); |
|---|
| 250 | |
|---|
| 251 | if(arg().mustDo("recursor")){ |
|---|
| 252 | DP=new DNSProxy(arg()["recursor"]); |
|---|
| 253 | DP->onlyFrom(arg()["allow-recursion"]); |
|---|
| 254 | DP->go(); |
|---|
| 255 | } |
|---|
| 256 | // NOW SAFE TO CREATE THREADS! |
|---|
| 257 | dl->go(); |
|---|
| 258 | |
|---|
| 259 | pthread_t qtid; |
|---|
| 260 | StatWebServer sws; |
|---|
| 261 | |
|---|
| 262 | if(arg()["webserver"]!="no") |
|---|
| 263 | sws.go(); |
|---|
| 264 | |
|---|
| 265 | if(arg().mustDo("slave") || arg().mustDo("master")) |
|---|
| 266 | Communicator.go(); |
|---|
| 267 | |
|---|
| 268 | //SRC=new SyncresCommunicator(); |
|---|
| 269 | |
|---|
| 270 | if(TN) |
|---|
| 271 | TN->go(); // tcp nameserver launch |
|---|
| 272 | |
|---|
| 273 | // fork(); (this worked :-)) |
|---|
| 274 | for(int n=0;n<arg().asNum("receiver-threads");++n) { |
|---|
| 275 | DNSDistributor *D= new DNSDistributor(arg().asNum("distributor-threads")); // the big dispatcher! |
|---|
| 276 | pthread_create(&qtid,0,qthread,static_cast<void *>(D)); // receives packets |
|---|
| 277 | } |
|---|
| 278 | |
|---|
| 279 | void *p; |
|---|
| 280 | pthread_join(qtid, &p); |
|---|
| 281 | |
|---|
| 282 | L<<Logger::Error<<"Mainthread exiting - should never happen"<<endl; |
|---|
| 283 | } |
|---|
| 284 | |
|---|
| 285 | |
|---|
| 286 | |
|---|
| 287 | |
|---|