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