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