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