Source code

Revision control

Other Tools

1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
* License, v. 2.0. If a copy of the MPL was not distributed with this
3
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "NetworkConnectivityService.h"
6
#include "mozilla/Preferences.h"
7
#include "mozilla/Services.h"
8
#include "xpcpublic.h"
9
#include "nsSocketTransport2.h"
10
#include "nsIURIMutator.h"
11
#include "nsINetworkLinkService.h"
12
13
static LazyLogModule gNCSLog("NetworkConnectivityService");
14
#undef LOG
15
#define LOG(args) MOZ_LOG(gNCSLog, mozilla::LogLevel::Debug, args)
16
17
namespace mozilla {
18
namespace net {
19
20
NS_IMPL_ISUPPORTS(NetworkConnectivityService, nsIDNSListener, nsIObserver,
21
nsINetworkConnectivityService, nsIStreamListener)
22
23
static StaticRefPtr<NetworkConnectivityService> gConnService;
24
25
// static
26
already_AddRefed<NetworkConnectivityService>
27
NetworkConnectivityService::GetSingleton() {
28
if (gConnService) {
29
return do_AddRef(gConnService);
30
}
31
32
RefPtr<NetworkConnectivityService> service = new NetworkConnectivityService();
33
service->Init();
34
35
gConnService = service.forget();
36
ClearOnShutdown(&gConnService);
37
return do_AddRef(gConnService);
38
}
39
40
nsresult NetworkConnectivityService::Init() {
41
nsCOMPtr<nsIObserverService> observerService =
42
mozilla::services::GetObserverService();
43
observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
44
observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
45
observerService->AddObserver(this, "network:captive-portal-connectivity",
46
false);
47
48
return NS_OK;
49
}
50
51
NS_IMETHODIMP
52
NetworkConnectivityService::GetDNSv4(ConnectivityState* aState) {
53
NS_ENSURE_ARG(aState);
54
*aState = mDNSv4;
55
return NS_OK;
56
}
57
58
NS_IMETHODIMP
59
NetworkConnectivityService::GetDNSv6(ConnectivityState* aState) {
60
NS_ENSURE_ARG(aState);
61
*aState = mDNSv6;
62
return NS_OK;
63
}
64
65
NS_IMETHODIMP
66
NetworkConnectivityService::GetIPv4(ConnectivityState* aState) {
67
NS_ENSURE_ARG(aState);
68
*aState = mIPv4;
69
return NS_OK;
70
}
71
72
NS_IMETHODIMP
73
NetworkConnectivityService::GetIPv6(ConnectivityState* aState) {
74
NS_ENSURE_ARG(aState);
75
*aState = mIPv6;
76
return NS_OK;
77
}
78
79
void NetworkConnectivityService::PerformChecks() {
80
mDNSv4 = UNKNOWN;
81
mDNSv6 = UNKNOWN;
82
83
mIPv4 = UNKNOWN;
84
mIPv6 = UNKNOWN;
85
86
RecheckDNS();
87
RecheckIPConnectivity();
88
}
89
90
static inline void NotifyObservers(const char* aTopic) {
91
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
92
obs->NotifyObservers(nullptr, aTopic, nullptr);
93
}
94
95
NS_IMETHODIMP
96
NetworkConnectivityService::OnLookupComplete(nsICancelable* aRequest,
97
nsIDNSRecord* aRecord,
98
nsresult aStatus) {
99
ConnectivityState state = aRecord ? OK : NOT_AVAILABLE;
100
101
if (aRequest == mDNSv4Request) {
102
mDNSv4 = state;
103
mDNSv4Request = nullptr;
104
} else if (aRequest == mDNSv6Request) {
105
mDNSv6 = state;
106
mDNSv6Request = nullptr;
107
}
108
109
if (!mDNSv4Request && !mDNSv6Request) {
110
NotifyObservers("network:connectivity-service:dns-checks-complete");
111
}
112
return NS_OK;
113
}
114
115
NS_IMETHODIMP
116
NetworkConnectivityService::OnLookupByTypeComplete(nsICancelable* aRequest,
117
nsIDNSByTypeRecord* aRes,
118
nsresult aStatus) {
119
return NS_OK;
120
}
121
122
NS_IMETHODIMP
123
NetworkConnectivityService::RecheckDNS() {
124
bool enabled =
125
Preferences::GetBool("network.connectivity-service.enabled", false);
126
if (!enabled) {
127
return NS_OK;
128
}
129
130
nsresult rv;
131
nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
132
OriginAttributes attrs;
133
nsAutoCString host;
134
Preferences::GetCString("network.connectivity-service.DNSv4.domain", host);
135
136
rv = dns->AsyncResolveNative(
137
host,
138
nsIDNSService::RESOLVE_DISABLE_IPV6 | nsIDNSService::RESOLVE_DISABLE_TRR,
139
this, NS_GetCurrentThread(), attrs, getter_AddRefs(mDNSv4Request));
140
NS_ENSURE_SUCCESS(rv, rv);
141
142
Preferences::GetCString("network.connectivity-service.DNSv6.domain", host);
143
rv = dns->AsyncResolveNative(
144
host,
145
nsIDNSService::RESOLVE_DISABLE_IPV4 | nsIDNSService::RESOLVE_DISABLE_TRR,
146
this, NS_GetCurrentThread(), attrs, getter_AddRefs(mDNSv6Request));
147
return rv;
148
}
149
150
NS_IMETHODIMP
151
NetworkConnectivityService::Observe(nsISupports* aSubject, const char* aTopic,
152
const char16_t* aData) {
153
if (!strcmp(aTopic, "network:captive-portal-connectivity")) {
154
// Captive portal is cleared, so we redo the checks.
155
PerformChecks();
156
} else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
157
if (mDNSv4Request) {
158
mDNSv4Request->Cancel(NS_ERROR_ABORT);
159
mDNSv4Request = nullptr;
160
}
161
if (mDNSv6Request) {
162
mDNSv6Request->Cancel(NS_ERROR_ABORT);
163
mDNSv6Request = nullptr;
164
}
165
166
nsCOMPtr<nsIObserverService> observerService =
167
mozilla::services::GetObserverService();
168
observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
169
observerService->RemoveObserver(this,
170
"network:captive-portal-connectivity");
171
observerService->RemoveObserver(this, NS_NETWORK_LINK_TOPIC);
172
} else if (!strcmp(aTopic, NS_NETWORK_LINK_TOPIC) &&
173
!NS_LITERAL_STRING(NS_NETWORK_LINK_DATA_UNKNOWN).Equals(aData)) {
174
PerformChecks();
175
}
176
177
return NS_OK;
178
}
179
180
static inline already_AddRefed<nsIChannel> SetupIPCheckChannel(bool ipv4) {
181
nsresult rv;
182
nsAutoCString url;
183
184
if (ipv4) {
185
rv = Preferences::GetCString("network.connectivity-service.IPv4.url", url);
186
} else {
187
rv = Preferences::GetCString("network.connectivity-service.IPv6.url", url);
188
}
189
NS_ENSURE_SUCCESS(rv, nullptr);
190
191
nsCOMPtr<nsIURI> uri;
192
rv = NS_NewURI(getter_AddRefs(uri), url);
193
NS_ENSURE_SUCCESS(rv, nullptr);
194
195
nsCOMPtr<nsIChannel> channel;
196
rv = NS_NewChannel(
197
getter_AddRefs(channel), uri, nsContentUtils::GetSystemPrincipal(),
198
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
199
nsIContentPolicy::TYPE_OTHER,
200
nullptr, // nsICookieSettings
201
nullptr, // aPerformanceStorage
202
nullptr, // aLoadGroup
203
nullptr,
204
nsIRequest::LOAD_BYPASS_CACHE | // don't read from the cache
205
nsIRequest::INHIBIT_CACHING | // don't write the response to cache
206
nsIRequest::LOAD_DISABLE_TRR | // check network capabilities not TRR
207
nsIRequest::LOAD_ANONYMOUS); // prevent privacy leaks
208
209
NS_ENSURE_SUCCESS(rv, nullptr);
210
211
nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(channel);
212
NS_ENSURE_TRUE(internalChan, nullptr);
213
214
if (ipv4) {
215
internalChan->SetIPv6Disabled();
216
} else {
217
internalChan->SetIPv4Disabled();
218
}
219
220
return channel.forget();
221
}
222
223
NS_IMETHODIMP
224
NetworkConnectivityService::RecheckIPConnectivity() {
225
bool enabled =
226
Preferences::GetBool("network.connectivity-service.enabled", false);
227
if (!enabled) {
228
return NS_OK;
229
}
230
231
if (xpc::AreNonLocalConnectionsDisabled() &&
232
!Preferences::GetBool("network.captive-portal-service.testMode", false)) {
233
return NS_OK;
234
}
235
236
if (mIPv4Channel) {
237
mIPv4Channel->Cancel(NS_ERROR_ABORT);
238
mIPv4Channel = nullptr;
239
}
240
if (mIPv6Channel) {
241
mIPv6Channel->Cancel(NS_ERROR_ABORT);
242
mIPv6Channel = nullptr;
243
}
244
245
nsresult rv;
246
mIPv4Channel = SetupIPCheckChannel(/* ipv4 = */ true);
247
if (mIPv4Channel) {
248
rv = mIPv4Channel->AsyncOpen(this);
249
NS_ENSURE_SUCCESS(rv, rv);
250
}
251
252
mIPv6Channel = SetupIPCheckChannel(/* ipv4 = */ false);
253
if (mIPv6Channel) {
254
rv = mIPv6Channel->AsyncOpen(this);
255
NS_ENSURE_SUCCESS(rv, rv);
256
}
257
258
return NS_OK;
259
}
260
261
NS_IMETHODIMP
262
NetworkConnectivityService::OnStartRequest(nsIRequest* aRequest) {
263
return NS_OK;
264
}
265
266
NS_IMETHODIMP
267
NetworkConnectivityService::OnStopRequest(nsIRequest* aRequest,
268
nsresult aStatusCode) {
269
if (aStatusCode == NS_ERROR_ABORT) {
270
return NS_OK;
271
}
272
273
ConnectivityState status = NS_FAILED(aStatusCode) ? NOT_AVAILABLE : OK;
274
275
if (aRequest == mIPv4Channel) {
276
mIPv4 = status;
277
mIPv4Channel = nullptr;
278
279
if (mIPv4 == nsINetworkConnectivityService::OK) {
280
nsCOMPtr<nsINetworkLinkService> nls =
281
do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID);
282
nsAutoCString networkId;
283
if (nls) {
284
nls->GetNetworkID(networkId);
285
}
286
Telemetry::AccumulateCategorical(
287
networkId.IsEmpty() ? Telemetry::LABELS_NETWORK_ID_ONLINE::absent
288
: Telemetry::LABELS_NETWORK_ID_ONLINE::present);
289
LOG(("networkId.IsEmpty() : %d\n", networkId.IsEmpty()));
290
}
291
} else if (aRequest == mIPv6Channel) {
292
mIPv6 = status;
293
mIPv6Channel = nullptr;
294
} else {
295
MOZ_ASSERT(false, "Unknown request");
296
}
297
298
if (!mIPv6Channel && !mIPv4Channel) {
299
NotifyObservers("network:connectivity-service:ip-checks-complete");
300
}
301
302
return NS_OK;
303
}
304
305
NS_IMETHODIMP
306
NetworkConnectivityService::OnDataAvailable(nsIRequest* aRequest,
307
nsIInputStream* aInputStream,
308
uint64_t aOffset, uint32_t aCount) {
309
nsAutoCString data;
310
Unused << NS_ReadInputStreamToString(aInputStream, data, aCount);
311
return NS_OK;
312
}
313
314
} // namespace net
315
} // namespace mozilla