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 "UDPSocketParent.h"
8 #include "nsComponentManagerUtils.h"
9 #include "nsIUDPSocket.h"
10 #include "nsINetAddr.h"
11 #include "mozilla/Unused.h"
12 #include "mozilla/dom/ContentParent.h"
13 #include "mozilla/ipc/InputStreamUtils.h"
14 #include "mozilla/net/DNS.h"
15 #include "mozilla/net/NeckoCommon.h"
16 #include "mozilla/net/PNeckoParent.h"
17 #include "nsIPermissionManager.h"
18 #include "mozilla/ipc/PBackgroundParent.h"
19 #include "mtransport/runnable_utils.h"
20
21 namespace mozilla {
22
23 using namespace net;
24
25 namespace dom {
26
NS_IMPL_ISUPPORTS(UDPSocketParent,nsIUDPSocketListener)27 NS_IMPL_ISUPPORTS(UDPSocketParent, nsIUDPSocketListener)
28
29 UDPSocketParent::UDPSocketParent(PBackgroundParent* aManager)
30 : mBackgroundManager(aManager), mIPCOpen(true) {}
31
UDPSocketParent(PNeckoParent * aManager)32 UDPSocketParent::UDPSocketParent(PNeckoParent* aManager)
33 : mBackgroundManager(nullptr), mIPCOpen(true) {}
34
35 UDPSocketParent::~UDPSocketParent() = default;
36
Init(nsIPrincipal * aPrincipal,const nsACString & aFilter)37 bool UDPSocketParent::Init(nsIPrincipal* aPrincipal,
38 const nsACString& aFilter) {
39 MOZ_ASSERT_IF(mBackgroundManager, !aPrincipal);
40 // will be used once we move all UDPSocket to PBackground, or
41 // if we add in Principal checking for mtransport
42 Unused << mBackgroundManager;
43
44 mPrincipal = aPrincipal;
45
46 if (!aFilter.IsEmpty()) {
47 nsAutoCString contractId(NS_NETWORK_UDP_SOCKET_FILTER_HANDLER_PREFIX);
48 contractId.Append(aFilter);
49 nsCOMPtr<nsISocketFilterHandler> filterHandler =
50 do_GetService(contractId.get());
51 if (filterHandler) {
52 nsresult rv = filterHandler->NewFilter(getter_AddRefs(mFilter));
53 if (NS_FAILED(rv)) {
54 printf_stderr(
55 "Cannot create filter that content specified. "
56 "filter name: %s, error code: %u.",
57 aFilter.BeginReading(), static_cast<uint32_t>(rv));
58 return false;
59 }
60 } else {
61 printf_stderr(
62 "Content doesn't have a valid filter. "
63 "filter name: %s.",
64 aFilter.BeginReading());
65 return false;
66 }
67 }
68
69 return true;
70 }
71
72 // PUDPSocketParent methods
73
RecvBind(const UDPAddressInfo & aAddressInfo,const bool & aAddressReuse,const bool & aLoopback,const uint32_t & recvBufferSize,const uint32_t & sendBufferSize)74 mozilla::ipc::IPCResult UDPSocketParent::RecvBind(
75 const UDPAddressInfo& aAddressInfo, const bool& aAddressReuse,
76 const bool& aLoopback, const uint32_t& recvBufferSize,
77 const uint32_t& sendBufferSize) {
78 UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, aAddressInfo.addr().get(),
79 aAddressInfo.port()));
80
81 if (NS_FAILED(BindInternal(aAddressInfo.addr(), aAddressInfo.port(),
82 aAddressReuse, aLoopback, recvBufferSize,
83 sendBufferSize))) {
84 FireInternalError(__LINE__);
85 return IPC_OK();
86 }
87
88 nsCOMPtr<nsINetAddr> localAddr;
89 mSocket->GetLocalAddr(getter_AddRefs(localAddr));
90
91 nsCString addr;
92 if (NS_FAILED(localAddr->GetAddress(addr))) {
93 FireInternalError(__LINE__);
94 return IPC_OK();
95 }
96
97 uint16_t port;
98 if (NS_FAILED(localAddr->GetPort(&port))) {
99 FireInternalError(__LINE__);
100 return IPC_OK();
101 }
102
103 UDPSOCKET_LOG(
104 ("%s: SendCallbackOpened: %s:%u", __FUNCTION__, addr.get(), port));
105 mozilla::Unused << SendCallbackOpened(UDPAddressInfo(addr, port));
106
107 return IPC_OK();
108 }
109
BindInternal(const nsCString & aHost,const uint16_t & aPort,const bool & aAddressReuse,const bool & aLoopback,const uint32_t & recvBufferSize,const uint32_t & sendBufferSize)110 nsresult UDPSocketParent::BindInternal(const nsCString& aHost,
111 const uint16_t& aPort,
112 const bool& aAddressReuse,
113 const bool& aLoopback,
114 const uint32_t& recvBufferSize,
115 const uint32_t& sendBufferSize) {
116 nsresult rv;
117
118 UDPSOCKET_LOG(
119 ("%s: [this=%p] %s:%u addressReuse: %d loopback: %d recvBufferSize: "
120 "%" PRIu32 ", sendBufferSize: %" PRIu32,
121 __FUNCTION__, this, nsCString(aHost).get(), aPort, aAddressReuse,
122 aLoopback, recvBufferSize, sendBufferSize));
123
124 nsCOMPtr<nsIUDPSocket> sock =
125 do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv);
126
127 if (NS_WARN_IF(NS_FAILED(rv))) {
128 return rv;
129 }
130
131 if (aHost.IsEmpty()) {
132 rv = sock->Init(aPort, false, mPrincipal, aAddressReuse,
133 /* optional_argc = */ 1);
134 } else {
135 PRNetAddr prAddr;
136 PR_InitializeNetAddr(PR_IpAddrAny, aPort, &prAddr);
137 PRStatus status = PR_StringToNetAddr(aHost.BeginReading(), &prAddr);
138 if (status != PR_SUCCESS) {
139 return NS_ERROR_FAILURE;
140 }
141
142 mozilla::net::NetAddr addr;
143 PRNetAddrToNetAddr(&prAddr, &addr);
144 rv = sock->InitWithAddress(&addr, mPrincipal, aAddressReuse,
145 /* optional_argc = */ 1);
146 }
147
148 if (NS_WARN_IF(NS_FAILED(rv))) {
149 return rv;
150 }
151
152 nsCOMPtr<nsINetAddr> laddr;
153 rv = sock->GetLocalAddr(getter_AddRefs(laddr));
154 if (NS_WARN_IF(NS_FAILED(rv))) {
155 return rv;
156 }
157 uint16_t family;
158 rv = laddr->GetFamily(&family);
159 if (NS_WARN_IF(NS_FAILED(rv))) {
160 return rv;
161 }
162 if (family == nsINetAddr::FAMILY_INET) {
163 rv = sock->SetMulticastLoopback(aLoopback);
164 if (NS_WARN_IF(NS_FAILED(rv))) {
165 return rv;
166 }
167 }
168 // TODO: once bug 1252759 is fixed query buffer first and only increase
169 if (recvBufferSize != 0) {
170 rv = sock->SetRecvBufferSize(recvBufferSize);
171 if (NS_WARN_IF(NS_FAILED(rv))) {
172 UDPSOCKET_LOG(
173 ("%s: [this=%p] %s:%u failed to set recv buffer size to: %" PRIu32,
174 __FUNCTION__, this, nsCString(aHost).get(), aPort, recvBufferSize));
175 }
176 }
177 if (sendBufferSize != 0) {
178 rv = sock->SetSendBufferSize(sendBufferSize);
179 if (NS_WARN_IF(NS_FAILED(rv))) {
180 UDPSOCKET_LOG(
181 ("%s: [this=%p] %s:%u failed to set send buffer size to: %" PRIu32,
182 __FUNCTION__, this, nsCString(aHost).get(), aPort, sendBufferSize));
183 }
184 }
185
186 // register listener
187 rv = sock->AsyncListen(this);
188 if (NS_WARN_IF(NS_FAILED(rv))) {
189 return rv;
190 }
191
192 mSocket = sock;
193
194 return NS_OK;
195 }
196
GetSTSThread()197 static nsCOMPtr<nsIEventTarget> GetSTSThread() {
198 nsresult rv;
199
200 nsCOMPtr<nsIEventTarget> sts_thread;
201
202 sts_thread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
203 MOZ_ASSERT(NS_SUCCEEDED(rv));
204
205 return sts_thread;
206 }
207
CheckSTSThread()208 static void CheckSTSThread() {
209 DebugOnly<nsCOMPtr<nsIEventTarget>> sts_thread = GetSTSThread();
210
211 ASSERT_ON_THREAD(sts_thread.value);
212 }
213
214 // Proxy the Connect() request to the STS thread, since it may block and
215 // should be done there.
RecvConnect(const UDPAddressInfo & aAddressInfo)216 mozilla::ipc::IPCResult UDPSocketParent::RecvConnect(
217 const UDPAddressInfo& aAddressInfo) {
218 nsCOMPtr<nsIEventTarget> target = GetCurrentThreadEventTarget();
219 Unused << NS_WARN_IF(NS_FAILED(GetSTSThread()->Dispatch(
220 WrapRunnable(RefPtr<UDPSocketParent>(this), &UDPSocketParent::DoConnect,
221 mSocket, target, aAddressInfo),
222 NS_DISPATCH_NORMAL)));
223 return IPC_OK();
224 }
225
DoSendConnectResponse(const UDPAddressInfo & aAddressInfo)226 void UDPSocketParent::DoSendConnectResponse(
227 const UDPAddressInfo& aAddressInfo) {
228 // can't use directly with WrapRunnable due to warnings
229 mozilla::Unused << SendCallbackConnected(aAddressInfo);
230 }
231
SendConnectResponse(const nsCOMPtr<nsIEventTarget> & aThread,const UDPAddressInfo & aAddressInfo)232 void UDPSocketParent::SendConnectResponse(
233 const nsCOMPtr<nsIEventTarget>& aThread,
234 const UDPAddressInfo& aAddressInfo) {
235 Unused << NS_WARN_IF(NS_FAILED(aThread->Dispatch(
236 WrapRunnable(RefPtr<UDPSocketParent>(this),
237 &UDPSocketParent::DoSendConnectResponse, aAddressInfo),
238 NS_DISPATCH_NORMAL)));
239 }
240
241 // Runs on STS thread
DoConnect(const nsCOMPtr<nsIUDPSocket> & aSocket,const nsCOMPtr<nsIEventTarget> & aReturnThread,const UDPAddressInfo & aAddressInfo)242 void UDPSocketParent::DoConnect(const nsCOMPtr<nsIUDPSocket>& aSocket,
243 const nsCOMPtr<nsIEventTarget>& aReturnThread,
244 const UDPAddressInfo& aAddressInfo) {
245 UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, aAddressInfo.addr().get(),
246 aAddressInfo.port()));
247 if (NS_FAILED(ConnectInternal(aAddressInfo.addr(), aAddressInfo.port()))) {
248 SendInternalError(aReturnThread, __LINE__);
249 return;
250 }
251 CheckSTSThread();
252
253 nsCOMPtr<nsINetAddr> localAddr;
254 aSocket->GetLocalAddr(getter_AddRefs(localAddr));
255
256 nsCString addr;
257 if (NS_FAILED(localAddr->GetAddress(addr))) {
258 SendInternalError(aReturnThread, __LINE__);
259 return;
260 }
261
262 uint16_t port;
263 if (NS_FAILED(localAddr->GetPort(&port))) {
264 SendInternalError(aReturnThread, __LINE__);
265 return;
266 }
267
268 UDPSOCKET_LOG(
269 ("%s: SendConnectResponse: %s:%u", __FUNCTION__, addr.get(), port));
270 SendConnectResponse(aReturnThread, UDPAddressInfo(addr, port));
271 }
272
ConnectInternal(const nsCString & aHost,const uint16_t & aPort)273 nsresult UDPSocketParent::ConnectInternal(const nsCString& aHost,
274 const uint16_t& aPort) {
275 nsresult rv;
276
277 UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, nsCString(aHost).get(), aPort));
278
279 if (!mSocket) {
280 return NS_ERROR_NOT_AVAILABLE;
281 }
282
283 PRNetAddr prAddr;
284 memset(&prAddr, 0, sizeof(prAddr));
285 PR_InitializeNetAddr(PR_IpAddrAny, aPort, &prAddr);
286 PRStatus status = PR_StringToNetAddr(aHost.BeginReading(), &prAddr);
287 if (status != PR_SUCCESS) {
288 return NS_ERROR_FAILURE;
289 }
290
291 mozilla::net::NetAddr addr;
292 PRNetAddrToNetAddr(&prAddr, &addr);
293
294 rv = mSocket->Connect(&addr);
295 if (NS_WARN_IF(NS_FAILED(rv))) {
296 return rv;
297 }
298
299 return NS_OK;
300 }
301
RecvOutgoingData(const UDPData & aData,const UDPSocketAddr & aAddr)302 mozilla::ipc::IPCResult UDPSocketParent::RecvOutgoingData(
303 const UDPData& aData, const UDPSocketAddr& aAddr) {
304 if (!mSocket) {
305 NS_WARNING("sending socket is closed");
306 FireInternalError(__LINE__);
307 return IPC_OK();
308 }
309
310 nsresult rv;
311 if (mFilter) {
312 if (aAddr.type() != UDPSocketAddr::TNetAddr) {
313 return IPC_OK();
314 }
315
316 // TODO, Packet filter doesn't support input stream yet.
317 if (aData.type() != UDPData::TArrayOfuint8_t) {
318 return IPC_OK();
319 }
320
321 bool allowed;
322 const nsTArray<uint8_t>& data(aData.get_ArrayOfuint8_t());
323 rv = mFilter->FilterPacket(&aAddr.get_NetAddr(), data.Elements(),
324 data.Length(), nsISocketFilter::SF_OUTGOING,
325 &allowed);
326
327 // Sending unallowed data, kill content.
328 if (NS_WARN_IF(NS_FAILED(rv)) || !allowed) {
329 return IPC_FAIL(this, "Content tried to send non STUN packet");
330 }
331 }
332
333 switch (aData.type()) {
334 case UDPData::TArrayOfuint8_t:
335 Send(aData.get_ArrayOfuint8_t(), aAddr);
336 break;
337 case UDPData::TIPCStream:
338 Send(aData.get_IPCStream(), aAddr);
339 break;
340 default:
341 MOZ_ASSERT(false, "Invalid data type!");
342 return IPC_OK();
343 }
344
345 return IPC_OK();
346 }
347
Send(const nsTArray<uint8_t> & aData,const UDPSocketAddr & aAddr)348 void UDPSocketParent::Send(const nsTArray<uint8_t>& aData,
349 const UDPSocketAddr& aAddr) {
350 nsresult rv;
351 uint32_t count;
352 switch (aAddr.type()) {
353 case UDPSocketAddr::TUDPAddressInfo: {
354 const UDPAddressInfo& addrInfo(aAddr.get_UDPAddressInfo());
355 rv = mSocket->Send(addrInfo.addr(), addrInfo.port(), aData, &count);
356 break;
357 }
358 case UDPSocketAddr::TNetAddr: {
359 const NetAddr& addr(aAddr.get_NetAddr());
360 rv = mSocket->SendWithAddress(&addr, aData, &count);
361 break;
362 }
363 default:
364 MOZ_ASSERT(false, "Invalid address type!");
365 return;
366 }
367
368 if (NS_WARN_IF(NS_FAILED(rv)) || count == 0) {
369 FireInternalError(__LINE__);
370 }
371 }
372
Send(const IPCStream & aStream,const UDPSocketAddr & aAddr)373 void UDPSocketParent::Send(const IPCStream& aStream,
374 const UDPSocketAddr& aAddr) {
375 nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aStream);
376
377 if (NS_WARN_IF(!stream)) {
378 return;
379 }
380
381 nsresult rv;
382 switch (aAddr.type()) {
383 case UDPSocketAddr::TUDPAddressInfo: {
384 const UDPAddressInfo& addrInfo(aAddr.get_UDPAddressInfo());
385 rv = mSocket->SendBinaryStream(addrInfo.addr(), addrInfo.port(), stream);
386 break;
387 }
388 case UDPSocketAddr::TNetAddr: {
389 const NetAddr& addr(aAddr.get_NetAddr());
390 rv = mSocket->SendBinaryStreamWithAddress(&addr, stream);
391 break;
392 }
393 default:
394 MOZ_ASSERT(false, "Invalid address type!");
395 return;
396 }
397
398 if (NS_FAILED(rv)) {
399 FireInternalError(__LINE__);
400 }
401 }
402
RecvJoinMulticast(const nsCString & aMulticastAddress,const nsCString & aInterface)403 mozilla::ipc::IPCResult UDPSocketParent::RecvJoinMulticast(
404 const nsCString& aMulticastAddress, const nsCString& aInterface) {
405 if (!mSocket) {
406 NS_WARNING("multicast socket is closed");
407 FireInternalError(__LINE__);
408 return IPC_OK();
409 }
410
411 nsresult rv = mSocket->JoinMulticast(aMulticastAddress, aInterface);
412
413 if (NS_WARN_IF(NS_FAILED(rv))) {
414 FireInternalError(__LINE__);
415 }
416
417 return IPC_OK();
418 }
419
RecvLeaveMulticast(const nsCString & aMulticastAddress,const nsCString & aInterface)420 mozilla::ipc::IPCResult UDPSocketParent::RecvLeaveMulticast(
421 const nsCString& aMulticastAddress, const nsCString& aInterface) {
422 if (!mSocket) {
423 NS_WARNING("multicast socket is closed");
424 FireInternalError(__LINE__);
425 return IPC_OK();
426 }
427
428 nsresult rv = mSocket->LeaveMulticast(aMulticastAddress, aInterface);
429
430 if (NS_WARN_IF(NS_FAILED(rv))) {
431 FireInternalError(__LINE__);
432 }
433
434 return IPC_OK();
435 }
436
RecvClose()437 mozilla::ipc::IPCResult UDPSocketParent::RecvClose() {
438 if (!mSocket) {
439 return IPC_OK();
440 }
441
442 nsresult rv = mSocket->Close();
443 mSocket = nullptr;
444
445 mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
446
447 return IPC_OK();
448 }
449
RecvRequestDelete()450 mozilla::ipc::IPCResult UDPSocketParent::RecvRequestDelete() {
451 mozilla::Unused << Send__delete__(this);
452 return IPC_OK();
453 }
454
ActorDestroy(ActorDestroyReason why)455 void UDPSocketParent::ActorDestroy(ActorDestroyReason why) {
456 MOZ_ASSERT(mIPCOpen);
457 mIPCOpen = false;
458 if (mSocket) {
459 mSocket->Close();
460 }
461 mSocket = nullptr;
462 }
463
464 // nsIUDPSocketListener
465
466 NS_IMETHODIMP
OnPacketReceived(nsIUDPSocket * aSocket,nsIUDPMessage * aMessage)467 UDPSocketParent::OnPacketReceived(nsIUDPSocket* aSocket,
468 nsIUDPMessage* aMessage) {
469 // receiving packet from remote host, forward the message content to child
470 // process
471 if (!mIPCOpen) {
472 return NS_OK;
473 }
474
475 uint16_t port;
476 nsCString ip;
477 nsCOMPtr<nsINetAddr> fromAddr;
478 aMessage->GetFromAddr(getter_AddRefs(fromAddr));
479 fromAddr->GetPort(&port);
480 fromAddr->GetAddress(ip);
481
482 nsCString data;
483 aMessage->GetData(data);
484
485 const char* buffer = data.get();
486 uint32_t len = data.Length();
487 UDPSOCKET_LOG(("%s: %s:%u, length %u", __FUNCTION__, ip.get(), port, len));
488
489 if (mFilter) {
490 bool allowed;
491 mozilla::net::NetAddr addr;
492 fromAddr->GetNetAddr(&addr);
493 nsresult rv = mFilter->FilterPacket(&addr, (const uint8_t*)buffer, len,
494 nsISocketFilter::SF_INCOMING, &allowed);
495 // Receiving unallowed data, drop.
496 if (NS_WARN_IF(NS_FAILED(rv)) || !allowed) {
497 if (!allowed) {
498 UDPSOCKET_LOG(("%s: not allowed", __FUNCTION__));
499 }
500 return NS_OK;
501 }
502 }
503
504 FallibleTArray<uint8_t> fallibleArray;
505 if (!fallibleArray.InsertElementsAt(0, buffer, len, fallible)) {
506 FireInternalError(__LINE__);
507 return NS_ERROR_OUT_OF_MEMORY;
508 }
509 nsTArray<uint8_t> infallibleArray;
510 infallibleArray.SwapElements(fallibleArray);
511
512 // compose callback
513 mozilla::Unused << SendCallbackReceivedData(UDPAddressInfo(ip, port),
514 infallibleArray);
515
516 return NS_OK;
517 }
518
519 NS_IMETHODIMP
OnStopListening(nsIUDPSocket * aSocket,nsresult aStatus)520 UDPSocketParent::OnStopListening(nsIUDPSocket* aSocket, nsresult aStatus) {
521 // underlying socket is dead, send state update to child process
522 if (mIPCOpen) {
523 mozilla::Unused << SendCallbackClosed();
524 }
525 return NS_OK;
526 }
527
FireInternalError(uint32_t aLineNo)528 void UDPSocketParent::FireInternalError(uint32_t aLineNo) {
529 if (!mIPCOpen) {
530 return;
531 }
532
533 mozilla::Unused << SendCallbackError(NS_LITERAL_CSTRING("Internal error"),
534 NS_LITERAL_CSTRING(__FILE__), aLineNo);
535 }
536
SendInternalError(const nsCOMPtr<nsIEventTarget> & aThread,uint32_t aLineNo)537 void UDPSocketParent::SendInternalError(const nsCOMPtr<nsIEventTarget>& aThread,
538 uint32_t aLineNo) {
539 UDPSOCKET_LOG(("SendInternalError: %u", aLineNo));
540 Unused << NS_WARN_IF(NS_FAILED(aThread->Dispatch(
541 WrapRunnable(RefPtr<UDPSocketParent>(this),
542 &UDPSocketParent::FireInternalError, aLineNo),
543 NS_DISPATCH_NORMAL)));
544 }
545
546 } // namespace dom
547 } // namespace mozilla
548