]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dynlistener.cc
Merge pull request #9099 from PowerDNS/omoerbeek-patch-1
[thirdparty/pdns.git] / pdns / dynlistener.cc
CommitLineData
12471842
PL
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
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.
8 *
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.
12 *
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.
17 *
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.
21 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
12c86877
BH
25#include <cstring>
26#include <string>
27#include <map>
28#include <sys/types.h>
29#include <sys/un.h>
30#include <dlfcn.h>
12c86877 31#include <unistd.h>
040712e0 32#include <boost/algorithm/string.hpp>
dd7da6cd 33
12c86877
BH
34#include <sys/socket.h>
35#include <netinet/in.h>
12c86877
BH
36#include <iostream>
37#include <sstream>
38#include <sys/types.h>
39#include <signal.h>
40
41#include <sys/stat.h>
42#include <fcntl.h>
43#include <unistd.h>
040712e0 44#include <boost/algorithm/string.hpp>
9092419e
RG
45#include <thread>
46
12c86877
BH
47#include "misc.hh"
48#include "dns.hh"
49#include "arguments.hh"
50#include "dnsbackend.hh"
51#include "dynlistener.hh"
52#include "dnspacket.hh"
53#include "logger.hh"
54#include "statbag.hh"
519f5484 55#include "threadname.hh"
12c86877 56
12c86877
BH
57extern StatBag S;
58
040712e0
BH
59DynListener::g_funkdb_t DynListener::s_funcdb;
60DynListener::g_funk_t* DynListener::s_restfunc;
61
e5d684f9
BH
62DynListener::~DynListener()
63{
64 if(!d_socketname.empty())
65 unlink(d_socketname.c_str());
66}
67
040712e0 68void DynListener::createSocketAndBind(int family, struct sockaddr*local, size_t len)
12c86877 69{
040712e0 70 d_s=socket(family, SOCK_STREAM,0);
3897b9e1 71 setCloseOnExec(d_s);
040712e0
BH
72
73 if(d_s < 0) {
aac40041 74 if (family == AF_UNIX)
a702a96c 75 g_log<<Logger::Error<<"Unable to create control socket at '"<<((struct sockaddr_un*)local)->sun_path<<"', reason: "<<stringerror()<<endl;
aac40041 76 else
a702a96c 77 g_log<<Logger::Error<<"Unable to create control socket on '"<<((ComboAddress *)local)->toStringWithPort()<<"', reason: "<<stringerror()<<endl;
040712e0
BH
78 exit(1);
79 }
80
81 int tmp=1;
82 if(setsockopt(d_s,SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0)
a702a96c 83 throw PDNSException(string("Setsockopt failed on control socket: ")+stringerror());
040712e0
BH
84
85 if(bind(d_s, local, len) < 0) {
aac40041 86 if (family == AF_UNIX)
a702a96c 87 g_log<<Logger::Critical<<"Unable to bind to control socket at '"<<((struct sockaddr_un*)local)->sun_path<<"', reason: "<<stringerror()<<endl;
aac40041 88 else
a702a96c 89 g_log<<Logger::Critical<<"Unable to bind to control socket on '"<<((ComboAddress *)local)->toStringWithPort()<<"', reason: "<<stringerror()<<endl;
040712e0
BH
90 exit(1);
91 }
040712e0 92}
12c86877 93
ce19a815 94/* this does a simplistic check, if we can connect, we consider it live. If we can't connect because
95 of access denied, we must consider it dead, nothing we can do about it.
96*/
97bool DynListener::testLive(const string& fname)
98{
99 struct sockaddr_un addr;
100 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
101 if(fd < 0) { // we'll have bigger issues down the road
102 return false;
103 }
104
76cb4593 105 if (makeUNsockaddr(fname, &addr)) {
e6a9dde5 106 g_log<<Logger::Critical<<"Unable to open controlsocket, path '"<<fname<<"' is not a valid UNIX socket path."<<endl;
06dbd1bd
CH
107 exit(1);
108 }
ce19a815 109
110 int status = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
ce19a815 111 close(fd);
112 return status==0;
113}
114
040712e0
BH
115void DynListener::listenOnUnixDomain(const string& fname)
116{
ce19a815 117 if(testLive(fname)) {
e6a9dde5 118 g_log<<Logger::Critical<<"Previous controlsocket '"<<fname<<"' is in use"<<endl;
ce19a815 119 exit(1);
120 }
040712e0
BH
121 int err=unlink(fname.c_str());
122 if(err < 0 && errno!=ENOENT) {
a702a96c 123 g_log<<Logger::Critical<<"Unable to remove (previous) controlsocket at '"<<fname<<"': "<<stringerror()<<endl;
040712e0
BH
124 exit(1);
125 }
126
127 struct sockaddr_un local;
76cb4593 128 if (makeUNsockaddr(fname, &local)) {
e6a9dde5 129 g_log<<Logger::Critical<<"Unable to bind to controlsocket, path '"<<fname<<"' is not a valid UNIX socket path."<<endl;
06dbd1bd
CH
130 exit(1);
131 }
040712e0
BH
132
133 createSocketAndBind(AF_UNIX, (struct sockaddr*)& local, sizeof(local));
134 d_socketname=fname;
135 if(!arg()["setgid"].empty()) {
040712e0 136 if(chmod(fname.c_str(),0660)<0)
a702a96c 137 g_log<<Logger::Error<<"Unable to change group access mode of controlsocket at '"<<fname<<"', reason: "<<stringerror()<<endl;
2211dac9 138 if(chown(fname.c_str(),static_cast<uid_t>(-1), strToGID(arg()["setgid"]))<0)
a702a96c 139 g_log<<Logger::Error<<"Unable to change group ownership of controlsocket at '"<<fname<<"', reason: "<<stringerror()<<endl;
040712e0
BH
140 }
141
df7ca913 142 listen(d_s, 10);
040712e0 143
e6a9dde5 144 g_log<<Logger::Warning<<"Listening on controlsocket in '"<<fname<<"'"<<endl;
040712e0
BH
145 d_nonlocal=true;
146}
147
148void DynListener::listenOnTCP(const ComboAddress& local)
149{
201aac4a
G
150 if (local.isIPv4()) {
151 createSocketAndBind(AF_INET, (struct sockaddr*)& local, local.getSocklen());
152 } else if (local.isIPv6()) {
153 createSocketAndBind(AF_INET6, (struct sockaddr*)& local, local.getSocklen());
154 }
df7ca913
BH
155 listen(d_s, 10);
156
040712e0 157 d_socketaddress=local;
e6a9dde5 158 g_log<<Logger::Warning<<"Listening on controlsocket on '"<<local.toStringWithPort()<<"'"<<endl;
040712e0
BH
159 d_nonlocal=true;
160
161 if(!::arg()["tcp-control-range"].empty()) {
68b011bd 162 d_tcprange.toMasks(::arg()["tcp-control-range"]);
e6a9dde5 163 g_log<<Logger::Warning<<"Only allowing TCP control from: "<<d_tcprange.toString()<<endl;
040712e0
BH
164 }
165}
12c86877 166
040712e0 167
8a70e507 168DynListener::DynListener(const ComboAddress& local) :
eace2c24 169 d_tcp(true)
040712e0
BH
170{
171 listenOnTCP(local);
040712e0
BH
172}
173
eace2c24 174DynListener::DynListener(const string &progname)
040712e0 175{
f41ec614 176
040712e0 177 if(!progname.empty()) {
f0f3f0b0
PL
178 string socketname = ::arg()["socket-dir"];
179 if (::arg()["socket-dir"].empty()) {
180 if (::arg()["chroot"].empty())
2dfe5b32 181 socketname = std::string(LOCALSTATEDIR) + "/pdns";
f0f3f0b0
PL
182 else
183 socketname = ::arg()["chroot"];
184 } else if (!::arg()["socket-dir"].empty() && !::arg()["chroot"].empty()) {
185 socketname = ::arg()["chroot"] + ::arg()["socket-dir"];
186 }
187 socketname += "/";
12c86877
BH
188 cleanSlashes(socketname);
189
190 if(!mkdir(socketname.c_str(),0700)) // make /var directory, if needed
e6a9dde5 191 g_log<<Logger::Warning<<"Created local state directory '"<<socketname<<"'"<<endl;
12c86877 192 else if(errno!=EEXIST) {
e6a9dde5 193 g_log<<Logger::Critical<<"Unable to create socket directory ("<<socketname<<") and it does not exist yet"<<endl;
12c86877
BH
194 exit(1);
195 }
196
040712e0
BH
197 socketname+=progname+".controlsocket";
198 listenOnUnixDomain(socketname);
12c86877
BH
199 }
200 else
040712e0 201 d_nonlocal=false; // we listen on stdin!
12c86877
BH
202}
203
204void DynListener::go()
205{
206 d_ppid=getpid();
9092419e
RG
207 std::thread listener(std::bind(&DynListener::theListener,this));
208 listener.detach();
12c86877
BH
209}
210
211string DynListener::getLine()
212{
093d83fd
BH
213 vector<char> mesg;
214 mesg.resize(1024000);
215
192a5a9b 216 ssize_t len;
12c86877 217
040712e0
BH
218 ComboAddress remote;
219 socklen_t remlen=remote.getSocklen();
220
221 if(d_nonlocal) {
803ca684
BH
222 for(;;) {
223 d_client=accept(d_s,(sockaddr*)&remote,&remlen);
224 if(d_client<0) {
4957a608 225 if(errno!=EINTR)
a702a96c 226 g_log<<Logger::Error<<"Unable to accept controlsocket connection ("<<d_s<<"): "<<stringerror()<<endl;
4957a608 227 continue;
803ca684 228 }
040712e0 229
64bcb6be 230 if(d_tcp && !d_tcprange.match(&remote)) { // checks if the remote is within the permitted range.
e6a9dde5 231 g_log<<Logger::Error<<"Access denied to remote "<<remote.toString()<<" because not allowed"<<endl;
4957a608
BH
232 writen2(d_client, "Access denied to "+remote.toString()+"\n");
233 close(d_client);
234 continue;
e81974aa
BH
235 }
236
dd7da6cd 237 std::shared_ptr<FILE> fp=std::shared_ptr<FILE>(fdopen(dup(d_client), "r"), fclose);
040712e0 238 if(d_tcp) {
4957a608 239 if(!fgets(&mesg[0], mesg.size(), fp.get())) {
a702a96c 240 g_log<<Logger::Error<<"Unable to receive password from controlsocket ("<<d_client<<"): "<<stringerror()<<endl;
4957a608
BH
241 close(d_client);
242 continue;
243 }
244 string password(&mesg[0]);
245 boost::trim(password);
246 if(password.empty() || password!=arg()["tcp-control-secret"]) {
e6a9dde5 247 g_log<<Logger::Error<<"Wrong password on TCP control socket"<<endl;
4957a608
BH
248 writen2(d_client, "Wrong password");
249
250 close(d_client);
251 continue;
252 }
040712e0 253 }
ce19a815 254 errno=0;
040712e0 255 if(!fgets(&mesg[0], mesg.size(), fp.get())) {
ce19a815 256 if(errno)
a702a96c 257 g_log<<Logger::Error<<"Unable to receive line from controlsocket ("<<d_client<<"): "<<stringerror()<<endl;
4957a608
BH
258 close(d_client);
259 continue;
803ca684 260 }
093d83fd 261
040712e0 262 if(strlen(&mesg[0]) == mesg.size()) {
e6a9dde5 263 g_log<<Logger::Error<<"Line on controlsocket ("<<d_client<<") was too long"<<endl;
4957a608
BH
264 close(d_client);
265 continue;
093d83fd 266 }
803ca684 267 break;
cc3afe25 268 }
12c86877
BH
269 }
270 else {
271 if(isatty(0))
a652d270 272 if(write(1, "% ", 2) !=2)
3f81d239 273 throw PDNSException("Writing to console: "+stringerror());
e67beab1 274 if((len=read(0, &mesg[0], mesg.size())) < 0)
3f81d239 275 throw PDNSException("Reading from the control pipe: "+stringerror());
12c86877 276 else if(len==0)
3f81d239 277 throw PDNSException("Guardian exited - going down as well");
093d83fd 278
192a5a9b 279 if(static_cast<size_t>(len) == mesg.size())
3f81d239 280 throw PDNSException("Line on control console was too long");
c7efa8ff 281
093d83fd 282 mesg[len]=0;
12c86877 283 }
192a5a9b 284
093d83fd 285 return &mesg[0];
12c86877
BH
286}
287
e37300dc 288void DynListener::sendlines(const string &l)
12c86877 289{
040712e0 290 if(d_nonlocal) {
cc3afe25
BH
291 unsigned int sent=0;
292 int ret;
040712e0
BH
293 while(sent < l.length()) {
294 ret=send(d_client, l.c_str()+sent, l.length()-sent, 0);
295
cc3afe25 296 if(ret<0 || !ret) {
e6a9dde5 297 g_log<<Logger::Error<<"Error sending data to pdns_control: "<<stringerror()<<endl;
4957a608 298 break;
cc3afe25
BH
299 }
300 sent+=ret;
301 }
302 close(d_client);
c7efa8ff 303 } else {
e37300dc
RA
304 string lines=l;
305 if(!lines.empty() && lines[lines.length()-1] != '\n')
306 lines.append("\n");
307 lines.append(1, '\0');
308 lines.append(1, '\n');
309 if((unsigned int)write(1, lines.c_str(), lines.length()) != lines.length())
e6a9dde5 310 g_log<<Logger::Error<<"Error sending data to console: "<<stringerror()<<endl;
12c86877
BH
311 }
312}
313
3c90953e 314void DynListener::registerFunc(const string &name, g_funk_t *gf, const string &usage, const string &args)
12c86877 315{
3c90953e
PD
316 g_funkwithusage_t e = {gf, args, usage};
317 s_funcdb[name]=e;
12c86877
BH
318}
319
320void DynListener::registerRestFunc(g_funk_t *gf)
321{
040712e0 322 s_restfunc=gf;
12c86877
BH
323}
324
325void DynListener::theListener()
326{
9092419e
RG
327 setThreadName("pdns/ctrlListen");
328
12c86877 329 try {
e67beab1 330 signal(SIGPIPE,SIG_IGN);
12c86877 331
040712e0 332 for(int n=0;;++n) {
12c86877 333 string line=getLine();
040712e0 334 boost::trim_right(line);
12c86877
BH
335
336 vector<string>parts;
337 stringtok(parts,line," ");
338 if(parts.empty()) {
e37300dc 339 sendlines("Empty line");
4957a608 340 continue;
12c86877
BH
341 }
342
eb943953
KM
343 try {
344 parts[0] = toUpper( parts[0] );
345 if(s_funcdb.count(parts[0]))
346 sendlines((*(s_funcdb[parts[0]].func))(parts,d_ppid));
347 else if (parts[0] == "HELP")
348 sendlines(getHelp());
349 else if(s_restfunc)
350 sendlines((*s_restfunc)(parts,d_ppid));
351 else
352 sendlines("Unknown command: '"+parts[0]+"'");
353 }
354 catch(PDNSException &AE) {
e6a9dde5 355 g_log<<Logger::Error<<"Non-fatal error in control listener command '"<<line<<"': "<<AE.reason<<endl;
eb943953
KM
356 }
357 catch(string &E) {
e6a9dde5 358 g_log<<Logger::Error<<"Non-fatal error 2 in control listener command '"<<line<<"': "<<E<<endl;
eb943953
KM
359 }
360 catch(std::exception& e) {
e6a9dde5 361 g_log<<Logger::Error<<"Non-fatal STL error in control listener command '"<<line<<"': "<<e.what()<<endl;
eb943953
KM
362 }
363 catch(...) {
e6a9dde5 364 g_log<<Logger::Error<<"Non-fatal error in control listener command '"<<line<<"': unknown exception occurred"<<endl;
eb943953 365 }
12c86877
BH
366 }
367 }
c7efa8ff 368 catch(PDNSException &AE) {
e6a9dde5 369 g_log<<Logger::Error<<"Fatal error in control listener: "<<AE.reason<<endl;
c7efa8ff
RA
370 }
371 catch(string &E) {
e6a9dde5 372 g_log<<Logger::Error<<"Fatal error 2 in control listener: "<<E<<endl;
c7efa8ff
RA
373 }
374 catch(std::exception& e) {
e6a9dde5 375 g_log<<Logger::Error<<"Fatal STL error in control listener: "<<e.what()<<endl;
c7efa8ff
RA
376 }
377 catch(...) {
e6a9dde5 378 g_log<<Logger::Error<<"Fatal: unknown exception in control listener occurred"<<endl;
c7efa8ff 379 }
12c86877
BH
380}
381
3c90953e
PD
382
383string DynListener::getHelp()
384{
385 vector<string> funcs;
386 string rest;
387
388 // s_restfunc, when in guardian mode, is the function that
389 // can pass commands on to the guarded instance
390 // we just pass it HELP and merge it with our own list
391 if(s_restfunc)
392 {
393 vector<string> parts;
394 parts.push_back("HELP");
395 rest=((*s_restfunc)(parts,d_ppid));
396 boost::split(funcs, rest, boost::is_any_of("\n"));
397 }
398
399 const boost::format fmter("%|-32| %||");
400
c7efa8ff 401 for(g_funkdb_t::const_iterator i=s_funcdb.begin();i!=s_funcdb.end();++i) {
3c90953e
PD
402 funcs.push_back(str(boost::format(fmter) % (toLower(i->first)+" "+i->second.args) % i->second.usage));
403 }
404 sort(funcs.begin(), funcs.end());
405
406 // hack: this removes the duplicate quit method
36926e31 407 funcs.resize(unique(funcs.begin(), funcs.end()) - funcs.begin());
3c90953e 408 return boost::join(funcs, "\n");
06dbd1bd 409}