Commit 1830215f authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

Merge #805

parents 24ec2f14 1627c015
......@@ -49,6 +49,7 @@
#include <asiolink/asiolink.h>
#include <log/logger_support.h>
#include <server_common/keyring.h>
#include <server_common/socket_request.h>
using namespace std;
using namespace isc::asiodns;
......@@ -158,6 +159,8 @@ main(int argc, char* argv[]) {
cc_session = new Session(io_service.get_io_service());
LOG_DEBUG(auth_logger, DBG_AUTH_START, AUTH_CONFIG_CHANNEL_CREATED);
// Initialize the Socket Requestor
isc::server_common::initSocketReqeustor(*cc_session);
// We delay starting listening to new commands/config just before we
// go into the main loop to avoid confusion due to mixture of
......
......@@ -41,6 +41,7 @@
#include <testutils/dnsmessage_test.h>
#include <testutils/srv_test.h>
#include <testutils/portconfig.h>
#include <testutils/socket_request.h>
using namespace std;
using namespace isc::cc;
......@@ -68,7 +69,8 @@ protected:
AuthSrvTest() :
dnss_(ios_, NULL, NULL, NULL),
server(true, xfrout),
rrclass(RRClass::IN())
rrclass(RRClass::IN()),
sock_requestor_(dnss_, address_store_, 53210)
{
server.setDNSService(dnss_);
server.setXfrinSession(&notify_session);
......@@ -85,6 +87,8 @@ protected:
AuthSrv server;
const RRClass rrclass;
vector<uint8_t> response_data;
AddressList address_store_;
TestSocketRequestor sock_requestor_;
};
// A helper function that builds a response to version.bind/TXT/CH that
......@@ -887,6 +891,20 @@ TEST_F(AuthSrvTest, stop) {
TEST_F(AuthSrvTest, listenAddresses) {
isc::testutils::portconfig::listenAddresses(server);
// Check it requests the correct addresses
const char* tokens[] = {
"TCP:127.0.0.1:53210:1",
"UDP:127.0.0.1:53210:2",
"TCP:::1:53210:3",
"UDP:::1:53210:4",
NULL
};
sock_requestor_.checkTokens(tokens, sock_requestor_.given_tokens_,
"Given tokens");
// It returns back to empty set of addresses afterwards, so
// they should be released
sock_requestor_.checkTokens(tokens, sock_requestor_.released_tokens_,
"Released tokens");
}
}
......@@ -31,6 +31,7 @@
#include <testutils/mockups.h>
#include <testutils/portconfig.h>
#include <testutils/socket_request.h>
using namespace isc::dns;
using namespace isc::data;
......@@ -44,7 +45,8 @@ protected:
AuthConfigTest() :
dnss_(ios_, NULL, NULL, NULL),
rrclass(RRClass::IN()),
server(true, xfrout)
server(true, xfrout),
sock_requestor_(dnss_, address_store_, 53210)
{
server.setDNSService(dnss_);
}
......@@ -53,6 +55,9 @@ protected:
const RRClass rrclass;
MockXfroutClient xfrout;
AuthSrv server;
isc::server_common::portconfig::AddressList address_store_;
private:
isc::testutils::TestSocketRequestor sock_requestor_;
};
TEST_F(AuthConfigTest, datasourceConfig) {
......
......@@ -44,12 +44,10 @@ import os
# installed on the system
if "B10_FROM_SOURCE" in os.environ:
SPECFILE_LOCATION = os.environ["B10_FROM_SOURCE"] + "/src/bin/bind10/bob.spec"
ADD_LIBEXEC_PATH = False
else:
PREFIX = "@prefix@"
DATAROOTDIR = "@datarootdir@"
SPECFILE_LOCATION = "@datadir@/@PACKAGE@/bob.spec".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
ADD_LIBEXEC_PATH = True
import subprocess
import signal
......@@ -65,6 +63,7 @@ import pwd
import posix
import copy
from bind10_config import LIBEXECPATH
import isc.cc
import isc.util.process
import isc.net.parse
......@@ -85,8 +84,8 @@ DBG_PROCESS = logger.DBGLVL_TRACE_BASIC
DBG_COMMANDS = logger.DBGLVL_TRACE_DETAIL
# Messages sent over the unix domain socket to indicate if it is followed by a real socket
CREATOR_SOCKET_OK = "1\n"
CREATOR_SOCKET_UNAVAILABLE = "0\n"
CREATOR_SOCKET_OK = b"1\n"
CREATOR_SOCKET_UNAVAILABLE = b"0\n"
# Assign this process some longer name
isc.util.process.rename(sys.argv[0])
......@@ -151,8 +150,7 @@ class ProcessInfo:
# on construction (self.env).
spawn_env = copy.deepcopy(os.environ)
spawn_env.update(self.env)
if ADD_LIBEXEC_PATH:
spawn_env['PATH'] = "@@LIBEXECDIR@@:" + spawn_env['PATH']
spawn_env['PATH'] = LIBEXECPATH + ':' + spawn_env['PATH']
self.process = subprocess.Popen(self.args,
stdin=subprocess.PIPE,
stdout=spawn_stdout,
......@@ -836,6 +834,7 @@ class BoB:
identified by the token back over the unix_socket.
"""
try:
token = str(token, 'ASCII') # Convert from bytes to str
fd = self._socket_cache.get_socket(token, unix_socket.fileno())
# FIXME: These two calls are blocking in their nature. An OS-level
# buffer is likely to be large enough to hold all these data, but
......@@ -914,7 +913,7 @@ class BoB:
Accept a socket from the unix domain socket server and put it to the
others we care about.
"""
socket = self._srv_socket.accept()
(socket, conn) = self._srv_socket.accept()
self._unix_sockets[socket.fileno()] = (socket, b'')
def _socket_data(self, socket_fileno):
......
......@@ -146,7 +146,7 @@ class TestCacheCommands(unittest.TestCase):
socket.
"""
def __init__(self):
self.send = ""
self.send = b""
def fileno(self):
"""
The file number. Used for identifying the remote application.
......@@ -207,17 +207,17 @@ class TestCacheCommands(unittest.TestCase):
socket = self.FalseSocket()
# An exception from the cache
self.__raise_exception = ValueError("Test value error")
self.__boss.socket_request_handler("token", socket)
self.__boss.socket_request_handler(b"token", socket)
# It was called, but it threw, so it is not noted here
self.assertIsNone(self.__get_socket_called)
self.assertEqual("0\n", socket.send)
self.assertEqual(b"0\n", socket.send)
# It should not have sent any socket.
self.assertIsNone(self.__send_fd_called)
# Now prepare a valid scenario
self.__raise_exception = None
socket.send = ""
self.__boss.socket_request_handler("token", socket)
self.assertEqual("1\n", socket.send)
socket.send = b""
self.__boss.socket_request_handler(b"token", socket)
self.assertEqual(b"1\n", socket.send)
self.assertEqual((42, 13), self.__send_fd_called)
self.assertEqual(("token", 42), self.__get_socket_called)
......@@ -1235,7 +1235,7 @@ class SocketSrvTest(unittest.TestCase):
return self.__fileno
def accept(self):
return self.__class__(self.__owner, 13)
return (self.__class__(self.__owner, 13), "/path/to/socket")
def recv(self, bufsize, flags=0):
self.__owner.assertEqual(1, bufsize)
......
......@@ -41,6 +41,8 @@
#include <cc/data.h>
#include <config/ccsession.h>
#include <server_common/socket_request.h>
#include <xfr/xfrout_client.h>
#include <auth/change_user.h>
......@@ -206,6 +208,7 @@ main(int argc, char* argv[]) {
LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_SERVICE_CREATED);
cc_session = new Session(io_service.get_io_service());
isc::server_common::initSocketReqeustor(*cc_session);
config_session = new ModuleCCSession(specfile, *cc_session,
my_config_handler,
my_command_handler);
......
......@@ -49,6 +49,7 @@
#include <dns/tests/unittest_util.h>
#include <testutils/srv_test.h>
#include <testutils/portconfig.h>
#include <testutils/socket_request.h>
using namespace std;
using boost::scoped_ptr;
......@@ -63,7 +64,8 @@ using isc::UnitTestUtil;
namespace {
const char* const TEST_ADDRESS = "127.0.0.1";
const char* const TEST_PORT = "53530";
const char* const TEST_ADDRESS_FAIL = "192.0.2.2";
const char* const TEST_PORT = "53210";
// An internal exception class
class TestConfigError : public isc::Exception {
......@@ -81,7 +83,10 @@ protected:
scoped_ptr<const IOMessage> query_message;
scoped_ptr<const Client> client;
scoped_ptr<const RequestContext> request;
ResolverConfig() : dnss(ios, NULL, NULL, NULL) {
ResolverConfig() :
dnss(ios, NULL, NULL, NULL),
sock_requestor_(dnss, address_store_, 53210)
{
server.setDNSService(dnss);
}
const RequestContext& createRequest(const string& source_addr) {
......@@ -96,6 +101,8 @@ protected:
return (*request);
}
void invalidTest(const string &JSON, const string& name);
isc::server_common::portconfig::AddressList address_store_;
isc::testutils::TestSocketRequestor sock_requestor_;
};
TEST_F(ResolverConfig, forwardAddresses) {
......@@ -248,7 +255,7 @@ TEST_F(ResolverConfig, listenOnConfigFail) {
"\"listen_on\": ["
" {"
" \"address\": \"" +
string(TEST_ADDRESS) + "\","
string(TEST_ADDRESS_FAIL) + "\","
" \"port\": " +
string(TEST_PORT) + "}]}"));
configAnswerCheck(server.updateConfig(config), false);
......@@ -264,7 +271,7 @@ TEST_F(ResolverConfig, listenOnAndOtherConfig) {
" {\"address\": \"192.0.2.1\","
" \"port\": 53}], "
"\"listen_on\": ["
" {\"address\": \"" + string(TEST_ADDRESS) + "\","
" {\"address\": \"" + string(TEST_ADDRESS_FAIL) + "\","
" \"port\": " + string(TEST_PORT) + "}]}");
// Normally, if listen_on fails the rest of the config parameters will
// be ignored.
......@@ -310,6 +317,20 @@ TEST_F(ResolverConfig, invalidForwardAddresses) {
// Try setting the addresses directly
TEST_F(ResolverConfig, listenAddresses) {
isc::testutils::portconfig::listenAddresses(server);
// Check it requests the correct addresses
const char* tokens[] = {
"TCP:127.0.0.1:53210:1",
"UDP:127.0.0.1:53210:2",
"TCP:::1:53210:3",
"UDP:::1:53210:4",
NULL
};
sock_requestor_.checkTokens(tokens, sock_requestor_.given_tokens_,
"Given tokens");
// It returns back to empty set of addresses afterwards, so
// they should be released
sock_requestor_.checkTokens(tokens, sock_requestor_.released_tokens_,
"Released tokens");
}
// Try setting some addresses and a rollback
......
......@@ -35,6 +35,14 @@ get_sock(const int type, struct sockaddr *bind_addr, const socklen_t addr_len)
if (sock == -1) {
return -1;
}
const int on(1);
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
return -2; // This is part of the binding process, so it's a bind error
}
if (bind_addr->sa_family == AF_INET6 &&
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) {
return -2; // This is part of the binding process, so it's a bind error
}
if (bind(sock, bind_addr, addr_len) == -1) {
return -2;
}
......@@ -62,7 +70,7 @@ get_sock(const int type, struct sockaddr *bind_addr, const socklen_t addr_len)
int
run(const int input_fd, const int output_fd, const get_sock_t get_sock,
const send_fd_t send_fd)
const send_fd_t send_fd_fun, const close_t close_fun)
{
for (;;) {
// Read the command
......@@ -122,8 +130,17 @@ run(const int input_fd, const int output_fd, const get_sock_t get_sock,
int result(get_sock(sock_type, addr, addr_len));
if (result >= 0) { // We got the socket
WRITE("S", 1);
// FIXME: Check the output and write a test for it
send_fd(output_fd, result);
if (send_fd_fun(output_fd, result) != 0) {
// We'll soon abort ourselves, but make sure we still
// close the socket; don't bother if it fails as the
// higher level result (abort) is the same.
close_fun(result);
return 3;
}
// Don't leak the socket
if (close_fun(result) == -1) {
return 4;
}
} else {
WRITE("E", 1);
switch (result) {
......
......@@ -27,6 +27,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
namespace isc {
namespace socket_creator {
......@@ -62,6 +63,11 @@ typedef
int
(*send_fd_t)(const int, const int);
/// \brief Type of the close() function, so it can be passed as a parameter.
typedef
int
(*close_t)(int);
/**
* \short Infinite loop parsing commands and returning the sockets.
*
......@@ -88,11 +94,14 @@ int
* \param send_fd_fun The function that is used to send the socket over
* a file descriptor. This should be left on the default value, it is
* here for testing purposes.
* \param close_fun The close function used to close sockets, coming from
* unistd.h. It can be overriden in tests.
*/
int
run(const int input_fd, const int output_fd,
const get_sock_t get_sock_fun = get_sock,
const send_fd_t send_fd_fun = isc::util::io::send_fd);
const send_fd_t send_fd_fun = isc::util::io::send_fd,
const close_t close_fun = close);
} // End of the namespaces
}
......
......@@ -59,6 +59,15 @@ namespace {
#SOCK_TYPE " and family " #ADDR_FAMILY ", failed with " \
<< socket << " and error " << strerror(errno); \
CHECK_SOCK(ADDR_TYPE, socket); \
int on; \
socklen_t len(sizeof(on)); \
EXPECT_EQ(0, getsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &on, &len));\
EXPECT_NE(0, on); \
if (ADDR_FAMILY == AF_INET6) { \
EXPECT_EQ(0, getsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, &on, \
&len)); \
EXPECT_NE(0, on); \
} \
EXPECT_EQ(0, close(socket)); \
} while (0)
......@@ -190,6 +199,12 @@ send_fd_dummy(const int destination, const int what)
}
}
// Just ignore the fd and pretend success. We close invalid fds in the tests.
int
closeIgnore(int) {
return (0);
}
/*
* Generic test that it works, with various inputs and outputs.
* It uses different functions to create the socket and send it and pass
......@@ -198,7 +213,8 @@ send_fd_dummy(const int destination, const int what)
*/
void run_test(const char *input_data, const size_t input_size,
const char *output_data, const size_t output_size,
bool should_succeed = true)
bool should_succeed = true, const close_t test_close = closeIgnore,
const send_fd_t send_fd = send_fd_dummy)
{
// Prepare the input feeder and output checker processes
int input_fd(0), output_fd(0);
......@@ -207,7 +223,7 @@ void run_test(const char *input_data, const size_t input_size,
ASSERT_NE(-1, input) << "Couldn't start input feeder";
ASSERT_NE(-1, output) << "Couldn't start output checker";
// Run the body
int result(run(input_fd, output_fd, get_sock_dummy, send_fd_dummy));
int result(run(input_fd, output_fd, get_sock_dummy, send_fd, test_close));
// Close the pipes
close(input_fd);
close(output_fd);
......@@ -270,4 +286,25 @@ TEST(run, bad_sockets) {
result, result_len);
}
// A close that fails
int
closeFail(int) {
return (-1);
}
TEST(run, cant_close) {
run_test("SU4\xff\xff\0\0\0\0", // This has 9 bytes
9, "S\x07", 2, false, closeFail);
}
int
sendFDFail(const int, const int) {
return (FD_SYSTEM_ERROR);
}
TEST(run, cant_send_fd) {
run_test("SU4\xff\xff\0\0\0\0", // This has 9 bytes
9, "S", 1, false, closeIgnore, sendFDFail);
}
}
......@@ -25,6 +25,7 @@ libasiodns_la_SOURCES += dns_service.cc dns_service.h
libasiodns_la_SOURCES += tcp_server.cc tcp_server.h
libasiodns_la_SOURCES += udp_server.cc udp_server.h
libasiodns_la_SOURCES += io_fetch.cc io_fetch.h
libasiodns_la_SOURCES += logger.h logger.cc
nodist_libasiodns_la_SOURCES = asiodns_messages.cc asiodns_messages.h
......
......@@ -14,6 +14,14 @@
$NAMESPACE isc::asiodns
% ASIODNS_FD_ADD_TCP adding a new TCP server by opened fd %1
A debug message informing about installing a file descriptor as a server.
The file descriptor number is noted.
% ASIODNS_FD_ADD_UDP adding a new UDP server by opened fd %1
A debug message informing about installing a file descriptor as a server.
The file descriptor number is noted.
% ASIODNS_FETCH_COMPLETED upstream fetch to %1(%2) has now completed
A debug message, this records that the upstream fetch (a query made by the
resolver on behalf of its client) to the specified address has completed.
......
......@@ -78,6 +78,13 @@ public:
DNSLookup *lookup_;
DNSAnswer *answer_;
template<class Ptr, class Server> void addServerFromFD(int fd, int af) {
Ptr server(new Server(io_service_.get_io_service(), fd, af, checkin_,
lookup_, answer_));
(*server)();
servers_.push_back(server);
}
void addServer(uint16_t port, const asio::ip::address& address) {
try {
dlog(std::string("Initialize TCP server at ") + address.to_string() + ":" + boost::lexical_cast<std::string>(port));
......@@ -189,6 +196,14 @@ DNSService::addServer(uint16_t port, const std::string& address) {
impl_->addServer(port, convertAddr(address));
}
void DNSService::addServerTCPFromFD(int fd, int af) {
impl_->addServerFromFD<DNSServiceImpl::TCPServerPtr, TCPServer>(fd, af);
}
void DNSService::addServerUDPFromFD(int fd, int af) {
impl_->addServerFromFD<DNSServiceImpl::UDPServerPtr, UDPServer>(fd, af);
}
void
DNSService::clearServers() {
BOOST_FOREACH(const DNSServiceImpl::DNSServerPtr& s, impl_->servers_) {
......
......@@ -88,6 +88,42 @@ public:
/// \brief Add another server to the service
void addServer(uint16_t port, const std::string &address);
void addServer(const char &port, const std::string &address);
/// \brief Add another TCP server/listener to the service from already
/// opened file descriptor
///
/// Adds a new TCP server using an already opened file descriptor (eg. it
/// only wraps it so the file descriptor is usable within the event loop).
/// The file descriptor must be associated with a TCP socket of the given
/// address family that is bound to an appropriate port (and possibly a
/// specific address) and is ready for listening to new connection
/// requests but has not actually started listening.
///
/// \param fd the file descriptor to be used.
/// \param af the address family of the file descriptor. Must be either
/// AF_INET or AF_INET6.
/// \throw isc::InvalidParameter if af is neither AF_INET nor AF_INET6.
/// \throw isc::asiolink::IOError when a low-level error happens, like the
/// fd is not a valid descriptor or it can't be listened on.
void addServerTCPFromFD(int fd, int af);
/// \brief Add another UDP server to the service from already opened
/// file descriptor
///
/// Adds a new UDP server using an already opened file descriptor (eg. it
/// only wraps it so the file descriptor is usable within the event loop).
/// The file descriptor must be associated with a UDP socket of the given
/// address family that is bound to an appropriate port (and possibly a
/// specific address).
///
/// \param fd the file descriptor to be used.
/// \param af the address family of the file descriptor. Must be either
/// AF_INET or AF_INET6.
/// \throw isc::InvalidParameter if af is neither AF_INET nor AF_INET6.
/// \throw isc::asiolink::IOError when a low-level error happens, like the
/// fd is not a valid descriptor or it can't be listened on.
void addServerUDPFromFD(int fd, int af);
/// \brief Remove all servers from the service
void clearServers();
......
......@@ -38,15 +38,13 @@
#include <dns/messagerenderer.h>
#include <dns/opcode.h>
#include <dns/rcode.h>
#include <log/logger.h>
#include <log/macros.h>
#include <asiodns/asiodns_messages.h>
#include <asiodns/io_fetch.h>
#include <util/buffer.h>
#include <util/random/qid_gen.h>
#include <asiodns/logger.h>
using namespace asio;
using namespace isc::asiolink;
......@@ -59,10 +57,6 @@ using namespace std;
namespace isc {
namespace asiodns {
/// Use the ASIO logger
isc::log::Logger logger("asiolink");
// Log debug verbosity
const int DBG_IMPORTANT = DBGLVL_TRACE_BASIC;
......
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <asiodns/logger.h>
namespace isc {
namespace asiodns {
/// Use the ASIO logger
isc::log::Logger logger("asiodns");
}
}
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <log/logger.h>
#include <log/macros.h>
#include <log/log_dbglevels.h>
#include <asiodns/asiodns_messages.h>
namespace isc {
namespace asiodns {
extern isc::log::Logger logger;
}
}
......@@ -29,8 +29,8 @@
#include <asiolink/dummy_io_cb.h>
#include <asiolink/tcp_endpoint.h>
#include <asiolink/tcp_socket.h>
#include <tcp_server.h>
#include <asiodns/tcp_server.h>
#include <asiodns/logger.h>