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