]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dynlistener.cc
spelling: syscall
[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 d_tcp(true)
170 {
171 listenOnTCP(local);
172 }
173
174 DynListener::DynListener(const string &progname)
175 {
176
177 if(!progname.empty()) {
178 string socketname = ::arg()["socket-dir"];
179 if (::arg()["socket-dir"].empty()) {
180 if (::arg()["chroot"].empty())
181 socketname = LOCALSTATEDIR;
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 += "/";
188 cleanSlashes(socketname);
189
190 if(!mkdir(socketname.c_str(),0700)) // make /var directory, if needed
191 g_log<<Logger::Warning<<"Created local state directory '"<<socketname<<"'"<<endl;
192 else if(errno!=EEXIST) {
193 g_log<<Logger::Critical<<"Unable to create socket directory ("<<socketname<<") and it does not exist yet"<<endl;
194 exit(1);
195 }
196
197 socketname+=progname+".controlsocket";
198 listenOnUnixDomain(socketname);
199 }
200 else
201 d_nonlocal=false; // we listen on stdin!
202 }
203
204 void DynListener::go()
205 {
206 d_ppid=getpid();
207 pthread_create(&d_tid,0,&DynListener::theListenerHelper,this);
208 }
209
210 void *DynListener::theListenerHelper(void *p)
211 {
212 setThreadName("pdns/ctrlListen");
213 DynListener *us=static_cast<DynListener *>(p);
214 us->theListener();
215 g_log<<Logger::Error<<"Control listener aborted, please file a bug!"<<endl;
216 return 0;
217 }
218
219 string DynListener::getLine()
220 {
221 vector<char> mesg;
222 mesg.resize(1024000);
223
224 int len;
225
226 ComboAddress remote;
227 socklen_t remlen=remote.getSocklen();
228
229 if(d_nonlocal) {
230 for(;;) {
231 d_client=accept(d_s,(sockaddr*)&remote,&remlen);
232 if(d_client<0) {
233 if(errno!=EINTR)
234 g_log<<Logger::Error<<"Unable to accept controlsocket connection ("<<d_s<<"): "<<strerror(errno)<<endl;
235 continue;
236 }
237
238 if(d_tcp && !d_tcprange.match(&remote)) { // checks if the remote is within the permitted range.
239 g_log<<Logger::Error<<"Access denied to remote "<<remote.toString()<<" because not allowed"<<endl;
240 writen2(d_client, "Access denied to "+remote.toString()+"\n");
241 close(d_client);
242 continue;
243 }
244
245 std::shared_ptr<FILE> fp=std::shared_ptr<FILE>(fdopen(dup(d_client), "r"), fclose);
246 if(d_tcp) {
247 if(!fgets(&mesg[0], mesg.size(), fp.get())) {
248 g_log<<Logger::Error<<"Unable to receive password from controlsocket ("<<d_client<<"): "<<strerror(errno)<<endl;
249 close(d_client);
250 continue;
251 }
252 string password(&mesg[0]);
253 boost::trim(password);
254 if(password.empty() || password!=arg()["tcp-control-secret"]) {
255 g_log<<Logger::Error<<"Wrong password on TCP control socket"<<endl;
256 writen2(d_client, "Wrong password");
257
258 close(d_client);
259 continue;
260 }
261 }
262 errno=0;
263 if(!fgets(&mesg[0], mesg.size(), fp.get())) {
264 if(errno)
265 g_log<<Logger::Error<<"Unable to receive line from controlsocket ("<<d_client<<"): "<<strerror(errno)<<endl;
266 close(d_client);
267 continue;
268 }
269
270 if(strlen(&mesg[0]) == mesg.size()) {
271 g_log<<Logger::Error<<"Line on controlsocket ("<<d_client<<") was too long"<<endl;
272 close(d_client);
273 continue;
274 }
275 break;
276 }
277 }
278 else {
279 if(isatty(0))
280 if(write(1, "% ", 2) !=2)
281 throw PDNSException("Writing to console: "+stringerror());
282 if((len=read(0, &mesg[0], mesg.size())) < 0)
283 throw PDNSException("Reading from the control pipe: "+stringerror());
284 else if(len==0)
285 throw PDNSException("Guardian exited - going down as well");
286
287 if(len == (int)mesg.size())
288 throw PDNSException("Line on control console was too long");
289
290 mesg[len]=0;
291 }
292
293 return &mesg[0];
294 }
295
296 void DynListener::sendlines(const string &l)
297 {
298 if(d_nonlocal) {
299 unsigned int sent=0;
300 int ret;
301 while(sent < l.length()) {
302 ret=send(d_client, l.c_str()+sent, l.length()-sent, 0);
303
304 if(ret<0 || !ret) {
305 g_log<<Logger::Error<<"Error sending data to pdns_control: "<<stringerror()<<endl;
306 break;
307 }
308 sent+=ret;
309 }
310 close(d_client);
311 } else {
312 string lines=l;
313 if(!lines.empty() && lines[lines.length()-1] != '\n')
314 lines.append("\n");
315 lines.append(1, '\0');
316 lines.append(1, '\n');
317 if((unsigned int)write(1, lines.c_str(), lines.length()) != lines.length())
318 g_log<<Logger::Error<<"Error sending data to console: "<<stringerror()<<endl;
319 }
320 }
321
322 void DynListener::registerFunc(const string &name, g_funk_t *gf, const string &usage, const string &args)
323 {
324 g_funkwithusage_t e = {gf, args, usage};
325 s_funcdb[name]=e;
326 }
327
328 void DynListener::registerRestFunc(g_funk_t *gf)
329 {
330 s_restfunc=gf;
331 }
332
333 void DynListener::theListener()
334 {
335 try {
336 signal(SIGPIPE,SIG_IGN);
337
338 for(int n=0;;++n) {
339 string line=getLine();
340 boost::trim_right(line);
341
342 vector<string>parts;
343 stringtok(parts,line," ");
344 if(parts.empty()) {
345 sendlines("Empty line");
346 continue;
347 }
348
349 try {
350 parts[0] = toUpper( parts[0] );
351 if(s_funcdb.count(parts[0]))
352 sendlines((*(s_funcdb[parts[0]].func))(parts,d_ppid));
353 else if (parts[0] == "HELP")
354 sendlines(getHelp());
355 else if(s_restfunc)
356 sendlines((*s_restfunc)(parts,d_ppid));
357 else
358 sendlines("Unknown command: '"+parts[0]+"'");
359 }
360 catch(PDNSException &AE) {
361 g_log<<Logger::Error<<"Non-fatal error in control listener command '"<<line<<"': "<<AE.reason<<endl;
362 }
363 catch(string &E) {
364 g_log<<Logger::Error<<"Non-fatal error 2 in control listener command '"<<line<<"': "<<E<<endl;
365 }
366 catch(std::exception& e) {
367 g_log<<Logger::Error<<"Non-fatal STL error in control listener command '"<<line<<"': "<<e.what()<<endl;
368 }
369 catch(...) {
370 g_log<<Logger::Error<<"Non-fatal error in control listener command '"<<line<<"': unknown exception occurred"<<endl;
371 }
372 }
373 }
374 catch(PDNSException &AE) {
375 g_log<<Logger::Error<<"Fatal error in control listener: "<<AE.reason<<endl;
376 }
377 catch(string &E) {
378 g_log<<Logger::Error<<"Fatal error 2 in control listener: "<<E<<endl;
379 }
380 catch(std::exception& e) {
381 g_log<<Logger::Error<<"Fatal STL error in control listener: "<<e.what()<<endl;
382 }
383 catch(...) {
384 g_log<<Logger::Error<<"Fatal: unknown exception in control listener occurred"<<endl;
385 }
386 }
387
388
389 string DynListener::getHelp()
390 {
391 vector<string> funcs;
392 string rest;
393
394 // s_restfunc, when in guardian mode, is the function that
395 // can pass commands on to the guarded instance
396 // we just pass it HELP and merge it with our own list
397 if(s_restfunc)
398 {
399 vector<string> parts;
400 parts.push_back("HELP");
401 rest=((*s_restfunc)(parts,d_ppid));
402 boost::split(funcs, rest, boost::is_any_of("\n"));
403 }
404
405 const boost::format fmter("%|-32| %||");
406
407 for(g_funkdb_t::const_iterator i=s_funcdb.begin();i!=s_funcdb.end();++i) {
408 funcs.push_back(str(boost::format(fmter) % (toLower(i->first)+" "+i->second.args) % i->second.usage));
409 }
410 sort(funcs.begin(), funcs.end());
411
412 // hack: this removes the duplicate quit method
413 funcs.resize(unique(funcs.begin(), funcs.end()) - funcs.begin());
414 return boost::join(funcs, "\n");
415 }