Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 : #include "TCPSocketParent.h"
8 : #include "jsapi.h"
9 : #include "jsfriendapi.h"
10 : #include "nsJSUtils.h"
11 : #include "mozilla/Unused.h"
12 : #include "mozilla/net/NeckoCommon.h"
13 : #include "mozilla/net/PNeckoParent.h"
14 : #include "mozilla/dom/ContentParent.h"
15 : #include "mozilla/dom/ScriptSettings.h"
16 : #include "mozilla/dom/TabParent.h"
17 : #include "mozilla/HoldDropJSObjects.h"
18 : #include "nsISocketTransportService.h"
19 : #include "nsISocketTransport.h"
20 : #include "nsIScriptSecurityManager.h"
21 : #include "nsNetUtil.h"
22 :
23 : namespace IPC {
24 :
25 : //Defined in TCPSocketChild.cpp
26 : extern bool
27 : DeserializeArrayBuffer(JSContext* aCx,
28 : const InfallibleTArray<uint8_t>& aBuffer,
29 : JS::MutableHandle<JS::Value> aVal);
30 :
31 : } // namespace IPC
32 :
33 : namespace mozilla {
34 :
35 : namespace net {
36 : //
37 : // set MOZ_LOG=TCPSocket:5
38 : //
39 : extern LazyLogModule gTCPSocketLog;
40 : #define TCPSOCKET_LOG(args) MOZ_LOG(gTCPSocketLog, LogLevel::Debug, args)
41 : #define TCPSOCKET_LOG_ENABLED() MOZ_LOG_TEST(gTCPSocketLog, LogLevel::Debug)
42 : } // namespace net
43 :
44 : namespace dom {
45 :
46 : static void
47 0 : FireInteralError(mozilla::net::PTCPSocketParent* aActor, uint32_t aLineNo)
48 : {
49 : mozilla::Unused <<
50 0 : aActor->SendCallback(NS_LITERAL_STRING("onerror"),
51 0 : TCPError(NS_LITERAL_STRING("InvalidStateError"), NS_LITERAL_STRING("Internal error")),
52 : static_cast<uint32_t>(TCPReadyState::Connecting));
53 0 : }
54 :
55 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocketParentBase)
56 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
57 0 : NS_INTERFACE_MAP_END
58 :
59 0 : NS_IMPL_CYCLE_COLLECTION(TCPSocketParentBase, mSocket)
60 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPSocketParentBase)
61 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPSocketParentBase)
62 :
63 0 : TCPSocketParentBase::TCPSocketParentBase()
64 0 : : mIPCOpen(false)
65 : {
66 0 : }
67 :
68 0 : TCPSocketParentBase::~TCPSocketParentBase()
69 : {
70 0 : }
71 :
72 : uint32_t
73 0 : TCPSocketParent::GetAppId()
74 : {
75 0 : return nsIScriptSecurityManager::UNKNOWN_APP_ID;
76 : };
77 :
78 : bool
79 0 : TCPSocketParent::GetInIsolatedMozBrowser()
80 : {
81 0 : const PContentParent *content = Manager()->Manager();
82 0 : if (PBrowserParent* browser = SingleManagedOrNull(content->ManagedPBrowserParent())) {
83 0 : TabParent *tab = TabParent::GetFrom(browser);
84 0 : return tab->IsIsolatedMozBrowserElement();
85 : } else {
86 0 : return false;
87 : }
88 : }
89 :
90 : void
91 0 : TCPSocketParentBase::ReleaseIPDLReference()
92 : {
93 0 : MOZ_ASSERT(mIPCOpen);
94 0 : mIPCOpen = false;
95 0 : this->Release();
96 0 : }
97 :
98 : void
99 0 : TCPSocketParentBase::AddIPDLReference()
100 : {
101 0 : MOZ_ASSERT(!mIPCOpen);
102 0 : mIPCOpen = true;
103 0 : this->AddRef();
104 0 : }
105 :
106 0 : NS_IMETHODIMP_(MozExternalRefCountType) TCPSocketParent::Release(void)
107 : {
108 0 : nsrefcnt refcnt = TCPSocketParentBase::Release();
109 0 : if (refcnt == 1 && mIPCOpen) {
110 0 : mozilla::Unused << PTCPSocketParent::SendRequestDelete();
111 0 : return 1;
112 : }
113 0 : return refcnt;
114 : }
115 :
116 : mozilla::ipc::IPCResult
117 0 : TCPSocketParent::RecvOpen(const nsString& aHost, const uint16_t& aPort, const bool& aUseSSL,
118 : const bool& aUseArrayBuffers)
119 : {
120 : // Obtain App ID
121 0 : uint32_t appId = GetAppId();
122 0 : bool inIsolatedMozBrowser = GetInIsolatedMozBrowser();
123 :
124 0 : mSocket = new TCPSocket(nullptr, aHost, aPort, aUseSSL, aUseArrayBuffers);
125 0 : mSocket->SetAppIdAndBrowser(appId, inIsolatedMozBrowser);
126 0 : mSocket->SetSocketBridgeParent(this);
127 0 : NS_ENSURE_SUCCESS(mSocket->Init(), IPC_OK());
128 0 : return IPC_OK();
129 : }
130 :
131 : mozilla::ipc::IPCResult
132 0 : TCPSocketParent::RecvOpenBind(const nsCString& aRemoteHost,
133 : const uint16_t& aRemotePort,
134 : const nsCString& aLocalAddr,
135 : const uint16_t& aLocalPort,
136 : const bool& aUseSSL,
137 : const bool& aReuseAddrPort,
138 : const bool& aUseArrayBuffers,
139 : const nsCString& aFilter)
140 : {
141 : nsresult rv;
142 : nsCOMPtr<nsISocketTransportService> sts =
143 0 : do_GetService("@mozilla.org/network/socket-transport-service;1", &rv);
144 0 : if (NS_FAILED(rv)) {
145 0 : FireInteralError(this, __LINE__);
146 0 : return IPC_OK();
147 : }
148 :
149 0 : nsCOMPtr<nsISocketTransport> socketTransport;
150 0 : if (aUseSSL) {
151 : const char* socketTypes[1];
152 0 : socketTypes[0] = "ssl";
153 0 : rv = sts->CreateTransport(socketTypes, 1,
154 0 : aRemoteHost, aRemotePort,
155 0 : nullptr, getter_AddRefs(socketTransport));
156 : } else {
157 0 : rv = sts->CreateTransport(nullptr, 0,
158 0 : aRemoteHost, aRemotePort,
159 0 : nullptr, getter_AddRefs(socketTransport));
160 : }
161 :
162 0 : if (NS_FAILED(rv)) {
163 0 : FireInteralError(this, __LINE__);
164 0 : return IPC_OK();
165 : }
166 :
167 : // in most cases aReuseAddrPort is false, but ICE TCP needs
168 : // sockets options set that allow addr/port reuse
169 0 : socketTransport->SetReuseAddrPort(aReuseAddrPort);
170 :
171 : PRNetAddr prAddr;
172 0 : if (PR_SUCCESS != PR_InitializeNetAddr(PR_IpAddrAny, aLocalPort, &prAddr)) {
173 0 : FireInteralError(this, __LINE__);
174 0 : return IPC_OK();
175 : }
176 0 : if (PR_SUCCESS != PR_StringToNetAddr(aLocalAddr.BeginReading(), &prAddr)) {
177 0 : FireInteralError(this, __LINE__);
178 0 : return IPC_OK();
179 : }
180 :
181 : mozilla::net::NetAddr addr;
182 0 : PRNetAddrToNetAddr(&prAddr, &addr);
183 0 : rv = socketTransport->Bind(&addr);
184 0 : if (NS_FAILED(rv)) {
185 0 : FireInteralError(this, __LINE__);
186 0 : return IPC_OK();
187 : }
188 :
189 0 : if (!aFilter.IsEmpty()) {
190 0 : nsAutoCString contractId(NS_NETWORK_TCP_SOCKET_FILTER_HANDLER_PREFIX);
191 0 : contractId.Append(aFilter);
192 : nsCOMPtr<nsISocketFilterHandler> filterHandler =
193 0 : do_GetService(contractId.get());
194 0 : if (!filterHandler) {
195 0 : NS_ERROR("Content doesn't have a valid filter");
196 0 : FireInteralError(this, __LINE__);
197 0 : return IPC_OK();
198 : }
199 0 : rv = filterHandler->NewFilter(getter_AddRefs(mFilter));
200 0 : if (NS_FAILED(rv)) {
201 0 : NS_ERROR("Cannot create filter that content specified");
202 0 : FireInteralError(this, __LINE__);
203 0 : return IPC_OK();
204 : }
205 : }
206 :
207 0 : bool inIsolatedMozBrowser = false;
208 0 : const PContentParent *content = Manager()->Manager();
209 0 : if (PBrowserParent* browser = SingleManagedOrNull(content->ManagedPBrowserParent())) {
210 0 : TabParent *tab = TabParent::GetFrom(browser);
211 0 : inIsolatedMozBrowser = tab->IsIsolatedMozBrowserElement();
212 : }
213 :
214 0 : mSocket = new TCPSocket(nullptr, NS_ConvertUTF8toUTF16(aRemoteHost), aRemotePort, aUseSSL, aUseArrayBuffers);
215 0 : mSocket->SetAppIdAndBrowser(nsIScriptSecurityManager::NO_APP_ID, inIsolatedMozBrowser);
216 0 : mSocket->SetSocketBridgeParent(this);
217 0 : rv = mSocket->InitWithUnconnectedTransport(socketTransport);
218 0 : NS_ENSURE_SUCCESS(rv, IPC_OK());
219 0 : return IPC_OK();
220 : }
221 :
222 : mozilla::ipc::IPCResult
223 0 : TCPSocketParent::RecvStartTLS()
224 : {
225 0 : NS_ENSURE_TRUE(mSocket, IPC_OK());
226 0 : ErrorResult rv;
227 0 : mSocket->UpgradeToSecure(rv);
228 0 : if (NS_WARN_IF(rv.Failed())) {
229 0 : rv.SuppressException();
230 : }
231 :
232 0 : return IPC_OK();
233 : }
234 :
235 : mozilla::ipc::IPCResult
236 0 : TCPSocketParent::RecvSuspend()
237 : {
238 0 : NS_ENSURE_TRUE(mSocket, IPC_OK());
239 0 : mSocket->Suspend();
240 0 : return IPC_OK();
241 : }
242 :
243 : mozilla::ipc::IPCResult
244 0 : TCPSocketParent::RecvResume()
245 : {
246 0 : NS_ENSURE_TRUE(mSocket, IPC_OK());
247 0 : ErrorResult rv;
248 0 : mSocket->Resume(rv);
249 0 : if (NS_WARN_IF(rv.Failed())) {
250 0 : rv.SuppressException();
251 : }
252 :
253 0 : return IPC_OK();
254 : }
255 :
256 : mozilla::ipc::IPCResult
257 0 : TCPSocketParent::RecvData(const SendableData& aData,
258 : const uint32_t& aTrackingNumber)
259 : {
260 0 : ErrorResult rv;
261 :
262 0 : if (mFilter) {
263 : mozilla::net::NetAddr addr; // dummy value
264 : bool allowed;
265 0 : MOZ_ASSERT(aData.type() == SendableData::TArrayOfuint8_t,
266 : "Unsupported data type for filtering");
267 0 : const InfallibleTArray<uint8_t>& data(aData.get_ArrayOfuint8_t());
268 0 : nsresult nsrv = mFilter->FilterPacket(&addr, data.Elements(),
269 0 : data.Length(),
270 : nsISocketFilter::SF_OUTGOING,
271 0 : &allowed);
272 :
273 : // Reject sending of unallowed data
274 0 : if (NS_WARN_IF(NS_FAILED(nsrv)) || !allowed) {
275 0 : TCPSOCKET_LOG(("%s: Dropping outgoing TCP packet", __FUNCTION__));
276 0 : return IPC_FAIL_NO_REASON(this);
277 : }
278 : }
279 :
280 0 : switch (aData.type()) {
281 : case SendableData::TArrayOfuint8_t: {
282 0 : AutoSafeJSContext autoCx;
283 0 : JS::Rooted<JS::Value> val(autoCx);
284 0 : const nsTArray<uint8_t>& buffer = aData.get_ArrayOfuint8_t();
285 0 : bool ok = IPC::DeserializeArrayBuffer(autoCx, buffer, &val);
286 0 : NS_ENSURE_TRUE(ok, IPC_OK());
287 0 : RootedTypedArray<ArrayBuffer> data(autoCx);
288 0 : data.Init(&val.toObject());
289 0 : Optional<uint32_t> byteLength(buffer.Length());
290 0 : mSocket->SendWithTrackingNumber(autoCx, data, 0, byteLength, aTrackingNumber, rv);
291 0 : break;
292 : }
293 :
294 : case SendableData::TnsCString: {
295 0 : const nsCString& strData = aData.get_nsCString();
296 0 : mSocket->SendWithTrackingNumber(strData, aTrackingNumber, rv);
297 0 : break;
298 : }
299 :
300 : default:
301 0 : MOZ_CRASH("unexpected SendableData type");
302 : }
303 0 : NS_ENSURE_SUCCESS(rv.StealNSResult(), IPC_OK());
304 0 : return IPC_OK();
305 : }
306 :
307 : mozilla::ipc::IPCResult
308 0 : TCPSocketParent::RecvClose()
309 : {
310 0 : NS_ENSURE_TRUE(mSocket, IPC_OK());
311 0 : mSocket->Close();
312 0 : return IPC_OK();
313 : }
314 :
315 : void
316 0 : TCPSocketParent::FireErrorEvent(const nsAString& aName, const nsAString& aType, TCPReadyState aReadyState)
317 : {
318 0 : SendEvent(NS_LITERAL_STRING("error"), TCPError(nsString(aName), nsString(aType)), aReadyState);
319 0 : }
320 :
321 : void
322 0 : TCPSocketParent::FireEvent(const nsAString& aType, TCPReadyState aReadyState)
323 : {
324 0 : return SendEvent(aType, mozilla::void_t(), aReadyState);
325 : }
326 :
327 : void
328 0 : TCPSocketParent::FireArrayBufferDataEvent(nsTArray<uint8_t>& aBuffer, TCPReadyState aReadyState)
329 : {
330 0 : InfallibleTArray<uint8_t> arr;
331 0 : arr.SwapElements(aBuffer);
332 :
333 0 : if (mFilter) {
334 : bool allowed;
335 : mozilla::net::NetAddr addr;
336 0 : nsresult nsrv = mFilter->FilterPacket(&addr, arr.Elements(), arr.Length(),
337 : nsISocketFilter::SF_INCOMING,
338 0 : &allowed);
339 : // receiving unallowed data, drop it.
340 0 : if (NS_WARN_IF(NS_FAILED(nsrv)) || !allowed) {
341 0 : TCPSOCKET_LOG(("%s: Dropping incoming TCP packet", __FUNCTION__));
342 0 : return;
343 : }
344 : }
345 :
346 0 : SendableData data(arr);
347 0 : SendEvent(NS_LITERAL_STRING("data"), data, aReadyState);
348 : }
349 :
350 : void
351 0 : TCPSocketParent::FireStringDataEvent(const nsACString& aData, TCPReadyState aReadyState)
352 : {
353 0 : SendableData data((nsCString(aData)));
354 :
355 0 : MOZ_ASSERT(!mFilter, "Socket filtering doesn't support nsCString");
356 :
357 0 : SendEvent(NS_LITERAL_STRING("data"), data, aReadyState);
358 0 : }
359 :
360 : void
361 0 : TCPSocketParent::SendEvent(const nsAString& aType, CallbackData aData, TCPReadyState aReadyState)
362 : {
363 0 : if (mIPCOpen) {
364 0 : mozilla::Unused << PTCPSocketParent::SendCallback(nsString(aType),
365 : aData,
366 0 : static_cast<uint32_t>(aReadyState));
367 : }
368 0 : }
369 :
370 : void
371 0 : TCPSocketParent::SetSocket(TCPSocket *socket)
372 : {
373 0 : mSocket = socket;
374 0 : }
375 :
376 : nsresult
377 0 : TCPSocketParent::GetHost(nsAString& aHost)
378 : {
379 0 : if (!mSocket) {
380 0 : NS_ERROR("No internal socket instance mSocket!");
381 0 : return NS_ERROR_FAILURE;
382 : }
383 0 : mSocket->GetHost(aHost);
384 0 : return NS_OK;
385 : }
386 :
387 : nsresult
388 0 : TCPSocketParent::GetPort(uint16_t* aPort)
389 : {
390 0 : if (!mSocket) {
391 0 : NS_ERROR("No internal socket instance mSocket!");
392 0 : return NS_ERROR_FAILURE;
393 : }
394 0 : *aPort = mSocket->Port();
395 0 : return NS_OK;
396 : }
397 :
398 : void
399 0 : TCPSocketParent::ActorDestroy(ActorDestroyReason why)
400 : {
401 0 : if (mSocket) {
402 0 : mSocket->Close();
403 : }
404 0 : mSocket = nullptr;
405 0 : }
406 :
407 : mozilla::ipc::IPCResult
408 0 : TCPSocketParent::RecvRequestDelete()
409 : {
410 0 : mozilla::Unused << Send__delete__(this);
411 0 : return IPC_OK();
412 : }
413 :
414 : } // namespace dom
415 : } // namespace mozilla
|