1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 *
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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "IOActivityMonitor.h"
7 #include "nsPrintfCString.h"
8 #include "nsSocketTransport2.h"
9 #include "nsSocketTransportService2.h"
10 #include "nsThreadUtils.h"
11 #include "mozilla/dom/Promise.h"
12 #include "prerror.h"
13 #include "prio.h"
14 #include "prmem.h"
15 #include <vector>
16
17 using namespace mozilla;
18 using namespace mozilla::net;
19
20 mozilla::StaticRefPtr<IOActivityMonitor> gInstance;
21 static PRDescIdentity sNetActivityMonitorLayerIdentity;
22 static PRIOMethods sNetActivityMonitorLayerMethods;
23 static PRIOMethods* sNetActivityMonitorLayerMethodsPtr = nullptr;
24
25 // Maximum number of activities entries in the monitoring class
26 #define MAX_ACTIVITY_ENTRIES 1000
27
28 // ActivityMonitorSecret is stored in the activity monitor layer
29 // and provides a method to get the location.
30 //
31 // A location can be :
32 // - a TCP or UDP socket. The form will be socket://ip:port
33 // - a File. The form will be file://path
34 //
35 // For other cases, the location will be fd://number
36 class ActivityMonitorSecret final {
37 public:
38 // constructor used for sockets
ActivityMonitorSecret(PRFileDesc * aFd)39 explicit ActivityMonitorSecret(PRFileDesc* aFd) {
40 mFd = aFd;
41 mLocationSet = false;
42 }
43
44 // constructor used for files
ActivityMonitorSecret(PRFileDesc * aFd,const char * aLocation)45 explicit ActivityMonitorSecret(PRFileDesc* aFd, const char* aLocation) {
46 mFd = aFd;
47 mLocation.AppendPrintf("file://%s", aLocation);
48 mLocationSet = true;
49 }
50
getLocation()51 nsCString getLocation() {
52 if (!mLocationSet) {
53 LazySetLocation();
54 }
55 return mLocation;
56 }
57
58 private:
59 // Called to set the location using the FD on the first getLocation() usage
60 // which is typically when a socket is opened. If done earlier, at
61 // construction time, the host won't be bound yet.
62 //
63 // If the location is a file, it needs to be initialized in the
64 // constructor.
LazySetLocation()65 void LazySetLocation() {
66 mLocationSet = true;
67 PRFileDesc* extract = mFd;
68 while (PR_GetDescType(extract) == PR_DESC_LAYERED) {
69 if (!extract->lower) {
70 break;
71 }
72 extract = extract->lower;
73 }
74
75 PRDescType fdType = PR_GetDescType(extract);
76 // we should not use LazySetLocation for files
77 MOZ_ASSERT(fdType != PR_DESC_FILE);
78
79 switch (fdType) {
80 case PR_DESC_SOCKET_TCP:
81 case PR_DESC_SOCKET_UDP: {
82 mLocation.AppendPrintf("socket://");
83 PRNetAddr addr;
84 PRStatus status = PR_GetSockName(mFd, &addr);
85 if (NS_WARN_IF(status == PR_FAILURE)) {
86 mLocation.AppendPrintf("unknown");
87 break;
88 }
89
90 // grabbing the host
91 char netAddr[mozilla::net::kNetAddrMaxCStrBufSize] = {0};
92 status = PR_NetAddrToString(&addr, netAddr, sizeof(netAddr) - 1);
93 if (NS_WARN_IF(status == PR_FAILURE) || netAddr[0] == 0) {
94 mLocation.AppendPrintf("unknown");
95 break;
96 }
97 mLocation.Append(netAddr);
98
99 // adding the port
100 uint16_t port;
101 if (addr.raw.family == PR_AF_INET) {
102 port = addr.inet.port;
103 } else {
104 port = addr.ipv6.port;
105 }
106 mLocation.AppendPrintf(":%d", port);
107 } break;
108
109 // for all other cases, we just send back fd://<value>
110 default: {
111 mLocation.AppendPrintf("fd://%d", PR_FileDesc2NativeHandle(mFd));
112 }
113 } // end switch
114 }
115
116 private:
117 nsCString mLocation;
118 bool mLocationSet;
119 PRFileDesc* mFd;
120 };
121
122 // FileDesc2Location converts a PRFileDesc into a "location" by
123 // grabbing the ActivityMonitorSecret in layer->secret
FileDesc2Location(PRFileDesc * fd)124 static nsAutoCString FileDesc2Location(PRFileDesc* fd) {
125 nsAutoCString location;
126 PRFileDesc* monitorLayer =
127 PR_GetIdentitiesLayer(fd, sNetActivityMonitorLayerIdentity);
128 if (!monitorLayer) {
129 location.AppendPrintf("unknown");
130 return location;
131 }
132
133 ActivityMonitorSecret* secret = (ActivityMonitorSecret*)monitorLayer->secret;
134 location.AppendPrintf("%s", secret->getLocation().get());
135 return location;
136 }
137
138 //
139 // Wrappers around the socket APIS
140 //
nsNetMon_Connect(PRFileDesc * fd,const PRNetAddr * addr,PRIntervalTime timeout)141 static PRStatus nsNetMon_Connect(PRFileDesc* fd, const PRNetAddr* addr,
142 PRIntervalTime timeout) {
143 return fd->lower->methods->connect(fd->lower, addr, timeout);
144 }
145
nsNetMon_Close(PRFileDesc * fd)146 static PRStatus nsNetMon_Close(PRFileDesc* fd) {
147 if (!fd) {
148 return PR_FAILURE;
149 }
150 PRFileDesc* layer = PR_PopIOLayer(fd, PR_TOP_IO_LAYER);
151 MOZ_RELEASE_ASSERT(
152 layer && layer->identity == sNetActivityMonitorLayerIdentity,
153 "NetActivityMonitor Layer not on top of stack");
154
155 if (layer->secret) {
156 delete (ActivityMonitorSecret*)layer->secret;
157 layer->secret = nullptr;
158 }
159 layer->dtor(layer);
160 return fd->methods->close(fd);
161 }
162
nsNetMon_Read(PRFileDesc * fd,void * buf,int32_t len)163 static int32_t nsNetMon_Read(PRFileDesc* fd, void* buf, int32_t len) {
164 int32_t ret = fd->lower->methods->read(fd->lower, buf, len);
165 if (ret >= 0) {
166 IOActivityMonitor::Read(fd, len);
167 }
168 return ret;
169 }
170
nsNetMon_Write(PRFileDesc * fd,const void * buf,int32_t len)171 static int32_t nsNetMon_Write(PRFileDesc* fd, const void* buf, int32_t len) {
172 int32_t ret = fd->lower->methods->write(fd->lower, buf, len);
173 if (ret > 0) {
174 IOActivityMonitor::Write(fd, len);
175 }
176 return ret;
177 }
178
nsNetMon_Writev(PRFileDesc * fd,const PRIOVec * iov,int32_t size,PRIntervalTime timeout)179 static int32_t nsNetMon_Writev(PRFileDesc* fd, const PRIOVec* iov, int32_t size,
180 PRIntervalTime timeout) {
181 int32_t ret = fd->lower->methods->writev(fd->lower, iov, size, timeout);
182 if (ret > 0) {
183 IOActivityMonitor::Write(fd, size);
184 }
185 return ret;
186 }
187
nsNetMon_Recv(PRFileDesc * fd,void * buf,int32_t amount,int flags,PRIntervalTime timeout)188 static int32_t nsNetMon_Recv(PRFileDesc* fd, void* buf, int32_t amount,
189 int flags, PRIntervalTime timeout) {
190 int32_t ret =
191 fd->lower->methods->recv(fd->lower, buf, amount, flags, timeout);
192 if (ret > 0) {
193 IOActivityMonitor::Read(fd, amount);
194 }
195 return ret;
196 }
197
nsNetMon_Send(PRFileDesc * fd,const void * buf,int32_t amount,int flags,PRIntervalTime timeout)198 static int32_t nsNetMon_Send(PRFileDesc* fd, const void* buf, int32_t amount,
199 int flags, PRIntervalTime timeout) {
200 int32_t ret =
201 fd->lower->methods->send(fd->lower, buf, amount, flags, timeout);
202 if (ret > 0) {
203 IOActivityMonitor::Write(fd, amount);
204 }
205 return ret;
206 }
207
nsNetMon_RecvFrom(PRFileDesc * fd,void * buf,int32_t amount,int flags,PRNetAddr * addr,PRIntervalTime timeout)208 static int32_t nsNetMon_RecvFrom(PRFileDesc* fd, void* buf, int32_t amount,
209 int flags, PRNetAddr* addr,
210 PRIntervalTime timeout) {
211 int32_t ret = fd->lower->methods->recvfrom(fd->lower, buf, amount, flags,
212 addr, timeout);
213 if (ret > 0) {
214 IOActivityMonitor::Read(fd, amount);
215 }
216 return ret;
217 }
218
nsNetMon_SendTo(PRFileDesc * fd,const void * buf,int32_t amount,int flags,const PRNetAddr * addr,PRIntervalTime timeout)219 static int32_t nsNetMon_SendTo(PRFileDesc* fd, const void* buf, int32_t amount,
220 int flags, const PRNetAddr* addr,
221 PRIntervalTime timeout) {
222 int32_t ret =
223 fd->lower->methods->sendto(fd->lower, buf, amount, flags, addr, timeout);
224 if (ret > 0) {
225 IOActivityMonitor::Write(fd, amount);
226 }
227 return ret;
228 }
229
nsNetMon_AcceptRead(PRFileDesc * listenSock,PRFileDesc ** acceptedSock,PRNetAddr ** peerAddr,void * buf,int32_t amount,PRIntervalTime timeout)230 static int32_t nsNetMon_AcceptRead(PRFileDesc* listenSock,
231 PRFileDesc** acceptedSock,
232 PRNetAddr** peerAddr, void* buf,
233 int32_t amount, PRIntervalTime timeout) {
234 int32_t ret = listenSock->lower->methods->acceptread(
235 listenSock->lower, acceptedSock, peerAddr, buf, amount, timeout);
236 if (ret > 0) {
237 IOActivityMonitor::Read(listenSock, amount);
238 }
239 return ret;
240 }
241
242 //
243 // Class IOActivityMonitor
244 //
NS_IMPL_ISUPPORTS(IOActivityMonitor,nsINamed)245 NS_IMPL_ISUPPORTS(IOActivityMonitor, nsINamed)
246
247 IOActivityMonitor::IOActivityMonitor() : mLock("IOActivityMonitor::mLock") {
248 RefPtr<IOActivityMonitor> mon(gInstance);
249 MOZ_ASSERT(!mon, "multiple IOActivityMonitor instances!");
250 }
251
252 // static
RequestActivities(dom::Promise * aPromise)253 void IOActivityMonitor::RequestActivities(dom::Promise* aPromise) {
254 MOZ_ASSERT(aPromise);
255 RefPtr<IOActivityMonitor> mon(gInstance);
256 if (!IsActive()) {
257 aPromise->MaybeReject(NS_ERROR_FAILURE);
258 return;
259 }
260 mon->RequestActivitiesInternal(aPromise);
261 }
262
RequestActivitiesInternal(dom::Promise * aPromise)263 void IOActivityMonitor::RequestActivitiesInternal(dom::Promise* aPromise) {
264 nsresult result = NS_OK;
265 FallibleTArray<dom::IOActivityDataDictionary> activities;
266
267 {
268 mozilla::MutexAutoLock lock(mLock);
269 // Remove inactive activities
270 for (auto iter = mActivities.Iter(); !iter.Done(); iter.Next()) {
271 dom::IOActivityDataDictionary* activity = &iter.Data();
272 if (activity->mRx == 0 && activity->mTx == 0) {
273 iter.Remove();
274 } else {
275 if (NS_WARN_IF(!activities.AppendElement(iter.Data(), fallible))) {
276 result = NS_ERROR_OUT_OF_MEMORY;
277 break;
278 }
279 }
280 }
281 }
282
283 if (NS_WARN_IF(NS_FAILED(result))) {
284 aPromise->MaybeReject(result);
285 return;
286 }
287 aPromise->MaybeResolve(activities);
288 }
289
290 // static
291 NS_IMETHODIMP
GetName(nsACString & aName)292 IOActivityMonitor::GetName(nsACString& aName) {
293 aName.AssignLiteral("IOActivityMonitor");
294 return NS_OK;
295 }
296
IsActive()297 bool IOActivityMonitor::IsActive() { return gInstance != nullptr; }
298
Init()299 nsresult IOActivityMonitor::Init() {
300 if (IsActive()) {
301 return NS_ERROR_ALREADY_INITIALIZED;
302 }
303 RefPtr<IOActivityMonitor> mon = new IOActivityMonitor();
304 nsresult rv = mon->InitInternal();
305 if (NS_SUCCEEDED(rv)) {
306 gInstance = mon;
307 }
308 return rv;
309 }
310
InitInternal()311 nsresult IOActivityMonitor::InitInternal() {
312 // wraps the socket APIs
313 if (!sNetActivityMonitorLayerMethodsPtr) {
314 sNetActivityMonitorLayerIdentity =
315 PR_GetUniqueIdentity("network activity monitor layer");
316 sNetActivityMonitorLayerMethods = *PR_GetDefaultIOMethods();
317 sNetActivityMonitorLayerMethods.connect = nsNetMon_Connect;
318 sNetActivityMonitorLayerMethods.read = nsNetMon_Read;
319 sNetActivityMonitorLayerMethods.write = nsNetMon_Write;
320 sNetActivityMonitorLayerMethods.writev = nsNetMon_Writev;
321 sNetActivityMonitorLayerMethods.recv = nsNetMon_Recv;
322 sNetActivityMonitorLayerMethods.send = nsNetMon_Send;
323 sNetActivityMonitorLayerMethods.recvfrom = nsNetMon_RecvFrom;
324 sNetActivityMonitorLayerMethods.sendto = nsNetMon_SendTo;
325 sNetActivityMonitorLayerMethods.acceptread = nsNetMon_AcceptRead;
326 sNetActivityMonitorLayerMethods.close = nsNetMon_Close;
327 sNetActivityMonitorLayerMethodsPtr = &sNetActivityMonitorLayerMethods;
328 }
329
330 return NS_OK;
331 }
332
Shutdown()333 nsresult IOActivityMonitor::Shutdown() {
334 RefPtr<IOActivityMonitor> mon(gInstance);
335 if (!mon) {
336 return NS_ERROR_NOT_INITIALIZED;
337 }
338 return mon->ShutdownInternal();
339 }
340
ShutdownInternal()341 nsresult IOActivityMonitor::ShutdownInternal() {
342 mozilla::MutexAutoLock lock(mLock);
343 mActivities.Clear();
344 gInstance = nullptr;
345 return NS_OK;
346 }
347
MonitorSocket(PRFileDesc * aFd)348 nsresult IOActivityMonitor::MonitorSocket(PRFileDesc* aFd) {
349 RefPtr<IOActivityMonitor> mon(gInstance);
350 if (!IsActive()) {
351 return NS_OK;
352 }
353 PRFileDesc* layer;
354 PRStatus status;
355 layer = PR_CreateIOLayerStub(sNetActivityMonitorLayerIdentity,
356 sNetActivityMonitorLayerMethodsPtr);
357 if (!layer) {
358 return NS_ERROR_FAILURE;
359 }
360
361 ActivityMonitorSecret* secret = new ActivityMonitorSecret(aFd);
362 layer->secret = reinterpret_cast<PRFilePrivate*>(secret);
363 status = PR_PushIOLayer(aFd, PR_NSPR_IO_LAYER, layer);
364
365 if (status == PR_FAILURE) {
366 delete secret;
367 PR_Free(layer); // PR_CreateIOLayerStub() uses PR_Malloc().
368 return NS_ERROR_FAILURE;
369 }
370 return NS_OK;
371 }
372
MonitorFile(PRFileDesc * aFd,const char * aPath)373 nsresult IOActivityMonitor::MonitorFile(PRFileDesc* aFd, const char* aPath) {
374 RefPtr<IOActivityMonitor> mon(gInstance);
375 if (!IsActive()) {
376 return NS_OK;
377 }
378 PRFileDesc* layer;
379 PRStatus status;
380 layer = PR_CreateIOLayerStub(sNetActivityMonitorLayerIdentity,
381 sNetActivityMonitorLayerMethodsPtr);
382 if (!layer) {
383 return NS_ERROR_FAILURE;
384 }
385
386 ActivityMonitorSecret* secret = new ActivityMonitorSecret(aFd, aPath);
387 layer->secret = reinterpret_cast<PRFilePrivate*>(secret);
388
389 status = PR_PushIOLayer(aFd, PR_NSPR_IO_LAYER, layer);
390 if (status == PR_FAILURE) {
391 delete secret;
392 PR_Free(layer); // PR_CreateIOLayerStub() uses PR_Malloc().
393 return NS_ERROR_FAILURE;
394 }
395
396 return NS_OK;
397 }
398
IncrementActivity(const nsACString & aLocation,uint32_t aRx,uint32_t aTx)399 bool IOActivityMonitor::IncrementActivity(const nsACString& aLocation,
400 uint32_t aRx, uint32_t aTx) {
401 mLock.AssertCurrentThreadOwns();
402 return mActivities.WithEntryHandle(aLocation, fallible, [&](auto&& entry) {
403 if (!entry) return false;
404
405 if (*entry) {
406 // already registered
407 entry->Data().mTx += aTx;
408 entry->Data().mRx += aRx;
409 } else {
410 // Creating a new IOActivity. Notice that mActivities
411 // will grow indefinitely, which is OK since we won't
412 // have but a few hundreds entries at the most, but we
413 // want to assert we have at the most 1000 entries
414 MOZ_ASSERT(mActivities.Count() <= MAX_ACTIVITY_ENTRIES);
415
416 dom::IOActivityDataDictionary activity;
417 activity.mLocation.Assign(aLocation);
418 activity.mTx = aTx;
419 activity.mRx = aRx;
420
421 entry->Insert(std::move(activity));
422 }
423
424 return true;
425 });
426 }
427
Write(const nsACString & aLocation,uint32_t aAmount)428 nsresult IOActivityMonitor::Write(const nsACString& aLocation,
429 uint32_t aAmount) {
430 RefPtr<IOActivityMonitor> mon(gInstance);
431 if (!mon) {
432 return NS_ERROR_FAILURE;
433 }
434 return mon->WriteInternal(aLocation, aAmount);
435 }
436
Write(PRFileDesc * fd,uint32_t aAmount)437 nsresult IOActivityMonitor::Write(PRFileDesc* fd, uint32_t aAmount) {
438 RefPtr<IOActivityMonitor> mon(gInstance);
439 if (!mon) {
440 return NS_ERROR_FAILURE;
441 }
442 return mon->Write(FileDesc2Location(fd), aAmount);
443 }
444
WriteInternal(const nsACString & aLocation,uint32_t aAmount)445 nsresult IOActivityMonitor::WriteInternal(const nsACString& aLocation,
446 uint32_t aAmount) {
447 mozilla::MutexAutoLock lock(mLock);
448 if (!IncrementActivity(aLocation, aAmount, 0)) {
449 return NS_ERROR_FAILURE;
450 }
451 return NS_OK;
452 }
453
Read(PRFileDesc * fd,uint32_t aAmount)454 nsresult IOActivityMonitor::Read(PRFileDesc* fd, uint32_t aAmount) {
455 RefPtr<IOActivityMonitor> mon(gInstance);
456 if (!mon) {
457 return NS_ERROR_FAILURE;
458 }
459 return mon->Read(FileDesc2Location(fd), aAmount);
460 }
461
Read(const nsACString & aLocation,uint32_t aAmount)462 nsresult IOActivityMonitor::Read(const nsACString& aLocation,
463 uint32_t aAmount) {
464 RefPtr<IOActivityMonitor> mon(gInstance);
465 if (!mon) {
466 return NS_ERROR_FAILURE;
467 }
468 return mon->ReadInternal(aLocation, aAmount);
469 }
470
ReadInternal(const nsACString & aLocation,uint32_t aAmount)471 nsresult IOActivityMonitor::ReadInternal(const nsACString& aLocation,
472 uint32_t aAmount) {
473 mozilla::MutexAutoLock lock(mLock);
474 if (!IncrementActivity(aLocation, 0, aAmount)) {
475 return NS_ERROR_FAILURE;
476 }
477 return NS_OK;
478 }
479