]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dynlistener.cc
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.
28 #include <sys/types.h>
33 #include <boost/algorithm/string.hpp>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
40 #include <sys/types.h>
46 #include <boost/algorithm/string.hpp>
49 #include "arguments.hh"
50 #include "dnsbackend.hh"
51 #include "dynlistener.hh"
52 #include "dnspacket.hh"
58 DynListener::g_funkdb_t
DynListener::s_funcdb
;
59 DynListener::g_funk_t
* DynListener::s_restfunc
;
61 DynListener::~DynListener()
63 if(!d_socketname
.empty())
64 unlink(d_socketname
.c_str());
67 void DynListener::createSocketAndBind(int family
, struct sockaddr
*local
, size_t len
)
69 d_s
=socket(family
, SOCK_STREAM
,0);
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
;
76 L
<<Logger::Error
<<"Unable to create control socket on '"<<((ComboAddress
*)local
)->toStringWithPort()<<"', reason: "<<strerror(errno
)<<endl
;
81 if(setsockopt(d_s
,SOL_SOCKET
,SO_REUSEADDR
,(char*)&tmp
,sizeof tmp
)<0)
82 throw PDNSException(string("Setsockopt failed on control socket: ")+strerror(errno
));
84 if(bind(d_s
, local
, len
) < 0) {
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
;
88 L
<<Logger::Critical
<<"Unable to bind to control socket on '"<<((ComboAddress
*)local
)->toStringWithPort()<<"', reason: "<<strerror(errno
)<<endl
;
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.
96 bool DynListener::testLive(const string
& fname
)
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
104 if (makeUNsockaddr(fname
, &addr
)) {
105 L
<<Logger::Critical
<<"Unable to open controlsocket, path '"<<fname
<<"' is not a valid UNIX socket path."<<endl
;
109 int status
= connect(fd
, (struct sockaddr
*)&addr
, sizeof(addr
));
114 void DynListener::listenOnUnixDomain(const string
& fname
)
116 if(testLive(fname
)) {
117 L
<<Logger::Critical
<<"Previous controlsocket '"<<fname
<<"' is in use"<<endl
;
120 int err
=unlink(fname
.c_str());
121 if(err
< 0 && errno
!=ENOENT
) {
122 L
<<Logger::Critical
<<"Unable to remove (previous) controlsocket at '"<<fname
<<"': "<<strerror(errno
)<<endl
;
126 struct sockaddr_un local
;
127 if (makeUNsockaddr(fname
, &local
)) {
128 L
<<Logger::Critical
<<"Unable to bind to controlsocket, path '"<<fname
<<"' is not a valid UNIX socket path."<<endl
;
132 createSocketAndBind(AF_UNIX
, (struct sockaddr
*)& local
, sizeof(local
));
134 if(!arg()["setgid"].empty()) {
135 if(chmod(fname
.c_str(),0660)<0)
136 L
<<Logger::Error
<<"Unable to change group access mode of controlsocket at '"<<fname
<<"', reason: "<<strerror(errno
)<<endl
;
137 if(chown(fname
.c_str(),static_cast<uid_t
>(-1),Utility::makeGidNumeric(arg()["setgid"]))<0)
138 L
<<Logger::Error
<<"Unable to change group ownership of controlsocket at '"<<fname
<<"', reason: "<<strerror(errno
)<<endl
;
143 L
<<Logger::Warning
<<"Listening on controlsocket in '"<<fname
<<"'"<<endl
;
147 void DynListener::listenOnTCP(const ComboAddress
& local
)
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());
156 d_socketaddress
=local
;
157 L
<<Logger::Warning
<<"Listening on controlsocket on '"<<local
.toStringWithPort()<<"'"<<endl
;
160 if(!::arg()["tcp-control-range"].empty()) {
161 d_tcprange
.toMasks(::arg()["tcp-control-range"]);
162 L
<<Logger::Warning
<<"Only allowing TCP control from: "<<d_tcprange
.toString()<<endl
;
167 DynListener::DynListener(const ComboAddress
& local
)
176 DynListener::DynListener(const string
&progname
)
183 if(!progname
.empty()) {
184 string socketname
= ::arg()["socket-dir"];
185 if (::arg()["socket-dir"].empty()) {
186 if (::arg()["chroot"].empty())
187 socketname
= LOCALSTATEDIR
;
189 socketname
= ::arg()["chroot"];
190 } else if (!::arg()["socket-dir"].empty() && !::arg()["chroot"].empty()) {
191 socketname
= ::arg()["chroot"] + ::arg()["socket-dir"];
194 cleanSlashes(socketname
);
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
) {
199 L
<<Logger::Critical
<<"Unable to create socket directory ("<<socketname
<<") and it does not exist yet"<<endl
;
203 socketname
+=progname
+".controlsocket";
204 listenOnUnixDomain(socketname
);
207 d_nonlocal
=false; // we listen on stdin!
211 void DynListener::go()
214 pthread_create(&d_tid
,0,&DynListener::theListenerHelper
,this);
217 void *DynListener::theListenerHelper(void *p
)
219 DynListener
*us
=static_cast<DynListener
*>(p
);
221 L
<<Logger::Error
<<"Control listener aborted, please file a bug!"<<endl
;
225 string
DynListener::getLine()
228 mesg
.resize(1024000);
233 socklen_t remlen
=remote
.getSocklen();
237 d_client
=accept(d_s
,(sockaddr
*)&remote
,&remlen
);
240 L
<<Logger::Error
<<"Unable to accept controlsocket connection ("<<d_s
<<"): "<<strerror(errno
)<<endl
;
244 if(d_tcp
&& !d_tcprange
.match(&remote
)) { // checks if the remote is within the permitted range.
245 L
<<Logger::Error
<<"Access denied to remote "<<remote
.toString()<<" because not allowed"<<endl
;
246 writen2(d_client
, "Access denied to "+remote
.toString()+"\n");
251 std::shared_ptr
<FILE> fp
=std::shared_ptr
<FILE>(fdopen(dup(d_client
), "r"), fclose
);
253 if(!fgets(&mesg
[0], mesg
.size(), fp
.get())) {
254 L
<<Logger::Error
<<"Unable to receive password from controlsocket ("<<d_client
<<"): "<<strerror(errno
)<<endl
;
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");
269 if(!fgets(&mesg
[0], mesg
.size(), fp
.get())) {
271 L
<<Logger::Error
<<"Unable to receive line from controlsocket ("<<d_client
<<"): "<<strerror(errno
)<<endl
;
276 if(strlen(&mesg
[0]) == mesg
.size()) {
277 L
<<Logger::Error
<<"Line on controlsocket ("<<d_client
<<") was too long"<<endl
;
286 if(write(1, "% ", 2) !=2)
287 throw PDNSException("Writing to console: "+stringerror());
288 if((len
=read(0, &mesg
[0], mesg
.size())) < 0)
289 throw PDNSException("Reading from the control pipe: "+stringerror());
291 throw PDNSException("Guardian exited - going down as well");
293 if(len
== (int)mesg
.size())
294 throw PDNSException("Line on control console was too long");
302 void DynListener::sendlines(const string
&l
)
307 while(sent
< l
.length()) {
308 ret
=send(d_client
, l
.c_str()+sent
, l
.length()-sent
, 0);
311 L
<<Logger::Error
<<"Error sending data to pdns_control: "<<stringerror()<<endl
;
319 if(!lines
.empty() && lines
[lines
.length()-1] != '\n')
321 lines
.append(1, '\0');
322 lines
.append(1, '\n');
323 if((unsigned int)write(1, lines
.c_str(), lines
.length()) != lines
.length())
324 L
<<Logger::Error
<<"Error sending data to console: "<<stringerror()<<endl
;
328 void DynListener::registerFunc(const string
&name
, g_funk_t
*gf
, const string
&usage
, const string
&args
)
330 g_funkwithusage_t e
= {gf
, args
, usage
};
334 void DynListener::registerRestFunc(g_funk_t
*gf
)
339 void DynListener::theListener()
342 signal(SIGPIPE
,SIG_IGN
);
345 string line
=getLine();
346 boost::trim_right(line
);
349 stringtok(parts
,line
," ");
351 sendlines("Empty line");
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());
362 sendlines((*s_restfunc
)(parts
,d_ppid
));
364 sendlines("Unknown command: '"+parts
[0]+"'");
366 catch(PDNSException
&AE
) {
367 L
<<Logger::Error
<<"Non-fatal error in control listener command '"<<line
<<"': "<<AE
.reason
<<endl
;
370 L
<<Logger::Error
<<"Non-fatal error 2 in control listener command '"<<line
<<"': "<<E
<<endl
;
372 catch(std::exception
& e
) {
373 L
<<Logger::Error
<<"Non-fatal STL error in control listener command '"<<line
<<"': "<<e
.what()<<endl
;
376 L
<<Logger::Error
<<"Non-fatal error in control listener command '"<<line
<<"': unknown exception occurred"<<endl
;
380 catch(PDNSException
&AE
) {
381 L
<<Logger::Error
<<"Fatal error in control listener: "<<AE
.reason
<<endl
;
384 L
<<Logger::Error
<<"Fatal error 2 in control listener: "<<E
<<endl
;
386 catch(std::exception
& e
) {
387 L
<<Logger::Error
<<"Fatal STL error in control listener: "<<e
.what()<<endl
;
390 L
<<Logger::Error
<<"Fatal: unknown exception in control listener occurred"<<endl
;
395 string
DynListener::getHelp()
397 vector
<string
> funcs
;
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
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"));
411 const boost::format
fmter("%|-32| %||");
413 for(g_funkdb_t::const_iterator i
=s_funcdb
.begin();i
!=s_funcdb
.end();++i
) {
414 funcs
.push_back(str(boost::format(fmter
) % (toLower(i
->first
)+" "+i
->second
.args
) % i
->second
.usage
));
416 sort(funcs
.begin(), funcs
.end());
418 // hack: this removes the duplicate quit method
419 funcs
.resize(unique(funcs
.begin(), funcs
.end()) - funcs
.begin());
420 return boost::join(funcs
, "\n");