Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ISC Open Source Projects
Kea
Commits
920ba906
Commit
920ba906
authored
Jan 07, 2017
by
Marcin Siodelski
Browse files
[master] Merge branch 'trac5094'
parents
715d18f9
734aee45
Changes
10
Hide whitespace changes
Inline
Side-by-side
src/bin/dhcp4/ctrl_dhcp4_srv.h
View file @
920ba906
...
...
@@ -7,6 +7,7 @@
#ifndef CTRL_DHCPV4_SRV_H
#define CTRL_DHCPV4_SRV_H
#include
<asiolink/asio_wrapper.h>
#include
<asiolink/asiolink.h>
#include
<cc/data.h>
#include
<cc/command_interpreter.h>
...
...
src/bin/dhcp6/ctrl_dhcp6_srv.h
View file @
920ba906
...
...
@@ -7,6 +7,7 @@
#ifndef CTRL_DHCPV6_SRV_H
#define CTRL_DHCPV6_SRV_H
#include
<asiolink/asio_wrapper.h>
#include
<asiolink/asiolink.h>
#include
<cc/data.h>
#include
<cc/command_interpreter.h>
...
...
src/bin/dhcp6/kea_controller.cc
View file @
920ba906
...
...
@@ -6,6 +6,7 @@
#include
<config.h>
#include
<asiolink/asio_wrapper.h>
#include
<asiolink/asiolink.h>
#include
<dhcpsrv/cfgmgr.h>
#include
<dhcpsrv/parsers/dhcp_config_parser.h>
...
...
src/lib/asiolink/Makefile.am
View file @
920ba906
...
...
@@ -27,6 +27,7 @@ libkea_asiolink_la_SOURCES += io_endpoint.cc io_endpoint.h
libkea_asiolink_la_SOURCES
+=
io_error.h
libkea_asiolink_la_SOURCES
+=
io_service.h io_service.cc
libkea_asiolink_la_SOURCES
+=
io_socket.h io_socket.cc
libkea_asiolink_la_SOURCES
+=
tcp_acceptor.h
libkea_asiolink_la_SOURCES
+=
tcp_endpoint.h
libkea_asiolink_la_SOURCES
+=
tcp_socket.h
libkea_asiolink_la_SOURCES
+=
udp_endpoint.h
...
...
src/lib/asiolink/io_asio_socket.h
View file @
920ba906
...
...
@@ -78,6 +78,7 @@ class IOEndpoint;
template
<
typename
C
>
class
IOAsioSocket
:
public
IOSocket
{
///
/// \name Constructors and Destructor
///
...
...
src/lib/asiolink/io_socket.h
View file @
920ba906
// Copyright (C) 2010-201
5
Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2010-201
7
Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
...
...
@@ -35,6 +35,16 @@ namespace asiolink {
/// derived class for testing purposes rather than providing factory methods
/// (i.e., getDummy variants below).
class
IOSocket
{
public:
/// @name Types of objects encapsulating socket options.
//@{
/// @brief Represents SO_REUSEADDR socket option.
typedef
boost
::
asio
::
socket_base
::
reuse_address
ReuseAddress
;
//@}
///
/// \name Constructors and Destructor
///
...
...
src/lib/asiolink/tcp_acceptor.h
0 → 100644
View file @
920ba906
// Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef TCP_ACCEPTOR_H
#define TCP_ACCEPTOR_H
#ifndef BOOST_ASIO_HPP
#error "asio.hpp must be included before including this, see asiolink.h as to why"
#endif
#include
<asiolink/io_service.h>
#include
<asiolink/io_socket.h>
#include
<asiolink/tcp_endpoint.h>
#include
<asiolink/tcp_socket.h>
#include
<boost/shared_ptr.hpp>
#include
<netinet/in.h>
namespace
isc
{
namespace
asiolink
{
/// @brief Provides a service for accepting new TCP connections.
///
/// Internally it uses @c boost::asio::ip::tcp::acceptor class to implement
/// the acceptor service.
///
/// @tparam C Acceptor callback type.
template
<
typename
C
>
class
TCPAcceptor
:
public
IOSocket
{
public:
/// @brief Constructor.
///
/// @param io_service IO service.
explicit
TCPAcceptor
(
IOService
&
io_service
)
:
IOSocket
(),
acceptor_
(
new
boost
::
asio
::
ip
::
tcp
::
acceptor
(
io_service
.
get_io_service
()))
{
}
/// @brief Destructor.
virtual
~
TCPAcceptor
()
{
}
/// @brief Returns file descriptor of the underlying socket.
virtual
int
getNative
()
const
final
{
return
(
acceptor_
->
native
());
}
/// @brief Returns protocol of the socket.
///
/// @return IPPROTO_TCP.
virtual
int
getProtocol
()
const
final
{
return
(
IPPROTO_TCP
);
}
/// @brief Opens acceptor socket given the endpoint.
///
/// @param endpoint Reference to the endpoint object which specifies the
/// address and port on which the acceptor service will run.
void
open
(
const
TCPEndpoint
&
endpoint
)
{
acceptor_
->
open
(
endpoint
.
getASIOEndpoint
().
protocol
());
}
/// @brief Sets socket option.
///
/// Typically, this method is used to set SO_REUSEADDR option on the socket:
/// @code
/// IOService io_service;
/// TCPAcceptor<Callback> acceptor(io_service);
/// acceptor.setOption(TCPAcceptor::ReuseAddress(true))
/// @endcode
///
/// @param socket_option Reference to the object encapsulating an option to
/// be set for the socket.
/// @tparam SettableSocketOption Type of the object encapsulating socket option
/// being set.
template
<
typename
SettableSocketOption
>
void
setOption
(
const
SettableSocketOption
&
socket_option
)
{
acceptor_
->
set_option
(
socket_option
);
}
/// @brief Binds socket to an endpoint.
///
/// @param endpoint Reference to an endpoint to which the socket is to
/// be bound.
void
bind
(
const
TCPEndpoint
&
endpoint
)
{
acceptor_
->
bind
(
endpoint
.
getASIOEndpoint
());
}
/// @brief Starts listening for the new connections.
void
listen
()
{
acceptor_
->
listen
();
}
/// @brief Asynchronously accept new connection.
///
/// This method accepts new connection into the specified socket. When the
/// new connection arrives or an error occurs the specified callback function
/// is invoked.
///
/// @param socket Socket into which connection should be accepted.
/// @param callback Callback function to be invoked when the new connection
/// arrives.
/// @tparam SocketCallback Type of the callback for the @ref TCPSocket.
template
<
typename
SocketCallback
>
void
asyncAccept
(
const
TCPSocket
<
SocketCallback
>&
socket
,
C
&
callback
)
{
acceptor_
->
async_accept
(
socket
.
getASIOSocket
(),
callback
);
}
/// @brief Checks if the acceptor is open.
///
/// @return true if acceptor is open.
bool
isOpen
()
const
{
return
(
acceptor_
->
is_open
());
}
/// @brief Closes the acceptor.
void
close
()
const
{
acceptor_
->
close
();
}
private:
/// @brief Underlying ASIO acceptor implementation.
boost
::
shared_ptr
<
boost
::
asio
::
ip
::
tcp
::
acceptor
>
acceptor_
;
};
}
// namespace asiolink
}
// namespace isc
#endif
src/lib/asiolink/tcp_socket.h
View file @
920ba906
// Copyright (C) 2011-201
5
Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011-201
6
Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
...
...
@@ -153,6 +153,12 @@ public:
/// \brief Close socket
virtual
void
close
();
/// \brief Returns reference to the underlying ASIO socket.
///
/// \return Reference to underlying ASIO socket.
virtual
boost
::
asio
::
ip
::
tcp
::
socket
&
getASIOSocket
()
const
{
return
(
socket_
);
}
private:
// Two variables to hold the socket - a socket and a pointer to it. This
...
...
src/lib/asiolink/tests/Makefile.am
View file @
920ba906
...
...
@@ -27,6 +27,7 @@ run_unittests_SOURCES += udp_endpoint_unittest.cc
run_unittests_SOURCES
+=
udp_socket_unittest.cc
run_unittests_SOURCES
+=
io_service_unittest.cc
run_unittests_SOURCES
+=
dummy_io_callback_unittest.cc
run_unittests_SOURCES
+=
tcp_acceptor_unittest.cc
run_unittests_CPPFLAGS
=
$(AM_CPPFLAGS)
$(GTEST_INCLUDES)
...
...
src/lib/asiolink/tests/tcp_acceptor_unittest.cc
0 → 100644
View file @
920ba906
// Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include
<config.h>
#include
<asiolink/asio_wrapper.h>
#include
<asiolink/interval_timer.h>
#include
<asiolink/io_address.h>
#include
<asiolink/io_service.h>
#include
<asiolink/tcp_acceptor.h>
#include
<asiolink/tcp_endpoint.h>
#include
<boost/bind.hpp>
#include
<boost/function.hpp>
#include
<boost/noncopyable.hpp>
#include
<boost/shared_ptr.hpp>
#include
<gtest/gtest.h>
#include
<list>
#include
<netinet/in.h>
#include
<string>
using
namespace
isc
::
asiolink
;
namespace
{
/// @brief Local server address used for testing.
const
char
SERVER_ADDRESS
[]
=
"127.0.0.1"
;
/// @brief Local server port used for testing.
const
unsigned
short
SERVER_PORT
=
18123
;
/// @brief Test timeout in ms.
const
long
TEST_TIMEOUT
=
10000
;
/// @brief Simple class representing TCP socket callback.
class
SocketCallback
{
public:
/// @brief Implements callback for the asynchronous operation on the socket.
///
/// This callback merely checks if error has occurred and reports this
/// error. It does nothing in case of success.
///
/// @param ec Error code.
/// @param length Length of received data.
void
operator
()(
boost
::
system
::
error_code
ec
,
size_t
length
=
0
)
{
if
(
ec
)
{
ADD_FAILURE
()
<<
"error occurred for a socket: "
<<
ec
.
message
();
}
}
};
/// @brief Entity which can connect to the TCP server endpoint and close the
/// connection.
class
TCPClient
:
public
boost
::
noncopyable
{
public:
/// @brief Constructor.
///
/// This constructor creates new socket instance. It doesn't connect. Call
/// connect() to connect to the server.
///
/// @param io_service IO service to be stopped on error.
explicit
TCPClient
(
IOService
&
io_service
)
:
io_service_
(
io_service
.
get_io_service
()),
socket_
(
io_service_
)
{
}
/// @brief Destructor.
///
/// Closes the underlying socket if it is open.
~
TCPClient
()
{
close
();
}
/// @brief Connect to the test server address and port.
///
/// This method asynchronously connects to the server endpoint and uses the
/// connectHandler as a callback function.
void
connect
()
{
boost
::
asio
::
ip
::
tcp
::
endpoint
endpoint
(
boost
::
asio
::
ip
::
address
::
from_string
(
SERVER_ADDRESS
),
SERVER_PORT
);
socket_
.
async_connect
(
endpoint
,
boost
::
bind
(
&
TCPClient
::
connectHandler
,
this
,
_1
));
}
/// @brief Callback function for connect().
///
/// This function stops the IO service upon error.
///
/// @param ec Error code.
void
connectHandler
(
const
boost
::
system
::
error_code
&
ec
)
{
if
(
ec
)
{
ADD_FAILURE
()
<<
"error occurred while connecting: "
<<
ec
.
message
();
io_service_
.
stop
();
}
}
/// @brief Close connection.
void
close
()
{
socket_
.
close
();
}
private:
/// @brief Holds reference to the IO service.
boost
::
asio
::
io_service
&
io_service_
;
/// @brief A socket used for the connection.
boost
::
asio
::
ip
::
tcp
::
socket
socket_
;
};
/// @brief Pointer to the TCPClient.
typedef
boost
::
shared_ptr
<
TCPClient
>
TCPClientPtr
;
/// @brief A signature of the function implementing callback for the
/// TCPAcceptor.
typedef
boost
::
function
<
void
(
const
boost
::
system
::
error_code
&
)
>
TCPAcceptorCallback
;
/// @brief TCPAcceptor using TCPAcceptorCallback.
typedef
TCPAcceptor
<
TCPAcceptorCallback
>
TestTCPAcceptor
;
/// @brief Implements asynchronous TCP acceptor service.
///
/// It creates a new socket into which connection is accepted. The socket
/// is retained until class instance exists.
class
Acceptor
{
public:
/// @brief Constructor.
///
/// @param io_service IO service.
/// @param acceptor Reference to the TCP acceptor on which asyncAccept
/// will be called.
/// @param callback Callback function for the asyncAccept.
explicit
Acceptor
(
IOService
&
io_service
,
TestTCPAcceptor
&
acceptor
,
const
TCPAcceptorCallback
&
callback
)
:
socket_
(
io_service
),
acceptor_
(
acceptor
),
callback_
(
callback
)
{
}
/// @brief Destructor.
///
/// Closes socket.
~
Acceptor
()
{
socket_
.
close
();
}
/// @brief Asynchronous accept new connection.
void
accept
()
{
acceptor_
.
asyncAccept
(
socket_
,
callback_
);
}
/// @brief Close connection.
void
close
()
{
socket_
.
close
();
}
private:
/// @brief Socket into which connection is accepted.
TCPSocket
<
SocketCallback
>
socket_
;
/// @brief Reference to the TCPAcceptor on which asyncAccept is called.
TestTCPAcceptor
&
acceptor_
;
/// @brief Instance of the callback used for asyncAccept.
TCPAcceptorCallback
callback_
;
};
/// @brief Pointer to the Acceptor object.
typedef
boost
::
shared_ptr
<
Acceptor
>
AcceptorPtr
;
/// @brief Test fixture class for TCPAcceptor.
///
/// This class provides means for creating new TCP connections, i.e. simulates
/// clients connecting to the servers via TCPAcceptor. It is possible to create
/// multiple simultaneous connections, which are retained by the test fixture
/// class and closed cleanly when the test fixture is destroyed.
class
TCPAcceptorTest
:
public
::
testing
::
Test
{
public:
/// @brief Constructor.
///
/// Besides initializing class members it also sets the test timer to guard
/// against endlessly running IO service when TCP connections are
/// unsuccessful.
TCPAcceptorTest
()
:
io_service_
(),
acceptor_
(
io_service_
),
asio_endpoint_
(
boost
::
asio
::
ip
::
address
::
from_string
(
SERVER_ADDRESS
),
SERVER_PORT
),
endpoint_
(
asio_endpoint_
),
test_timer_
(
io_service_
),
connections_
(),
clients_
(),
connections_num_
(
0
),
aborted_connections_num_
(
0
),
max_connections_
(
1
)
{
test_timer_
.
setup
(
boost
::
bind
(
&
TCPAcceptorTest
::
timeoutHandler
,
this
),
TEST_TIMEOUT
,
IntervalTimer
::
ONE_SHOT
);
}
/// @brief Destructor.
virtual
~
TCPAcceptorTest
()
{
}
/// @brief Specifies how many new connections are expected before the IO
/// service is stopped.
///
/// @param max_connections Connections limit.
void
setMaxConnections
(
const
unsigned
int
max_connections
)
{
max_connections_
=
max_connections
;
}
/// @brief Create ASIO endpoint from the provided endpoint by retaining the
/// IP address and modifying the port.
///
/// This convenience method is useful to create new endpoint from the
/// existing endpoint to test reusing IP address for multiple acceptors.
/// The returned endpoint has the same IP address but different port.
///
/// @param endpoint Source endpoint.
///
/// @return New endpoint with the port number increased by 1.
boost
::
asio
::
ip
::
tcp
::
endpoint
createSiblingEndpoint
(
const
boost
::
asio
::
ip
::
tcp
::
endpoint
&
endpoint
)
const
{
boost
::
asio
::
ip
::
tcp
::
endpoint
endpoint_copy
(
endpoint
);
endpoint_copy
.
port
(
endpoint
.
port
()
+
1
);
return
(
endpoint_copy
);
}
/// @brief Starts accepting TCP connections.
///
/// This method creates new Acceptor instance and calls accept() to start
/// accepting new connections. The instance of the Acceptor object is
/// retained in the connections_ list.
void
accept
()
{
TCPAcceptorCallback
cb
=
boost
::
bind
(
&
TCPAcceptorTest
::
acceptHandler
,
this
,
_1
);
AcceptorPtr
conn
(
new
Acceptor
(
io_service_
,
acceptor_
,
cb
));
connections_
.
push_back
(
conn
);
connections_
.
back
()
->
accept
();
}
/// @brief Connect to the endpoint.
///
/// This method creates TCPClient instance and retains it in the clients_
/// list.
void
connect
()
{
TCPClientPtr
client
(
new
TCPClient
(
io_service_
));
clients_
.
push_back
(
client
);
clients_
.
back
()
->
connect
();
}
/// @brief Callback function for asynchronous accept calls.
///
/// It stops the IO service upon error or when the number of accepted
/// connections reaches the max_connections_ value. Otherwise it calls
/// accept() to start accepting next connections.
///
/// @param ec Error code.
void
acceptHandler
(
const
boost
::
system
::
error_code
&
ec
)
{
if
(
ec
)
{
if
(
ec
.
value
()
!=
boost
::
asio
::
error
::
operation_aborted
)
{
ADD_FAILURE
()
<<
"error occurred while accepting connection: "
<<
ec
.
message
();
}
else
{
++
aborted_connections_num_
;
}
io_service_
.
stop
();
}
// We have reached the maximum number of connections - end the test.
if
(
++
connections_num_
>=
max_connections_
)
{
io_service_
.
stop
();
return
;
}
accept
();
}
/// @brief Callback function invoke upon test timeout.
///
/// It stops the IO service and reports test timeout.
void
timeoutHandler
()
{
ADD_FAILURE
()
<<
"Timeout occurred while running the test!"
;
io_service_
.
stop
();
}
/// @brief IO service.
IOService
io_service_
;
/// @brief TCPAcceptor under test.
TestTCPAcceptor
acceptor_
;
/// @brief Server endpoint.
boost
::
asio
::
ip
::
tcp
::
endpoint
asio_endpoint_
;
/// @brief asiolink server endpoint (uses asio_endpoint_).
TCPEndpoint
endpoint_
;
/// @brief Asynchronous timer service to detect timeouts.
IntervalTimer
test_timer_
;
/// @brief List of connections on the server side.
std
::
list
<
AcceptorPtr
>
connections_
;
/// @brief List of client connections.
std
::
list
<
TCPClientPtr
>
clients_
;
/// @brief Current number of established connections.
unsigned
int
connections_num_
;
/// @brief Current number of aborted connections.
unsigned
int
aborted_connections_num_
;
/// @brief Connections limit.
unsigned
int
max_connections_
;
};
// Test TCPAcceptor::asyncAccept.
TEST_F
(
TCPAcceptorTest
,
asyncAccept
)
{
// Establish up to 10 connections.
setMaxConnections
(
10
);
// Initialize acceptor.
acceptor_
.
open
(
endpoint_
);
acceptor_
.
bind
(
endpoint_
);
acceptor_
.
listen
();
// Start accepting new connections.
accept
();
// Create 10 new TCP connections (client side).
for
(
unsigned
int
i
=
0
;
i
<
10
;
++
i
)
{
connect
();
}
// Run the IO service until we have accepted 10 connections, an error
// or test timeout occurred.
io_service_
.
run
();
// Make sure that all accepted connections have been recorded.
EXPECT_EQ
(
10
,
connections_num_
);
EXPECT_EQ
(
10
,
connections_
.
size
());
}
// Check that it is possible to set SO_REUSEADDR flag for the TCPAcceptor.
TEST_F
(
TCPAcceptorTest
,
reuseAddress
)
{
// We need at least two acceptors using common address. Let's create the
// second endpoint which has the same address but different port.
boost
::
asio
::
ip
::
tcp
::
endpoint
asio_endpoint2
(
createSiblingEndpoint
(
asio_endpoint_
));
TCPEndpoint
endpoint2
(
asio_endpoint2
);
// Create and open two acceptors.
TestTCPAcceptor
acceptor1
(
io_service_
);
TestTCPAcceptor
acceptor2
(
io_service_
);
ASSERT_NO_THROW
(
acceptor1
.
open
(
endpoint_
));
ASSERT_NO_THROW
(
acceptor2
.
open
(
endpoint2
));
// Set SO_REUSEADDR socket option so as acceptors can bind to the
/// same address.
ASSERT_NO_THROW
(
acceptor1
.
setOption
(
TestTCPAcceptor
::
ReuseAddress
(
true
))
);
ASSERT_NO_THROW
(
acceptor2
.
setOption
(
TestTCPAcceptor
::
ReuseAddress
(
true
))
);
ASSERT_NO_THROW
(
acceptor1
.
bind
(
endpoint_
));
ASSERT_NO_THROW
(
acceptor2
.
bind
(
endpoint2
));
// Create third acceptor, but don't set the SO_REUSEADDR. It should
// refuse to bind.
TCPEndpoint
endpoint3
(
createSiblingEndpoint
(
asio_endpoint2
));
TestTCPAcceptor
acceptor3
(
io_service_
);
ASSERT_NO_THROW
(
acceptor3
.
open
(
endpoint3
));
EXPECT_THROW
(
acceptor3
.
bind
(
endpoint_
),
boost
::
system
::
system_error
);
}
// Test that TCPAcceptor::getProtocol returns IPPROTO_TCP.
TEST_F
(
TCPAcceptorTest
,
getProtocol
)
{
EXPECT_EQ
(
IPPROTO_TCP
,
acceptor_
.
getProtocol
());
}
// Test that TCPAcceptor::getNative returns valid socket descriptor.
TEST_F
(
TCPAcceptorTest
,
getNative
)
{
// Initially the descriptor should be invalid (negative).