1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /*
8 Copyright (c) 2007, Adobe Systems, Incorporated
9 Copyright (c) 2013, Mozilla
10
11 All rights reserved.
12
13 Redistribution and use in source and binary forms, with or without
14 modification, are permitted provided that the following conditions are
15 met:
16
17 * Redistributions of source code must retain the above copyright
18 notice, this list of conditions and the following disclaimer.
19
20 * Redistributions in binary form must reproduce the above copyright
21 notice, this list of conditions and the following disclaimer in the
22 documentation and/or other materials provided with the distribution.
23
24 * Neither the name of Adobe Systems, Network Resonance, Mozilla nor
25 the names of its contributors may be used to endorse or promote
26 products derived from this software without specific prior written
27 permission.
28
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 */
41
42 #include "nr_socket_tcp.h"
43
44 #include "mozilla/ErrorNames.h"
45
46 #include "WebrtcTCPSocketWrapper.h"
47
48 namespace mozilla {
49 using namespace net;
50
51 using std::shared_ptr;
52
53 class NrTcpSocketData {
54 public:
NrTcpSocketData(nsTArray<uint8_t> && aData)55 explicit NrTcpSocketData(nsTArray<uint8_t>&& aData)
56 : mData(std::move(aData)) {
57 MOZ_COUNT_CTOR(NrTcpSocketData);
58 }
59
MOZ_COUNTED_DTOR(NrTcpSocketData) const60 MOZ_COUNTED_DTOR(NrTcpSocketData)
61
62 const nsTArray<uint8_t>& GetData() const { return mData; }
63
64 private:
65 nsTArray<uint8_t> mData;
66 };
67
NrTcpSocket(const shared_ptr<NrSocketProxyConfig> & aConfig)68 NrTcpSocket::NrTcpSocket(const shared_ptr<NrSocketProxyConfig>& aConfig)
69 : mClosed(false),
70 mReadOffset(0),
71 mConfig(aConfig),
72 mWebrtcTCPSocket(nullptr) {
73 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::NrTcpSocket %p\n", this);
74 }
75
~NrTcpSocket()76 NrTcpSocket::~NrTcpSocket() {
77 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::~NrTcpSocket %p\n", this);
78 MOZ_ASSERT(!mWebrtcTCPSocket, "webrtc TCP socket not null");
79 }
80
create(nr_transport_addr * aAddr)81 int NrTcpSocket::create(nr_transport_addr* aAddr) {
82 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::create %p\n", this);
83 int32_t port;
84 nsCString host;
85
86 // Sanity check
87 if (nr_transport_addr_get_addrstring_and_port(aAddr, &host, &port)) {
88 return R_FAILED;
89 }
90
91 if (nr_transport_addr_copy(&my_addr_, aAddr)) {
92 return R_FAILED;
93 }
94
95 return 0;
96 }
97
connect(const nr_transport_addr * aAddr)98 int NrTcpSocket::connect(const nr_transport_addr* aAddr) {
99 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::connect %p\n", this);
100
101 nsCString remote_host;
102 int remote_port;
103
104 if (NS_WARN_IF(nr_transport_addr_get_addrstring_and_port(aAddr, &remote_host,
105 &remote_port))) {
106 return R_FAILED;
107 }
108
109 bool use_tls = aAddr->tls;
110
111 nsCString local_addr;
112 int local_port;
113
114 if (NS_WARN_IF(nr_transport_addr_get_addrstring_and_port(
115 &my_addr_, &local_addr, &local_port))) {
116 return R_FAILED;
117 }
118
119 mWebrtcTCPSocket = new WebrtcTCPSocketWrapper(this);
120
121 mWebrtcTCPSocket->AsyncOpen(remote_host, remote_port, local_addr, local_port,
122 use_tls, mConfig);
123
124 // trigger nr_socket_buffered to set write/read callback
125 return R_WOULDBLOCK;
126 }
127
close()128 void NrTcpSocket::close() {
129 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::close %p\n", this);
130
131 if (mClosed) {
132 return;
133 }
134
135 mClosed = true;
136
137 // We're not always open at this point.
138 if (mWebrtcTCPSocket) {
139 mWebrtcTCPSocket->Close();
140 mWebrtcTCPSocket = nullptr;
141 }
142 }
143
write(const void * aBuffer,size_t aCount,size_t * aWrote)144 int NrTcpSocket::write(const void* aBuffer, size_t aCount, size_t* aWrote) {
145 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::write %p count=%zu\n", this,
146 aCount);
147
148 if (mClosed) {
149 return R_FAILED;
150 }
151
152 if (!aWrote) {
153 return R_FAILED;
154 }
155
156 if (NS_WARN_IF(!mWebrtcTCPSocket)) {
157 return R_FAILED;
158 }
159
160 *aWrote = aCount;
161
162 if (aCount > 0) {
163 nsTArray<uint8_t> writeData;
164 writeData.SetLength(aCount);
165 memcpy(writeData.Elements(), aBuffer, aCount);
166
167 mWebrtcTCPSocket->SendWrite(std::move(writeData));
168 }
169
170 return 0;
171 }
172
read(void * aBuffer,size_t aCount,size_t * aRead)173 int NrTcpSocket::read(void* aBuffer, size_t aCount, size_t* aRead) {
174 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::read %p\n", this);
175
176 if (mClosed) {
177 return R_FAILED;
178 }
179
180 if (!aRead) {
181 return R_FAILED;
182 }
183
184 *aRead = 0;
185
186 if (mReadQueue.empty()) {
187 return R_WOULDBLOCK;
188 }
189
190 while (aCount > 0 && !mReadQueue.empty()) {
191 const NrTcpSocketData& data = mReadQueue.front();
192
193 size_t remainingCount = data.GetData().Length() - mReadOffset;
194 size_t amountToCopy = std::min(aCount, remainingCount);
195
196 char* buffer = static_cast<char*>(aBuffer) + (*aRead);
197
198 memcpy(buffer, data.GetData().Elements() + mReadOffset, amountToCopy);
199
200 mReadOffset += amountToCopy;
201 *aRead += amountToCopy;
202 aCount -= amountToCopy;
203
204 if (remainingCount == amountToCopy) {
205 mReadOffset = 0;
206 mReadQueue.pop_front();
207 }
208 }
209
210 return 0;
211 }
212
getaddr(nr_transport_addr * aAddr)213 int NrTcpSocket::getaddr(nr_transport_addr* aAddr) {
214 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::getaddr %p\n", this);
215 return nr_transport_addr_copy(aAddr, &my_addr_);
216 }
217
sendto(const void * aBuffer,size_t aCount,int aFlags,const nr_transport_addr * aAddr)218 int NrTcpSocket::sendto(const void* aBuffer, size_t aCount, int aFlags,
219 const nr_transport_addr* aAddr) {
220 // never call this
221 MOZ_ASSERT(0);
222 return R_FAILED;
223 }
224
recvfrom(void * aBuffer,size_t aCount,size_t * aRead,int aFlags,nr_transport_addr * aAddr)225 int NrTcpSocket::recvfrom(void* aBuffer, size_t aCount, size_t* aRead,
226 int aFlags, nr_transport_addr* aAddr) {
227 // never call this
228 MOZ_ASSERT(0);
229 return R_FAILED;
230 }
231
listen(int aBacklog)232 int NrTcpSocket::listen(int aBacklog) {
233 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::listen %p\n", this);
234 return R_INTERNAL;
235 }
236
accept(nr_transport_addr * aAddr,nr_socket ** aSocket)237 int NrTcpSocket::accept(nr_transport_addr* aAddr, nr_socket** aSocket) {
238 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::accept %p\n", this);
239 return R_INTERNAL;
240 }
241
242 // WebrtcTCPSocketCallback
OnClose(nsresult aReason)243 void NrTcpSocket::OnClose(nsresult aReason) {
244 nsCString errorName;
245 GetErrorName(aReason, errorName);
246
247 r_log(LOG_GENERIC, LOG_ERR, "NrTcpSocket::OnClose %p reason=%u name=%s\n",
248 this, static_cast<uint32_t>(aReason), errorName.get());
249
250 close();
251
252 DoCallbacks();
253 }
254
OnConnected(const nsCString & aProxyType)255 void NrTcpSocket::OnConnected(const nsCString& aProxyType) {
256 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::OnConnected %p\n", this);
257 if (aProxyType != "" && aProxyType != "direct") {
258 my_addr_.is_proxied = true;
259 }
260
261 DoCallbacks();
262 }
263
OnRead(nsTArray<uint8_t> && aReadData)264 void NrTcpSocket::OnRead(nsTArray<uint8_t>&& aReadData) {
265 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::OnRead %p read=%zu\n", this,
266 aReadData.Length());
267
268 mReadQueue.emplace_back(std::move(aReadData));
269
270 DoCallbacks();
271 }
272
DoCallbacks()273 void NrTcpSocket::DoCallbacks() {
274 size_t lastCount = -1;
275 size_t currentCount = 0;
276 while ((poll_flags() & PR_POLL_READ) != 0 &&
277 // Make sure whatever is reading knows we're closed. This doesn't need
278 // to happen for writes since ICE doesn't like a failing writable.
279 (mClosed || (currentCount = CountUnreadBytes()) > 0) &&
280 lastCount != currentCount) {
281 fire_callback(NR_ASYNC_WAIT_READ);
282 lastCount = currentCount;
283 }
284
285 // We're always ready to write after we're connected. The parent process will
286 // buffer writes for us.
287 if (!mClosed && mWebrtcTCPSocket && (poll_flags() & PR_POLL_WRITE) != 0) {
288 fire_callback(NR_ASYNC_WAIT_WRITE);
289 }
290 }
291
CountUnreadBytes() const292 size_t NrTcpSocket::CountUnreadBytes() const {
293 size_t count = 0;
294
295 for (const NrTcpSocketData& data : mReadQueue) {
296 count += data.GetData().Length();
297 }
298
299 MOZ_ASSERT(count >= mReadOffset, "offset exceeds read buffer length");
300
301 count -= mReadOffset;
302
303 return count;
304 }
305
AssignChannel_DoNotUse(WebrtcTCPSocketWrapper * aWrapper)306 void NrTcpSocket::AssignChannel_DoNotUse(WebrtcTCPSocketWrapper* aWrapper) {
307 mWebrtcTCPSocket = aWrapper;
308 }
309
310 } // namespace mozilla
311