reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
TurnTcpSocket.cxx
Go to the documentation of this file.
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
4
5#include "TurnTcpSocket.hxx"
6#include "../AsyncSocketBase.hxx"
7#include <rutil/Logger.hxx>
8#include "../ReTurnSubsystem.hxx"
9
10#define RESIPROCATE_SUBSYSTEM ReTurnSubsystem::RETURN
11
12using namespace std;
13
14namespace reTurn {
15
16TurnTcpSocket::TurnTcpSocket(const asio::ip::address& address, unsigned short port) :
17 TurnSocket(address,port),
18 mSocket(mIOService)
19{
21 // connect() opens the socket and binds it to address and port
22}
23
24asio::ip::tcp
26{
27 return mLocalBinding.getAddress().is_v6() ? asio::ip::tcp::v6() : asio::ip::tcp::v4();
28}
29
30asio::error_code
31TurnTcpSocket::openAndBind(asio::ip::tcp::socket& socket)
32{
33 asio::error_code errorCode;
34 socket.open(protocol(), errorCode);
35 if(!errorCode)
36 {
37 asio::error_code ignored;
38 socket.set_option(asio::ip::tcp::no_delay(true), ignored); // ?slg? do we want this?
39 socket.set_option(asio::ip::tcp::socket::reuse_address(true), ignored);
40 socket.bind(asio::ip::tcp::endpoint(mLocalBinding.getAddress(), mLocalBinding.getPort()), errorCode);
41 }
42 return errorCode;
43}
44
45asio::error_code
46TurnTcpSocket::waitForConnectStep(asio::error_code& result, const std::function<void()>& cancel)
47{
48 bool timedOut = false;
49 asio::steady_timer timer(mIOService);
50 timer.expires_after(std::chrono::milliseconds(mConnectTimeout));
51 timer.async_wait([&timedOut, &cancel](const asio::error_code& e)
52 {
53 if(!e)
54 {
55 timedOut = true;
56 cancel(); // the operation then completes, with operation_aborted
57 }
58 });
59
60 mIOService.restart();
61 while(result == asio::error::would_block && mIOService.run_one() > 0)
62 {
63 }
64 timer.cancel();
65 mIOService.run(); // lets the timer's handler finish
66 mIOService.restart();
67 return timedOut ? asio::error_code(asio::error::timed_out) : result;
68}
69
70asio::error_code
71TurnTcpSocket::connect(const std::string& address, unsigned short port)
72{
73 // Get a list of endpoints corresponding to the server name.
74 asio::ip::tcp::resolver resolver(mIOService);
75 resip::Data service(port);
76 asio::error_code errorCode;
77 asio::ip::tcp::resolver::results_type results = resolver.resolve(protocol(), address, service.c_str(), errorCode);
78 if(errorCode)
79 {
80 return errorCode;
81 }
82 asio::ip::tcp::resolver::results_type::const_iterator endpoint_iterator = results.begin();
83 asio::ip::tcp::resolver::results_type::const_iterator end = results.end();
84
85 // Try each endpoint until we successfully establish a connection, each on a new socket bound
86 // to the local binding
87 errorCode = asio::error::host_not_found;
88 while (errorCode && endpoint_iterator != end)
89 {
90 mSocket.close();
91 errorCode = openAndBind(mSocket);
92 if(!errorCode)
93 {
94 asio::error_code result = asio::error::would_block;
95 mSocket.async_connect(*endpoint_iterator, [&result](const asio::error_code& ec) { result = ec; });
96 errorCode = waitForConnectStep(result, [this]() { asio::error_code ignored; mSocket.close(ignored); });
97 }
98
99 if(!errorCode)
100 {
101 mConnected = true;
103 mConnectedTuple.setAddress(endpoint_iterator->endpoint().address());
104 mConnectedTuple.setPort(endpoint_iterator->endpoint().port());
105 }
106 ++endpoint_iterator;
107 }
108
109 return errorCode;
110}
111
112asio::error_code
113TurnTcpSocket::rawWrite(const char* buffer, unsigned int size)
114{
115 asio::error_code errorCode;
116 asio::write(mSocket, asio::buffer(buffer, size), asio::transfer_all(), errorCode);
117 return errorCode;
118}
119
120asio::error_code
121TurnTcpSocket::rawWrite(const std::vector<asio::const_buffer>& buffers)
122{
123 asio::error_code errorCode;
124 asio::write(mSocket, buffers, asio::transfer_all(), errorCode);
125 return errorCode;
126}
127
128asio::error_code
129TurnTcpSocket::rawRead(unsigned int timeout, unsigned int* bytesRead, asio::ip::address* sourceAddress, unsigned short* sourcePort)
130{
131 startReadTimer(timeout);
132 readHeader();
133
134 // Wait for timer and read to end
135 mIOService.run();
136 mIOService.restart();
137
138 *bytesRead = (unsigned int)(mBytesRead + 4 - (mBytesRead >= mPadding ? mPadding : 0));
139 if(!mReadErrorCode)
140 {
141 if(sourceAddress)
142 {
143 *sourceAddress = mConnectedTuple.getAddress();
144 }
145 if(sourcePort)
146 {
147 *sourcePort = mConnectedTuple.getPort();
148 }
149 }
150 return mReadErrorCode;
151}
152
153void
155{
156 asio::async_read(mSocket, asio::buffer(mReadBuffer, 4),
157 std::bind(&TurnTcpSocket::handleReadHeader, this, std::placeholders::_1));
158}
159
160void
161TurnTcpSocket::readBody(unsigned int len)
162{
163 asio::async_read(mSocket, asio::buffer(&mReadBuffer[4], len),
164 std::bind(&TurnTcpSocket::handleRawRead, this, std::placeholders::_1, std::placeholders::_2));
165}
166
167void
169{
170 asio::error_code ec;
171 mSocket.cancel(ec);
172}
173
174void
175TurnTcpSocket::handleReadHeader(const asio::error_code& e)
176{
177 if (!e)
178 {
179 /*
180 std::cout << "Read header from turn tcp socket: " << std::endl;
181 for(unsigned int i = 0; i < 4; i++)
182 {
183 std::cout << (char)mReadBuffer[i] << "(" << (int)mReadBuffer[i] << ") ";
184 }
185 std::cout << std::endl;
186 */
187
188 // Note: For both StunMessages and ChannelData messages the length in bytes 3 and 4
189 uint16_t dataLen;
190 memcpy(&dataLen, &mReadBuffer[2], 2);
191 dataLen = ntohs(dataLen);
192
193 // ChannelData's padding is read with it, but not counted in what rawRead reports
194 mPadding = 0;
195 if((mReadBuffer[0] & 0xC0) == 0) // If first 2 bits are 00 then this is a stun message
196 {
197 dataLen += 16; // There are 20 bytes in total in the header, and we have already read 4 - read the rest of the header + the body
198 }
199 else
200 {
202 }
203
204 readBody(dataLen + (unsigned int)mPadding);
205 }
206 else
207 {
208 mBytesRead = 0;
209 mReadErrorCode = e;
210 if (e != asio::error::operation_aborted)
211 {
212 WarningLog(<< "Read header error: " << e.value() << "-" << e.message());
213 mReadTimer.cancel();
214 }
215 }
216}
217
218} // namespace
219
220
221/* ====================================================================
222
223 Copyright (c) 2008-2026 SIP Spectrum, Inc. https://www.sipspectrum.com
224 Copyright (c) 2007-2008, Plantronics, Inc.
225 All rights reserved.
226
227 Redistribution and use in source and binary forms, with or without
228 modification, are permitted provided that the following conditions are
229 met:
230
231 1. Redistributions of source code must retain the above copyright
232 notice, this list of conditions and the following disclaimer.
233
234 2. Redistributions in binary form must reproduce the above copyright
235 notice, this list of conditions and the following disclaimer in the
236 documentation and/or other materials provided with the distribution.
237
238 3. Neither the name of Plantronics nor the names of its contributors
239 may be used to endorse or promote products derived from this
240 software without specific prior written permission.
241
242 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
243 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
244 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
245 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
246 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
247 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
248 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
249 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
250 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
251 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
252 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
253
254 ==================================================================== */
#define WarningLog(args_)
Definition Logger.hxx:134
static size_t channelDataPadding(size_t length)
Over TCP and TLS, a TURN ChannelData message is padded to a multiple of 4 bytes, the padding not coun...
const asio::ip::address & getAddress() const
Definition StunTuple.hxx:44
unsigned int getPort() const
Definition StunTuple.hxx:47
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
asio::steady_timer mReadTimer
StunTuple mLocalBinding
asio::error_code mReadErrorCode
char mReadBuffer[8192]
void startReadTimer(unsigned int timeout)
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 asio::error_code connect(const std::string &address, unsigned short port)
virtual asio::error_code rawRead(unsigned int timeout, unsigned int *bytesRead, asio::ip::address *sourceAddress=0, unsigned short *sourcePort=0)
TurnTcpSocket(const asio::ip::address &address=UnspecifiedIpAddress, unsigned short port=0)
virtual asio::error_code rawWrite(const char *buffer, unsigned int size)
virtual void readHeader()
virtual void readBody(unsigned int len)
virtual void handleReadHeader(const asio::error_code &e)
Handle completion of a read operation for first 32 bits.
unsigned int mConnectTimeout
asio::ip::tcp protocol() const
The protocol (IPv4 or IPv6) of the local binding.
virtual void cancelSocket()
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.
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
unsigned short uint16_t
Definition stdint.h:84