]>
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
) :
174 DynListener :: DynListener ( const string
& progname
)
177 if (! progname
. empty ()) {
178 string socketname
= :: arg ()[ "socket-dir" ];
179 if (:: arg ()[ "socket-dir" ]. empty ()) {
180 if (:: arg ()[ "chroot" ]. empty ())
181 socketname
= LOCALSTATEDIR
;
183 socketname
= :: arg ()[ "chroot" ];
184 } else if (!:: arg ()[ "socket-dir" ]. empty () && !:: arg ()[ "chroot" ]. empty ()) {
185 socketname
= :: arg ()[ "chroot" ] + :: arg ()[ "socket-dir" ];
188 cleanSlashes ( socketname
);
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
;
197 socketname
+= progname
+ ".controlsocket" ;
198 listenOnUnixDomain ( socketname
);
201 d_nonlocal
= false ; // we listen on stdin!
204 void DynListener :: go ()
207 pthread_create (& d_tid
, 0 ,& DynListener :: theListenerHelper
, this );
210 void * DynListener :: theListenerHelper ( void * p
)
212 setThreadName ( "pdns/ctrlListen" );
213 DynListener
* us
= static_cast < DynListener
*>( p
);
215 g_log
<< Logger :: Error
<< "Control listener aborted, please file a bug!" << endl
;
219 string
DynListener :: getLine ()
222 mesg
. resize ( 1024000 );
227 socklen_t remlen
= remote
. getSocklen ();
231 d_client
= accept ( d_s
,( sockaddr
*)& remote
,& remlen
);
234 g_log
<< Logger :: Error
<< "Unable to accept controlsocket connection (" << d_s
<< "): " << strerror ( errno
)<< endl
;
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 " );
245 std :: shared_ptr
< FILE > fp
= std :: shared_ptr
< FILE >( fdopen ( dup ( d_client
), "r" ), fclose
);
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
;
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" );
263 if (! fgets (& mesg
[ 0 ], mesg
. size (), fp
. get ())) {
265 g_log
<< Logger :: Error
<< "Unable to receive line from controlsocket (" << d_client
<< "): " << strerror ( errno
)<< endl
;
270 if ( strlen (& mesg
[ 0 ]) == mesg
. size ()) {
271 g_log
<< Logger :: Error
<< "Line on controlsocket (" << d_client
<< ") was too long" << endl
;
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 ());
285 throw PDNSException ( "Guardian exited - going down as well" );
287 if ( len
== ( int ) mesg
. size ())
288 throw PDNSException ( "Line on control console was too long" );
296 void DynListener :: sendlines ( const string
& l
)
301 while ( sent
< l
. length ()) {
302 ret
= send ( d_client
, l
. c_str ()+ sent
, l
. length ()- sent
, 0 );
305 g_log
<< Logger :: Error
<< "Error sending data to pdns_control: " << stringerror ()<< endl
;
313 if (! lines
. empty () && lines
[ lines
. length ()- 1 ] != ' \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
;
322 void DynListener :: registerFunc ( const string
& name
, g_funk_t
* gf
, const string
& usage
, const string
& args
)
324 g_funkwithusage_t e
= { gf
, args
, usage
};
328 void DynListener :: registerRestFunc ( g_funk_t
* gf
)
333 void DynListener :: theListener ()
336 signal ( SIGPIPE
, SIG_IGN
);
339 string line
= getLine ();
340 boost :: trim_right ( line
);
343 stringtok ( parts
, line
, " " );
345 sendlines ( "Empty line" );
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 ());
356 sendlines ((* s_restfunc
)( parts
, d_ppid
));
358 sendlines ( "Unknown command: '" + parts
[ 0 ]+ "'" );
360 catch ( PDNSException
& AE
) {
361 g_log
<< Logger :: Error
<< "Non-fatal error in control listener command '" << line
<< "': " << AE
. reason
<< endl
;
364 g_log
<< Logger :: Error
<< "Non-fatal error 2 in control listener command '" << line
<< "': " << E
<< endl
;
366 catch ( std :: exception
& e
) {
367 g_log
<< Logger :: Error
<< "Non-fatal STL error in control listener command '" << line
<< "': " << e
. what ()<< endl
;
370 g_log
<< Logger :: Error
<< "Non-fatal error in control listener command '" << line
<< "': unknown exception occurred" << endl
;
374 catch ( PDNSException
& AE
) {
375 g_log
<< Logger :: Error
<< "Fatal error in control listener: " << AE
. reason
<< endl
;
378 g_log
<< Logger :: Error
<< "Fatal error 2 in control listener: " << E
<< endl
;
380 catch ( std :: exception
& e
) {
381 g_log
<< Logger :: Error
<< "Fatal STL error in control listener: " << e
. what ()<< endl
;
384 g_log
<< Logger :: Error
<< "Fatal: unknown exception in control listener occurred" << endl
;
389 string
DynListener :: getHelp ()
391 vector
< string
> funcs
;
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
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 " ));
405 const boost :: format
fmter ( "%|-32| %||" );
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
));
410 sort ( funcs
. begin (), funcs
. end ());
412 // hack: this removes the duplicate quit method
413 funcs
. resize ( unique ( funcs
. begin (), funcs
. end ()) - funcs
. begin ());
414 return boost :: join ( funcs
, " \n " );