]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dynlistener.cc
Sphinx 1.8.0 seems broken, use any other version available instead
[thirdparty/pdns.git] / pdns / dynlistener.cc
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 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
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>
33 #include <boost/algorithm/string.hpp>
34
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>
46 #include <boost/algorithm/string.hpp>
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
56 extern StatBag S;
57
58 DynListener::g_funkdb_t DynListener::s_funcdb;
59 DynListener::g_funk_t* DynListener::s_restfunc;
60
61 DynListener::~DynListener()
62 {
63 if(!d_socketname.empty())
64 unlink(d_socketname.c_str());
65 }
66
67 void DynListener::createSocketAndBind(int family, struct sockaddr*local, size_t len)
68 {
69 d_s=socket(family, SOCK_STREAM,0);
70 setCloseOnExec(d_s);
71
72 if(d_s < 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;
75 else
76 L<<Logger::Error<<"Unable to create control socket on '"<<((ComboAddress *)local)->toStringWithPort()<<"', reason: "<<strerror(errno)<<endl;
77 exit(1);
78 }
79
80 int tmp=1;
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));
83
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;
87 else
88 L<<Logger::Critical<<"Unable to bind to control socket on '"<<((ComboAddress *)local)->toStringWithPort()<<"', reason: "<<strerror(errno)<<endl;
89 exit(1);
90 }
91 }
92
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 */
96 bool 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
104 if (makeUNsockaddr(fname, &addr)) {
105 L<<Logger::Critical<<"Unable to open controlsocket, path '"<<fname<<"' is not a valid UNIX socket path."<<endl;
106 exit(1);
107 }
108
109 int status = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
110 close(fd);
111 return status==0;
112 }
113
114 void DynListener::listenOnUnixDomain(const string& fname)
115 {
116 if(testLive(fname)) {
117 L<<Logger::Critical<<"Previous controlsocket '"<<fname<<"' is in use"<<endl;
118 exit(1);
119 }
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;
123 exit(1);
124 }
125
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;
129 exit(1);
130 }
131
132 createSocketAndBind(AF_UNIX, (struct sockaddr*)& local, sizeof(local));
133 d_socketname=fname;
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;
139 }
140
141 listen(d_s, 10);
142
143 L<<Logger::Warning<<"Listening on controlsocket in '"<<fname<<"'"<<endl;
144 d_nonlocal=true;
145 }
146
147 void DynListener::listenOnTCP(const ComboAddress& local)
148 {
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 }
154 listen(d_s, 10);
155
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()) {
161 d_tcprange.toMasks(::arg()["tcp-control-range"]);
162 L<<Logger::Warning<<"Only allowing TCP control from: "<<d_tcprange.toString()<<endl;
163 }
164 }
165
166
167 DynListener::DynListener(const ComboAddress& local)
168 {
169 listenOnTCP(local);
170 d_tcp=true;
171 d_client=-1;
172 d_tid=0;
173 d_ppid=0;
174 }
175
176 DynListener::DynListener(const string &progname)
177 {
178 d_client=-1;
179 d_tid=0;
180 d_ppid=0;
181 d_s=-1;
182
183 if(!progname.empty()) {
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 += "/";
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) {
199 L<<Logger::Critical<<"Unable to create socket directory ("<<socketname<<") and it does not exist yet"<<endl;
200 exit(1);
201 }
202
203 socketname+=progname+".controlsocket";
204 listenOnUnixDomain(socketname);
205 }
206 else
207 d_nonlocal=false; // we listen on stdin!
208 d_tcp=false;
209 }
210
211 void DynListener::go()
212 {
213 d_ppid=getpid();
214 pthread_create(&d_tid,0,&DynListener::theListenerHelper,this);
215 }
216
217 void *DynListener::theListenerHelper(void *p)
218 {
219 DynListener *us=static_cast<DynListener *>(p);
220 us->theListener();
221 L<<Logger::Error<<"Control listener aborted, please file a bug!"<<endl;
222 return 0;
223 }
224
225 string DynListener::getLine()
226 {
227 vector<char> mesg;
228 mesg.resize(1024000);
229
230 int len;
231
232 ComboAddress remote;
233 socklen_t remlen=remote.getSocklen();
234
235 if(d_nonlocal) {
236 for(;;) {
237 d_client=accept(d_s,(sockaddr*)&remote,&remlen);
238 if(d_client<0) {
239 if(errno!=EINTR)
240 L<<Logger::Error<<"Unable to accept controlsocket connection ("<<d_s<<"): "<<strerror(errno)<<endl;
241 continue;
242 }
243
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");
247 close(d_client);
248 continue;
249 }
250
251 std::shared_ptr<FILE> fp=std::shared_ptr<FILE>(fdopen(dup(d_client), "r"), fclose);
252 if(d_tcp) {
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 }
267 }
268 errno=0;
269 if(!fgets(&mesg[0], mesg.size(), fp.get())) {
270 if(errno)
271 L<<Logger::Error<<"Unable to receive line from controlsocket ("<<d_client<<"): "<<strerror(errno)<<endl;
272 close(d_client);
273 continue;
274 }
275
276 if(strlen(&mesg[0]) == mesg.size()) {
277 L<<Logger::Error<<"Line on controlsocket ("<<d_client<<") was too long"<<endl;
278 close(d_client);
279 continue;
280 }
281 break;
282 }
283 }
284 else {
285 if(isatty(0))
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());
290 else if(len==0)
291 throw PDNSException("Guardian exited - going down as well");
292
293 if(len == (int)mesg.size())
294 throw PDNSException("Line on control console was too long");
295
296 mesg[len]=0;
297 }
298
299 return &mesg[0];
300 }
301
302 void DynListener::sendlines(const string &l)
303 {
304 if(d_nonlocal) {
305 unsigned int sent=0;
306 int ret;
307 while(sent < l.length()) {
308 ret=send(d_client, l.c_str()+sent, l.length()-sent, 0);
309
310 if(ret<0 || !ret) {
311 L<<Logger::Error<<"Error sending data to pdns_control: "<<stringerror()<<endl;
312 break;
313 }
314 sent+=ret;
315 }
316 close(d_client);
317 } else {
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())
324 L<<Logger::Error<<"Error sending data to console: "<<stringerror()<<endl;
325 }
326 }
327
328 void DynListener::registerFunc(const string &name, g_funk_t *gf, const string &usage, const string &args)
329 {
330 g_funkwithusage_t e = {gf, args, usage};
331 s_funcdb[name]=e;
332 }
333
334 void DynListener::registerRestFunc(g_funk_t *gf)
335 {
336 s_restfunc=gf;
337 }
338
339 void DynListener::theListener()
340 {
341 try {
342 signal(SIGPIPE,SIG_IGN);
343
344 for(int n=0;;++n) {
345 string line=getLine();
346 boost::trim_right(line);
347
348 vector<string>parts;
349 stringtok(parts,line," ");
350 if(parts.empty()) {
351 sendlines("Empty line");
352 continue;
353 }
354
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 }
378 }
379 }
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) {
387 L<<Logger::Error<<"Fatal STL error in control listener: "<<e.what()<<endl;
388 }
389 catch(...) {
390 L<<Logger::Error<<"Fatal: unknown exception in control listener occurred"<<endl;
391 }
392 }
393
394
395 string 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
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));
415 }
416 sort(funcs.begin(), funcs.end());
417
418 // hack: this removes the duplicate quit method
419 funcs.resize(unique(funcs.begin(), funcs.end()) - funcs.begin());
420 return boost::join(funcs, "\n");
421 }