Using SSL sockets and non-SSL sockets simultaneously in Boost.Asio? - c++

I'm in the process of converting a library to Boost.Asio (which has worked very well so far), but I've hit something of a stumbling block with regards to a design decision.
Boost.Asio provides support for SSL, but a boost::asio::ssl::stream<boost::asio::ip::tcp::socket> type must be used for the socket. My library has the option of connecting to SSL servers or connecting normally, so I've made a class with two sockets like this:
class client : public boost::enable_shared_from_this<client>
{
public:
client(boost::asio::io_service & io_service, boost::asio::ssl::context & context) : socket_(io_service), secureSocket_(io_service, context) {}
private:
boost::asio::ip::tcp::socket socket_;
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> secureSocket_;
};
And within there are a bunch of handlers that reference socket_. (For example, I have socket_.is_open() in several places, which would need to become secureSocket_.lowest_layer().is_open() for the other socket.)
Can anyone suggest the best way to go about this? I'd rather not create a separate class just for this purpose, because that would mean duplicating a lot of code.
Edit: I rephrased my original question because I misunderstood the purpose of an OpenSSL function.

I'm rather late in answering this question, but I hope this will help others. Sam's answer contains the germ of an idea, but doesn't quit go far enough in my opinion.
The idea came about from the observation that asio wraps an SSL socket in a stream. All this solution does is that it wraps the non-SSL socket similarly.
The desired result of having a uniform external interface between SSL and non-SSL sockets is done with three classes. One, the base, effectively defines the interface:
class Socket {
public:
virtual boost::asio::ip::tcp::socket &getSocketForAsio() = 0;
static Socket* create(boost::asio::io_service& iIoService, boost::asio::ssl::context *ipSslContext) {
// Obviously this has to be in a separate source file since it makes reference to subclasses
if (ipSslContext == nullptr) {
return new NonSslSocket(iIoService);
}
return new SslSocket(iIoService, *ipSslContext);
}
size_t _read(void *ipData, size_t iLength) {
return boost::asio::read(getSocketForAsio(), boost::asio::buffer(ipData, iLength));
}
size_t _write(const void *ipData, size_t iLength) {
return boost::asio::write(getSocketForAsio(), boost::asio::buffer(ipData, iLength));
}
};
Two sub-classes wrap SSL and non-SSL sockets.
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SslSocket_t;
class SslSocket: public Socket, private SslSocket_t {
public:
SslSocket(boost::asio::io_service& iIoService, boost::asio::ssl::context &iSslContext) :
SslSocket_t(iIoService, iSslContext) {
}
private:
boost::asio::ip::tcp::socket &getSocketForAsio() {
return next_layer();
}
};
and
class NonSslSocket: public Socket, private Socket_t {
public:
NonSslSocket(boost::asio::io_service& iIoService) :
Socket_t(iIoService) {
}
private:
boost::asio::ip::tcp::socket &getSocketForAsio() {
return next_layer();
}
};
Every time you call an asio function use getSocketForAsio(), rather than pass a reference to the Socket object. For example:
boost::asio::async_read(pSocket->getSocketForAsio(),
boost::asio::buffer(&buffer, sizeof(buffer)),
boost::bind(&Connection::handleRead,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
Notice that the Socket is stored as pointer. I cannot think how else the polymorphism can be hidden.
The penalty (which I don't think great) is the extra level of indirection used to obtain non-SSL sockets.

There's a couple of ways you can do this. In the past, I've done something like
if ( sslEnabled )
boost::asio::async_write( secureSocket_ );
} else {
boost::asio::async_write( secureSocket_.lowest_layer() );
}
Which can get messy pretty quickly with a lot of if/else statements. You could also create an abstract class (pseudo code - oversimplified)
class Socket
{
public:
virtual void connect( ... );
virtual void accept( ... );
virtual void async_write( ... );
virtual void async_read( ... );
private:
boost::asio::ip::tcp::socket socket_;
};
Then create a derived class SecureSocket to operate on a secureSocket_ instead of socket_. I don't think it would be duplicating a lot of code, and it's probably cleaner than if/else whenever you need to async_read or async_write.

The problem of course is that tcp::socket and the ssl "socket" don't share the any common ancestor. But most functions for using the socket once it's open share the exact same syntax. The cleanest solution is thus with templates.
template <typename SocketType>
void doStuffWithOpenSocket(SocketType socket) {
boost::asio::write(socket, ...);
boost::asio::read(socket, ...);
boost::asio::read_until(socket, ...);
// etc...
}
This function will work work with normal tcp::sockets and also secure SSL sockets:
boost::asio::ip::tcp::socket socket_;
// socket_ opened normally ...
doStuffWithOpenSocket<boost::asio::ip::tcp::socket>(socket_); // works!
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> secureSocket_;
// secureSocket_ opened normally (including handshake) ...
doStuffWithOpenSocket(secureSocket_); // also works, with (different) implicit instantiation!
// shutdown the ssl socket when done ...

It would compile with something like this:
typedef boost::asio::buffered_stream<boost::asio::ip::tcp::socket> Socket_t;

Related

Get boost::asio::io_context from a boost::asio::ip::tcp::socket to exec a custom function [duplicate]

Some years ago, I wrote a email client using Boost asio library.
There are a abstract class ICON with four subclasses.
POP3conN to flat POP3 communications
POP3conS to secure POP3 communications
SMTPconN to flat SMTP communications
SMTPconS to secure SMTP communications
ICON has a member
boost::asio::ip::tcp::socket socket_
and two virtual procedures, defined in each subclass:
void SMTPconN::run() { socket_.get_io_service().run(); }
void SMTPconN::reset() { socket_.get_io_service().reset(); }
The application worked fine with boost_1_63_0. But when I try update to boost_1_70_0, the compiler (MS V Studio 2015) complains in both definitions:
class "boost::asio::ssl::stream<boost::asio::ip::tcp::socket>" has no member "get_io_service".
Because I want do the minimal change in what is a huge amount of code and complex logic: do is there some workaround to this missed method?
The docs state under Networking TS compatibility that you can use get_context().context(), which will get you a io_context instance (which replaced io_service somewhere around boost 1.64/1.65 IIRC).
Networking TS compatibility
Boost.Asio now provides the interfaces and functionality specified by the "C++ Extensions for Networking" Technical Specification. In addition to access via the usual Boost.Asio header files, this functionality may be accessed through special headers that correspond to the header files defined in the TS. These are listed in the table below:
[...]
Use get_executor().context() to obtain the associated io_context.
Both get_io_service() and get_io_context() were previously in place to facilitate porting, but they have in the mean time also been deprecated and obsoleted.
PS: Also see Get boost::asio::io_context from a boost::asio::ip::tcp::socket to exec a custom function which is eerily similar to your question but specifies a specific use-case.
The comments there have the decidedly better solution for that use-case:
socket.get_io_service().post([](){ /* my custom code */ } );
Becomes
post(socket.executor(), [](){ /* my custom code */ } );
The subclasses: POP3conN and SMTPconN have a member:
boost::asio::ip::tcp::socket socket_
Similarly, POP3conS and SMTPconS have a member:
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
The first argument of all constructors is a pointer to io_service. Some like:
IPCON::IPCON(boost::asio::io_service* ioserv_, ...) { ... }
POP3conN::POP3conN(boost::asio::io_service* ioserv_, ....) {...}
First change: in the abstract class IPCON has been added a new member:
boost::asio::io_context* iocontPtr_;
wich is initialized in the constructor replacing the old reference to io_service:
IPCON::IPCON(boost::asio::io_context* iocont_, ...) { ... }
In the constructors of the subclasses has been added initialization to such member:
POP3conN::POP3conN(boost::asio::io_context* iocont, ....) : IPCON(iocont) { ... }
Second change: all occurences of
boost::asio::io_service
Can be replaced by
boost::asio::io_context
The problematic expressions
void SMTPconN::run() { socket_.get_io_service().run(); }
void SMTPconN::reset() { socket_.get_io_service().reset(); }
appears now like this:
void SMTPconN::run() { iocontPtr->run(); }
void SMTPconN::reset() { iocontPtr->reset(); }
It seems that the functionality of the old io_service has been replaced by the new io_context.

C++ Design: Multiple TCP clients, boost asio and observers

In my system, I have a juggle a bunch of TCP clients and I am bit confused on how to design it [most of my experience is in C, hence the insecurity]. I am using boost ASIO for managing connection. These are the components I have
A TCPStream class : thin wrapper over boost asio
an IPC protocol, which implement a protocol over TCP:
basically Each message starts with a type and length field
so we can read the individual messages out of the stream.
Connection classes which handle the messages
Observer class which monitors connections
I am writing pseudo C++ code to be concise. I think you will get the idea
class TCPStream {
boost::asio::socket socket_;
public:
template <typename F>
void connect (F f)
{
socket_.connect(f);
}
template <typename F>
void read (F f)
{
socket_.read(f);
}
};
class IpcProtocol : public TCPStream {
public:
template <typename F
void read (F f)
{
TCPStream::read(
[f] (buffer, err) {
while (msg = read_indvidual_message(buffer)) {
// **** this is a violation of how this pattern is
// supposed to work. Ideally there should a callback
// for individual message. Here the same callback
// is called for N no. of messages. But in our case
// its the same callback everytime so this should be
// fine - just avoids some function calls.
f(msg);
};
};
)
}
};
Lets say I have a bunch of TCP connections and there are a handler class
for each of the connection. Lets name it Connection1, Connection2 ...
class Connection {
virtual int type() = 0;
};
class Connection1 : public Connection {
shared_ptr<IpcProtocol> ipc_;
int type ()
{
return 1;
}
void start ()
{
ipc_.connect([self = shared_from_this()](){ self->connected(); });
ipc_.read(
[self = shared_from_this()](msg, err) {
if (!err)
self->process(msg);
} else {
self->error();
}
});
}
void connected ()
{
observer.notify_connected(shared_from_this());
}
void error ()
{
observer.notify_error(shared_from_this());
}
};
This pattern repeats for all connections one way or other.
messages are processed by the connection class itself. But it will let know of
other events [connect, error] to an observer. The reason -
Restart the connection, everytime it disconnect
Bunch of guys needs to know if the connection is established so that they can
send initial request/confguration to server.
There are things that needs be done based on connection status of muliple connections
Eg: if connection1 and connection2 are established, then start connection3 etc.
I added a middle Observer class is there so that the observers do have to directly connect to the connection everytime it is restarted. Each time connection breaks, the connection class is deleted and new one is created.
class Listeners {
public:
virtual void notify_error(shared_ptr<Connection>) = 0;
virtual void notify_connect(shared_ptr<Connection>) = 0;
virtual void interested(int type) = 0;
};
class Observer {
std::vector<Listeners *> listeners_;
public:
void notify_connect(shared_ptr<Connection> connection)
{
for (listener : listeners_) {
if (listener->interested(connection->type())) {
listener->notify_error(connection);
}
}
}
};
Now a rough prototype of this works. But I was wondering if this class design
any good. There are multiple streaming servers which will continuously produce states and send it to my module to program the state in h/w. This needs to be extensible as more clients will be added in future.
Threading
The legacy code had one thread per TCP connection and this worked fine. Here I am trying to handle multiple connections on same thread. Still there will be multiple threads calling ioservice. So the observer will run on multiple threads. I am planning to have a mutex per Listener, so that listeners wont get multiple events concurrently.
HTTP Implements a protocol over TCP so the HTTP Server asio examples are a good starting point for your design, especially: HTTP Server 2, HTTP Server 3 and HTTP Server 4.
Note: that connection lifetime is likely to be an issue, especially since you intend to use class member functions as handlers, see the question and answers here: How to design proper release of a boost::asio socket or wrapper thereof.

Make a class with no default constructor a private property of another class

I am trying to make a class that manipulates with Boost sockets to make the conections simple to use.
My SocketClient class has a few properties with boost::asio::ip::tcp::socket being one of them. But I get C2512 error in my constructor, because boost::asio::ip::tcp::socket cannot exist unitialised, as it has no constructor.
Here, see the code of the class:
class SocketClient {
private:
int port; //Port, currently unused
boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver::iterator endpoint_iterator;
boost::asio::ip::tcp::socket sock; //This causes the error
//It wants to be like this (impossible too):
//boost::asio::ip::tcp::socket sock(io_service);
public:
void init(const char*, const char* );
SocketClient(); //Default constructor
bool connect();
bool read(int bytes, char *text);
bool send(int length, char *text);
bool send(std::string text);
unsigned int timeout;
};
And here is the constructor:
SocketClient::SocketClient() { //ERROR: (23): error C2512: 'boost::asio::basic_stream_socket<Protocol>' : no appropriate default constructor available
sock=boost::asio::ip::tcp::socket(io_service); //Adding this didn't help
}
So what to do? Do I have to keep sock as void*?
Use initialization lists:
SocketClient::SocketClient()
:
sock(io_service)
{
// Other initialization code here...
}
Be careful though: this is well-defined because member variables are constructed in the order they appear in the class definition, and io_service appears before sock. If that wasn't the case, you would pass an uninitialized object to the constructor of socket, most likely resulting in Undefined Behavior.
If you are using C++ 11, you can do it like this:
boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver::iterator endpoint_iterator;
boost::asio::ip::tcp::socket sock { io_service };
But (as Andy Prowl said) io_service must be must be placed before sock in the member list.
This would compile, but may lead to unpredictable errors:
boost::asio::ip::tcp::socket sock { io_service };
boost::asio::io_service io_service;
generally - as already stated - whenever possible use initialization lists. also use a naming scheme that identifies members.
also already stated - initialization depends on member order. if you have dependencies between members this is most often a sign for a design error. it smells. at least document this well in the code - there is always a next maintainer. of course you can alway hold elements via typed (smart) pointers.
for the concrete problem I'd suggest to pass the io_service via reference. this would give you more control over the io_service (eg. use async mode and run multiple sockets within one io_service)
#include <boost/asio.hpp>
class SocketClient {
private:
boost::asio::io_service& m_io_service;
boost::asio::ip::tcp::socket m_socket;
[...]
public:
SocketClient(boost::asio::io_service& io_service);
[...]
};
SocketClient::SocketClient(boost::asio::io_service& io_service)
: m_io_service(io_service)
, m_socket(io_service)
[...]
{
}
header .h
boost::asio::ip::tcp::socket *sock;
body .cpp
sock = new boost::asio::ip::tcp::socket(service);

Overloadable boost::asio::basic_stream_socket

Developing a network application, I have a Connection class that manages sending and receiving messages on the network. I'm using boost::asio.
I now want to let the Connection class handle connections both over TCP, and over local UNIX stream sockets. However, the template-design of boost confuses me. AFAICT, there's no shared base-class between local::stream_protocol::socket and ip::tcp::socket.
How would I go about creating a Connection that encapsulates the network-semantics such that other code don't have to deal with the details of what protocol is used?
I.E. I want to implemented something like:
class Connection() {
Connection(ip::tcp::endpoint& ep);
Connection(local::stream_protocol::endpoint& ep);
void send(Buffer& buf);
}
How would I achieve this?
After some pondering, my current solution is to make the send and recv functions of Connection virtual, and create a template-subclass of Connection, roughly:
template <typename Protocol>
class ConnectionImpl : public Connection {
typedef typename Protocol::socket Socket;
typedef typename Protocol::endpoint EndPoint;
Socket _socket;
public:
ConnectionImpl(boost::asio::io_service& ioSvc, const EndPoint& addr)
: Connection(ioSvc), _socket(ioSvc) {
_socket.connect(addr);
}
void trySend() {
// Initiate async send on _socket here
}
void tryRead() {
// Initiate async recv on _socket here
}
}
Is there a way to avoid the need to subclass and use of virtual functions?
AFAICT, there's no shared base-class between
local::stream_protocol::socket and ip::tcp::socket.
There is explicitly no base class for all socket objects on purpose, the documentation describes the rationale quite well
Unsafe and error prone aspects of the BSD socket API not included. For
example, the use of int to represent all sockets lacks type safety.
The socket representation in Boost.Asio uses a distinct type for each
protocol, e.g. for TCP one would use ip::tcp::socket, and for UDP one
uses ip::udp::socket
Use boost::asio:generic::stream_protocol::socket instead. When you call async_connect()/connect(), it will extract the family and protocol from the remote endpoint and then pass them to the socket() syscall to create the correct socket.
boost::asio::generic::stream_protocol::socket socket_{io_service};
if (use_unix_socket) {
boost::asio::local::stream_protocol::endpoint unix_endpoint{"/tmp/socketpath.sock"};
socket_.async_connect(unix_endpoint, [](boost::system::error_code ec){
}};
}
else {
boost::asio::ip::tcp::endpoint tcp_endpoint{...};
socket_.async_connect(tcp_endpoint, [](boost::system::error_code ec){
}};
}
And there is the code from boost::asio::basic_socket:
template <typename ConnectHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler,
void (boost::system::error_code))
async_connect(const endpoint_type& peer_endpoint,
BOOST_ASIO_MOVE_ARG(ConnectHandler) handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a ConnectHandler.
BOOST_ASIO_CONNECT_HANDLER_CHECK(ConnectHandler, handler) type_check;
if (!is_open())
{
boost::system::error_code ec;
const protocol_type protocol = peer_endpoint.protocol();
if (this->get_service().open(this->get_implementation(), protocol, ec))
{
detail::async_result_init<
ConnectHandler, void (boost::system::error_code)> init(
BOOST_ASIO_MOVE_CAST(ConnectHandler)(handler));
this->get_io_service().post(
boost::asio::detail::bind_handler(
BOOST_ASIO_MOVE_CAST(BOOST_ASIO_HANDLER_TYPE(
ConnectHandler, void (boost::system::error_code)))(
init.handler), ec));
return init.result.get();
}
}
return this->get_service().async_connect(this->get_implementation(),
peer_endpoint, BOOST_ASIO_MOVE_CAST(ConnectHandler)(handler));
}

boost::asio: How do I use async_accept to accept incoming connections?

I'm using boost::asio, and I have code like this:
void CServer::Start(int port)
{
tcp::acceptor acceptor(m_IoService, tcp::endpoint(tcp::v4(), port));
for ( ;; )
{
shared_ptr<tcp::socket> pSocket(new tcp::socket(m_IoService));
acceptor.accept(*pSocket);
HandleRequest(pSocket);
}
}
This code works, but I'd like to switch to using Acceptor::async_accept so that I can call Acceptor::cancel to stop receiving requests.
So my new code looks like this:
void CServer::StartAsync(int port)
{
m_pAcceptor = shared_ptr<tcp::acceptor>( new tcp::acceptor(m_IoService, tcp::endpoint(tcp::v4(), port)) );
StartAccept();
}
void CServer::StopAsync()
{
m_pAcceptor->cancel();
}
void CServer::StartAccept()
{
shared_ptr<tcp::socket> pSocket(new tcp::socket(m_IoService));
m_pAcceptor->async_accept(*pSocket, bind(&CServer::HandleAccept, this, pSocket));
}
void CServer::HandleAccept(shared_ptr<tcp::socket> pSocket)
{
HandleRequest(pSocket);
StartAccept();
}
But this code doesn't seem to work, my function CServer::HandleAccept never gets called. Any ideas? I've looked at sample code, and the main difference between my code and theirs is they seem often make a class like tcp_connection that has the socket as a member, and I'm not seeing why thats necessary.
Alex
Ah, looks like to kick things off you need to run the IOService, e.g.:
m_IoService.run();