reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
TurnTlsSocket.cxx
Go to the documentation of this file.
1
2#if defined(HAVE_CONFIG_H)
3 #include "config.h"
4#endif
5
6#ifdef USE_SSL
7#include <functional>
8
9#include "TurnTlsSocket.hxx"
10#include "../AsyncTlsSocketBase.hxx"
11#include <openssl/ssl.h>
12#include <rutil/Logger.hxx>
13#include "../ReTurnSubsystem.hxx"
14
15#define RESIPROCATE_SUBSYSTEM ReTurnSubsystem::RETURN
16
17using namespace std;
18
19namespace reTurn {
20
21// The client's context, fully set up before mSocket is created from it: the SSL object takes the
22// context's verify mode when it's created, not later changes
23static asio::ssl::context
24makeClientContext(const std::string& rootCertificatesFile)
25{
26 asio::ssl::context context(asio::ssl::context::sslv23); // SSLv23 (actually chooses TLS version dynamically)
27
28
29
30 // Enable certificate validation
31 context.set_verify_mode(asio::ssl::context::verify_peer | // Verify the peer.
32 asio::ssl::context::verify_fail_if_no_peer_cert); // Fail verification if the peer has no certificate.
33
34 // File that should contain all required root certificates
35 asio::error_code ec;
36 context.load_verify_file(rootCertificatesFile, ec);
37 if (ec)
38 {
39 ErrLog(<< "Unable to load TLS root certificates from " << rootCertificatesFile << ", error=" << ec.value() << "(" << ec.message() << ")");
40 }
41 return context;
42}
43
44TurnTlsSocket::TurnTlsSocket(bool validateServerCertificateHostname,
45 const asio::ip::address& address,
46 unsigned short port,
47 const std::string& rootCertificatesFile) :
48 TurnTcpSocket(address,port),
49 mSslContext(makeClientContext(rootCertificatesFile)),
50 mSocket(new asio::ssl::stream<asio::ip::tcp::socket>(mIOService, mSslContext)),
51 mValidateServerCertificateHostname(validateServerCertificateHostname)
52{
54 // connect() opens the socket and binds it to address and port
55}
56
57asio::error_code
58TurnTlsSocket::connect(const std::string& address, unsigned short port)
59{
60 // Get a list of endpoints corresponding to the server name.
61 asio::ip::tcp::resolver resolver(mIOService);
62 resip::Data service(port);
63 asio::error_code errorCode;
64 asio::ip::tcp::resolver::results_type results = resolver.resolve(protocol(), address, service.c_str(), errorCode);
65 if(errorCode)
66 {
67 return errorCode;
68 }
69 asio::ip::tcp::resolver::results_type::const_iterator endpoint_iterator = results.begin();
70 asio::ip::tcp::resolver::results_type::const_iterator end = results.end();
71
72 // Try each endpoint until we successfully establish a connection
73 errorCode = asio::error::host_not_found;
74 while (errorCode && endpoint_iterator != end)
75 {
76 // A new SSL stream, on a new socket bound to the local binding: after a failed handshake
77 // the SSL object can't be used again, nor anything left in the stream's buffers
78 mSocket.reset(new asio::ssl::stream<asio::ip::tcp::socket>(mIOService, mSslContext));
79
80 // SNI, and the check that the server certificate is for address, made during the handshake
81 if (!AsyncTlsSocketBase::setupClientSsl(mSocket->native_handle(), address, mValidateServerCertificateHostname))
82 {
83 return asio::error::invalid_argument;
84 }
85
86 // The TCP connect and the handshake each fail after the connect timeout
87 const std::function<void()> closeSocket = [this]() { asio::error_code ignored; mSocket->lowest_layer().close(ignored); };
88 errorCode = openAndBind(mSocket->next_layer());
89 if(!errorCode)
90 {
91 asio::error_code result = asio::error::would_block;
92 mSocket->lowest_layer().async_connect(*endpoint_iterator, [&result](const asio::error_code& ec) { result = ec; });
93 errorCode = waitForConnectStep(result, closeSocket);
94 }
95 if(!errorCode)
96 {
97 DebugLog(<< "Connected!");
98 asio::error_code result = asio::error::would_block;
99 mSocket->async_handshake(asio::ssl::stream_base::client, [&result](const asio::error_code& ec) { result = ec; });
100 errorCode = waitForConnectStep(result, closeSocket);
101 AsyncTlsSocketBase::logClientHandshakeResult(mSocket->native_handle(), address, errorCode);
102 if(!errorCode)
103 {
104 mConnected = true;
106 mConnectedTuple.setAddress(endpoint_iterator->endpoint().address());
107 mConnectedTuple.setPort(endpoint_iterator->endpoint().port());
108 }
109 // As the async client: only a failed TCP connect moves on to the next address. A
110 // handshake that fails (eg. the certificate isn't for the name) would fail with the
111 // others too.
112 return errorCode;
113 }
114 ++endpoint_iterator;
115 }
116
117 return errorCode;
118}
119
120asio::error_code
121TurnTlsSocket::rawWrite(const char* buffer, unsigned int size)
122{
123 asio::error_code errorCode;
124 asio::write(*mSocket, asio::buffer(buffer, size), asio::transfer_all(), errorCode);
125 return errorCode;
126}
127
128asio::error_code
129TurnTlsSocket::rawWrite(const std::vector<asio::const_buffer>& buffers)
130{
131 asio::error_code errorCode;
132 asio::write(*mSocket, buffers, asio::transfer_all(), errorCode);
133 return errorCode;
134}
135
136void
138{
139 asio::async_read(*mSocket, asio::buffer(mReadBuffer, 4),
140 std::bind(&TurnTlsSocket::handleReadHeader, this, std::placeholders::_1));
141}
142
143void
144TurnTlsSocket::readBody(unsigned int len)
145{
146 asio::async_read(*mSocket, asio::buffer(&mReadBuffer[4], len),
147 std::bind(&TurnTlsSocket::handleRawRead, this, std::placeholders::_1, std::placeholders::_2));
148}
149
150void
152{
153 asio::error_code ec;
154 mSocket->lowest_layer().cancel(ec);
155}
156
157} // namespace
158#endif
159
160/* ====================================================================
161
162 Copyright (c) 2008-2026 SIP Spectrum, Inc. https://www.sipspectrum.com
163 Copyright (c) 2007-2008, Plantronics, Inc.
164 All rights reserved.
165
166 Redistribution and use in source and binary forms, with or without
167 modification, are permitted provided that the following conditions are
168 met:
169
170 1. Redistributions of source code must retain the above copyright
171 notice, this list of conditions and the following disclaimer.
172
173 2. Redistributions in binary form must reproduce the above copyright
174 notice, this list of conditions and the following disclaimer in the
175 documentation and/or other materials provided with the distribution.
176
177 3. Neither the name of Plantronics nor the names of its contributors
178 may be used to endorse or promote products derived from this
179 software without specific prior written permission.
180
181 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
182 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
183 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
184 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
185 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
186 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
187 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
188 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
189 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
190 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
191 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
192
193 ==================================================================== */
#define DebugLog(args_)
Definition Logger.hxx:128
#define ErrLog(args_)
Definition Logger.hxx:137
static bool setupClientSsl(SSL *ssl, const std::string &hostname, bool validateHostname)
Sets up a client SSL to connect to hostname (a host name or IP address): SNI for a host name,...
static void logClientHandshakeResult(SSL *ssl, const std::string &hostname, const asio::error_code &ec)
Logs a client handshake's outcome: the TLS version and cipher, or why the server certificate was reje...
void setPort(unsigned int port)
Definition StunTuple.hxx:48
void setAddress(const asio::ip::address &address)
Definition StunTuple.hxx:45
void setTransportType(TransportType transport)
Definition StunTuple.hxx:42
void handleRawRead(const asio::error_code &errorCode, size_t bytesRead)
asio::io_context mIOService
StunTuple mLocalBinding
char mReadBuffer[8192]
StunTuple mConnectedTuple
asio::error_code waitForConnectStep(asio::error_code &result, const std::function< void()> &cancel)
For a step of connect() started as an asynchronous operation that sets result (initially asio::error:...
virtual void handleReadHeader(const asio::error_code &e)
Handle completion of a read operation for first 32 bits.
asio::ip::tcp protocol() const
The protocol (IPv4 or IPv6) of the local binding.
asio::error_code openAndBind(asio::ip::tcp::socket &socket)
Opens socket and binds it to the local binding (address and port), for a connection attempt.
virtual void cancelSocket()
virtual asio::error_code rawWrite(const char *buffer, unsigned int size)
virtual void readBody(unsigned int len)
TurnTlsSocket(bool validateServerCertificateHostname, const asio::ip::address &address=UnspecifiedIpAddress, unsigned short port=0, const std::string &rootCertificatesFile="ca.pem")
validateServerCertificateHostname: check the server certificate is for the host name (or IP address) ...
virtual asio::error_code connect(const std::string &address, unsigned short port)
virtual void readHeader()
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
const char * c_str() const
Returns a null-terminated string representing.
Definition Data.cxx:1097
static asio::ssl::context makeClientContext(const std::string &rootCertificatesFile)