]>
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"
55 #include "threadname.hh"
59 DynListener :: g_funkdb_t
DynListener :: s_funcdb
;
60 DynListener :: g_funk_t
* DynListener :: s_restfunc
;
62 DynListener ::~ DynListener ()
64 if (! d_socketname
. empty ())
65 unlink ( d_socketname
. c_str ());
68 void DynListener :: createSocketAndBind ( int family
, struct sockaddr
* local
, size_t len
)
70 d_s
= socket ( family
, SOCK_STREAM
, 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
;
77 g_log
<< Logger :: Error
<< "Unable to create control socket on '" <<(( ComboAddress
*) local
)-> toStringWithPort ()<< "', reason: " << strerror ( errno
)<< endl
;
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
));
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
;
89 g_log
<< Logger :: Critical
<< "Unable to bind to control socket on '" <<(( ComboAddress
*) local
)-> toStringWithPort ()<< "', reason: " << strerror ( errno
)<< endl
;
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.
97 bool DynListener :: testLive ( const string
& fname
)
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
105 if ( makeUNsockaddr ( fname
, & addr
)) {
106 g_log
<< Logger :: Critical
<< "Unable to open controlsocket, path '" << fname
<< "' is not a valid UNIX socket path." << endl
;
110 int status
= connect ( fd
, ( struct sockaddr
*)& addr
, sizeof ( addr
));
115 void DynListener :: listenOnUnixDomain ( const string
& fname
)
117 if ( testLive ( fname
)) {
118 g_log
<< Logger :: Critical
<< "Previous controlsocket '" << fname
<< "' is in use" << endl
;
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
;
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
;
133 createSocketAndBind ( AF_UNIX
, ( struct sockaddr
*)& local
, sizeof ( local
));
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
;
144 g_log
<< Logger :: Warning
<< "Listening on controlsocket in '" << fname
<< "'" << endl
;
148 void DynListener :: listenOnTCP ( const ComboAddress
& local
)
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 ());
157 d_socketaddress
= local
;
158 g_log
<< Logger :: Warning
<< "Listening on controlsocket on '" << local
. toStringWithPort ()<< "'" << endl
;
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
;
168 DynListener :: DynListener ( const ComboAddress
& local
)
177 DynListener :: DynListener ( const string
& progname
)
184 if (! progname
. empty ()) {
185 string socketname
= :: arg ()[ "socket-dir" ];
186 if (:: arg ()[ "socket-dir" ]. empty ()) {
187 if (:: arg ()[ "chroot" ]. empty ())
188 socketname
= LOCALSTATEDIR
;
190 socketname
= :: arg ()[ "chroot" ];
191 } else if (!:: arg ()[ "socket-dir" ]. empty () && !:: arg ()[ "chroot" ]. empty ()) {
192 socketname
= :: arg ()[ "chroot" ] + :: arg ()[ "socket-dir" ];
195 cleanSlashes ( socketname
);
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
;
204 socketname
+= progname
+ ".controlsocket" ;
205 listenOnUnixDomain ( socketname
);
208 d_nonlocal
= false ; // we listen on stdin!
212 void DynListener :: go ()
215 pthread_create (& d_tid
, 0 ,& DynListener :: theListenerHelper
, this );
218 void * DynListener :: theListenerHelper ( void * p
)
220 setThreadName ( "pdns/ctrlListen" );
221 DynListener
* us
= static_cast < DynListener
*>( p
);
223 g_log
<< Logger :: Error
<< "Control listener aborted, please file a bug!" << endl
;
227 string
DynListener :: getLine ()
230 mesg
. resize ( 1024000 );
235 socklen_t remlen
= remote
. getSocklen ();
239 d_client
= accept ( d_s
,( sockaddr
*)& remote
,& remlen
);
242 g_log
<< Logger :: Error
<< "Unable to accept controlsocket connection (" << d_s
<< "): " << strerror ( errno
)<< endl
;
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 " );
253 std :: shared_ptr
< FILE > fp
= std :: shared_ptr
< FILE >( fdopen ( dup ( d_client
), "r" ), fclose
);
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
;
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" );
271 if (! fgets (& mesg
[ 0 ], mesg
. size (), fp
. get ())) {
273 g_log
<< Logger :: Error
<< "Unable to receive line from controlsocket (" << d_client
<< "): " << strerror ( errno
)<< endl
;
278 if ( strlen (& mesg
[ 0 ]) == mesg
. size ()) {
279 g_log
<< Logger :: Error
<< "Line on controlsocket (" << d_client
<< ") was too long" << endl
;
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 ());
293 throw PDNSException ( "Guardian exited - going down as well" );
295 if ( len
== ( int ) mesg
. size ())
296 throw PDNSException ( "Line on control console was too long" );
304 void DynListener :: sendlines ( const string
& l
)
309 while ( sent
< l
. length ()) {
310 ret
= send ( d_client
, l
. c_str ()+ sent
, l
. length ()- sent
, 0 );
313 g_log
<< Logger :: Error
<< "Error sending data to pdns_control: " << stringerror ()<< endl
;
321 if (! lines
. empty () && lines
[ lines
. length ()- 1 ] != ' \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
;
330 void DynListener :: registerFunc ( const string
& name
, g_funk_t
* gf
, const string
& usage
, const string
& args
)
332 g_funkwithusage_t e
= { gf
, args
, usage
};
336 void DynListener :: registerRestFunc ( g_funk_t
* gf
)
341 void DynListener :: theListener ()
344 signal ( SIGPIPE
, SIG_IGN
);
347 string line
= getLine ();
348 boost :: trim_right ( line
);
351 stringtok ( parts
, line
, " " );
353 sendlines ( "Empty line" );
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 ());
364 sendlines ((* s_restfunc
)( parts
, d_ppid
));
366 sendlines ( "Unknown command: '" + parts
[ 0 ]+ "'" );
368 catch ( PDNSException
& AE
) {
369 g_log
<< Logger :: Error
<< "Non-fatal error in control listener command '" << line
<< "': " << AE
. reason
<< endl
;
372 g_log
<< Logger :: Error
<< "Non-fatal error 2 in control listener command '" << line
<< "': " << E
<< endl
;
374 catch ( std :: exception
& e
) {
375 g_log
<< Logger :: Error
<< "Non-fatal STL error in control listener command '" << line
<< "': " << e
. what ()<< endl
;
378 g_log
<< Logger :: Error
<< "Non-fatal error in control listener command '" << line
<< "': unknown exception occurred" << endl
;
382 catch ( PDNSException
& AE
) {
383 g_log
<< Logger :: Error
<< "Fatal error in control listener: " << AE
. reason
<< endl
;
386 g_log
<< Logger :: Error
<< "Fatal error 2 in control listener: " << E
<< endl
;
388 catch ( std :: exception
& e
) {
389 g_log
<< Logger :: Error
<< "Fatal STL error in control listener: " << e
. what ()<< endl
;
392 g_log
<< Logger :: Error
<< "Fatal: unknown exception in control listener occurred" << endl
;
397 string
DynListener :: getHelp ()
399 vector
< string
> funcs
;
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
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 " ));
413 const boost :: format
fmter ( "%|-32| %||" );
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
));
418 sort ( funcs
. begin (), funcs
. end ());
420 // hack: this removes the duplicate quit method
421 funcs
. resize ( unique ( funcs
. begin (), funcs
. end ()) - funcs
. begin ());
422 return boost :: join ( funcs
, " \n " );