Import chromium-123.0.6312.40

This commit is contained in:
importer
2024-03-21 18:13:28 +08:00
committed by klzgrad
commit f0de60c19c
24941 changed files with 4484504 additions and 0 deletions

3842
src/net/BUILD.gn Normal file

File diff suppressed because it is too large Load Diff

7
src/net/COMMON_METADATA Normal file
View File

@@ -0,0 +1,7 @@
monorail: {
component: "Internals>Network"
}
team_email: "net-dev@chromium.org"
buganizer_public: {
component_id: 1456527
}

72
src/net/DEPS Normal file
View File

@@ -0,0 +1,72 @@
include_rules = [
"+components/miracle_parameter",
"+crypto",
"+net/net_jni_headers",
"+third_party/apple_apsl",
"+third_party/boringssl/src/include",
"+third_party/boringssl/src/pki",
"+third_party/nss",
"+third_party/protobuf/src/google/protobuf",
"+third_party/zlib",
# Most of net should not depend on icu, and brotli to keep size down when
# built as a library.
"-base/i18n",
"-third_party/brotli",
"-third_party/icu",
]
specific_include_rules = {
# Within net, only used by file: requests.
"directory_lister(\.cc|_unittest\.cc)": [
"+base/i18n",
],
# Functions largely not used by the rest of net.
"directory_listing\.cc": [
"+base/i18n",
],
# Within net, only used by file: requests.
"filename_util_icu\.cc": [
"+base/i18n/file_util_icu.h",
],
# Consolidated string functions that depend on icu.
"net_string_util_icu\.cc": [
"+base/i18n/case_conversion.h",
"+base/i18n/i18n_constants.h",
"+base/i18n/icu_string_conversions.h",
"+third_party/icu/source/common/unicode/ucnv.h"
],
"websocket_channel\.h": [
"+base/i18n",
],
"brotli_source_stream\.cc": [
"+third_party/brotli",
],
"cert_compression\.cc": [
"+third_party/brotli",
],
"fuzzer_test_support.cc": [
"+base/i18n",
],
"zstd_source_stream\.cc": [
"+third_party/zstd",
],
# Dependencies specific for fuzz targets and other fuzzing-related code.
".*fuzz.*": [
"+third_party/fuzztest",
"+third_party/libprotobuf-mutator", # This is needed for LPM-based fuzzers.
]
}
skip_child_includes = [
"third_party",
]

9
src/net/DIR_METADATA Normal file
View File

@@ -0,0 +1,9 @@
# Metadata information for this directory.
#
# For more information on DIR_METADATA files, see:
# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
#
# For the schema of this file, see Metadata message:
# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
mixins: "//net/COMMON_METADATA"

20
src/net/OWNERS Normal file
View File

@@ -0,0 +1,20 @@
set noparent
# Primary
bashi@chromium.org
ricea@chromium.org
# Secondary
agl@chromium.org
davidben@chromium.org
dschinazi@chromium.org
ericorth@chromium.org
mattm@chromium.org
mmenke@chromium.org
morlovich@chromium.org
nharper@chromium.org
pauljensen@chromium.org
rch@chromium.org
per-file BUILD.gn=file://net/nqe/OWNERS
per-file BUILD.gn=file://net/websockets/OWNERS

32
src/net/PRESUBMIT.py Normal file
View File

@@ -0,0 +1,32 @@
# Copyright 2023 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Top-level presubmit for //net
See https://www.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into depot_tools.
"""
PRESUBMIT_VERSION = '2.0.0'
def CheckChange(input_api, output_api):
import sys
old_sys_path = sys.path[:]
results = []
try:
sys.path.append(input_api.change.RepositoryRoot())
from build.ios import presubmit_support
results += presubmit_support.CheckBundleData(
input_api,
output_api,
'data/test_bundle_data',
globroot='.')
results += presubmit_support.CheckBundleData(
input_api,
output_api,
'data/test_support_bundle_data',
globroot='.')
finally:
sys.path = old_sys_path
return results

6
src/net/README.md Normal file
View File

@@ -0,0 +1,6 @@
# Chrome Networking Stack
This directory contains the code behind Chrome's networking stack.
It is documented
[here](https://www.chromium.org/developers/design-documents/network-stack).

263
src/net/android/BUILD.gn Normal file
View File

@@ -0,0 +1,263 @@
# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/android/config.gni")
import("//build/config/android/rules.gni")
import("//third_party/jni_zero/jni_zero.gni")
android_library("net_java") {
sources = [
"java/src/org/chromium/net/AndroidCertVerifyResult.java",
"java/src/org/chromium/net/AndroidKeyStore.java",
"java/src/org/chromium/net/AndroidNetworkLibrary.java",
"java/src/org/chromium/net/AndroidTrafficStats.java",
"java/src/org/chromium/net/ChromiumNetworkAdapter.java",
"java/src/org/chromium/net/DnsStatus.java",
"java/src/org/chromium/net/GURLUtils.java",
"java/src/org/chromium/net/HttpNegotiateAuthenticator.java",
"java/src/org/chromium/net/HttpNegotiateConstants.java",
"java/src/org/chromium/net/HttpUtil.java",
"java/src/org/chromium/net/MimeTypeFilter.java",
"java/src/org/chromium/net/NetStringUtil.java",
"java/src/org/chromium/net/NetworkActiveNotifier.java",
"java/src/org/chromium/net/NetworkChangeNotifier.java",
"java/src/org/chromium/net/NetworkChangeNotifierAutoDetect.java",
"java/src/org/chromium/net/NetworkTrafficAnnotationTag.java",
"java/src/org/chromium/net/ProxyBroadcastReceiver.java",
"java/src/org/chromium/net/ProxyChangeListener.java",
"java/src/org/chromium/net/RegistrationPolicyAlwaysRegister.java",
"java/src/org/chromium/net/RegistrationPolicyApplicationStatus.java",
"java/src/org/chromium/net/X509Util.java",
]
deps = [
":net_thread_stats_uid_java",
"//base:base_java",
"//build/android:build_java",
"//third_party/android_deps:com_google_code_findbugs_jsr305_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/jni_zero:jni_zero_java",
]
srcjar_deps = [
":net_android_java_enums_srcjar",
":net_errors_java",
"//net:net_jni_headers",
]
resources_package = "org.chromium.native_test"
}
android_library("net_thread_stats_uid_java") {
sources = [ "java/src/org/chromium/net/ThreadStatsUid.java" ]
}
android_aidl("embedded_test_server_aidl") {
interface_file = "../test/android/javatests/src/org/chromium/net/test/IEmbeddedTestServerInterface.aidl"
sources = [
"../test/android/javatests/src/org/chromium/net/test/IConnectionListener.aidl",
"../test/android/javatests/src/org/chromium/net/test/IEmbeddedTestServerImpl.aidl",
]
}
android_library("embedded_test_server_aidl_java") {
testonly = true
deps = []
srcjar_deps = [ ":embedded_test_server_aidl" ]
}
generate_jni("net_test_support_jni") {
testonly = true
sources = [ "../test/android/javatests/src/org/chromium/net/AndroidNetworkLibraryTestUtil.java" ]
}
android_library("net_java_test_support") {
testonly = true
sources = [
"../test/android/javatests/src/org/chromium/net/AndroidNetworkLibraryTestUtil.java",
"../test/android/javatests/src/org/chromium/net/test/EmbeddedTestServer.java",
"../test/android/javatests/src/org/chromium/net/test/EmbeddedTestServerRule.java",
"../test/android/javatests/src/org/chromium/net/test/util/CertTestUtil.java",
"../test/android/javatests/src/org/chromium/net/test/util/NetworkChangeNotifierTestUtil.java",
"../test/android/javatests/src/org/chromium/net/test/util/TestWebServer.java",
"../test/android/javatests/src/org/chromium/net/test/util/WebServer.java",
]
deps = [
":embedded_test_server_aidl_java",
":net_java",
"//base:base_java",
"//base:base_java_test_support",
"//base:base_java_url_utils_for_test",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_test_monitor_java",
"//third_party/androidx:androidx_test_uiautomator_uiautomator_java",
"//third_party/jni_zero:jni_zero_java",
"//third_party/junit",
]
srcjar_deps = [
":net_java_test_support_enums_srcjar",
":net_test_support_jni",
]
mergeable_android_manifests =
[ "../test/android/javatests/AndroidManifest_client.xml" ]
}
generate_jni("net_test_support_provider_jni") {
testonly = true
sources = [
"../test/android/javatests/src/org/chromium/net/test/DummySpnegoAuthenticator.java",
"../test/android/javatests/src/org/chromium/net/test/EmbeddedTestServerImpl.java",
]
}
android_library("net_test_support_provider_java") {
testonly = true
sources = [
"../test/android/javatests/src/org/chromium/net/test/DummySpnegoAuthenticator.java",
"../test/android/javatests/src/org/chromium/net/test/DummySpnegoAuthenticatorService.java",
"../test/android/javatests/src/org/chromium/net/test/EmbeddedTestServerImpl.java",
"../test/android/javatests/src/org/chromium/net/test/EmbeddedTestServerService.java",
]
srcjar_deps = [ ":net_test_support_provider_jni" ]
deps = [
":embedded_test_server_aidl_java",
":net_java",
"//base:base_java",
"//base:base_java_url_utils_for_test",
"//third_party/jni_zero:jni_zero_java",
]
data_deps = [ "//net:test_support" ]
}
source_set("java_test_native_support") {
testonly = true
sources = [
"../test/android/net_test_entry_point.cc",
"../test/android/net_test_jni_onload.cc",
"../test/android/net_test_jni_onload.h",
"../test/embedded_test_server/android/embedded_test_server_android.cc",
"../test/embedded_test_server/android/embedded_test_server_android.h",
]
deps = [ "//net:test_support" ]
public_deps = [ ":net_test_support_provider_jni" ]
}
shared_library_with_jni("net_java_test_native_support") {
testonly = true
deps = [
":java_test_native_support",
"//net:test_support",
]
configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
configs += [ "//build/config/android:hide_all_but_jni" ]
java_targets = [ ":net_test_support_apk" ]
}
android_apk("net_test_support_apk") {
testonly = true
# Used as an additional_apk in test scripts.
never_incremental = true
# Required on Android Q+ to read from /sdcard when installing certs.
target_sdk_version = 28
deps = [
":net_java_test_support",
":net_test_support_provider_java",
]
android_manifest = "../test/android/javatests/AndroidManifest.xml"
apk_name = "ChromiumNetTestSupport"
shared_libraries = [ ":net_java_test_native_support" ]
srcjar_deps = [ ":net_java_test_native_support__jni_registration" ]
}
android_resources("net_unittests_apk_resources") {
sources = [
"unittest_support/res/mipmap-hdpi/app_icon.png",
"unittest_support/res/mipmap-mdpi/app_icon.png",
"unittest_support/res/mipmap-xhdpi/app_icon.png",
"unittest_support/res/mipmap-xxhdpi/app_icon.png",
"unittest_support/res/mipmap-xxxhdpi/app_icon.png",
"unittest_support/res/xml/dummy_spnego_account_preferences.xml",
"unittest_support/res/xml/dummy_spnego_authenticator.xml",
]
}
generate_jni("net_tests_jni") {
testonly = true
sources = [
"javatests/src/org/chromium/net/AndroidKeyStoreTestUtil.java",
"javatests/src/org/chromium/net/AndroidProxyConfigServiceTestUtil.java",
]
}
android_library("net_tests_java") {
testonly = true
sources = [
"javatests/src/org/chromium/net/AndroidKeyStoreTestUtil.java",
"javatests/src/org/chromium/net/AndroidNetworkLibraryTest.java",
"javatests/src/org/chromium/net/AndroidProxyConfigServiceTestUtil.java",
"javatests/src/org/chromium/net/AndroidProxySelectorTest.java",
"javatests/src/org/chromium/net/HttpUtilTest.java",
"javatests/src/org/chromium/net/MimeTypeFilterTest.java",
"javatests/src/org/chromium/net/NetErrorsTest.java",
"javatests/src/org/chromium/net/NetworkChangeNotifierNoNativeTest.java",
"javatests/src/org/chromium/net/NetworkChangeNotifierTest.java",
"javatests/src/org/chromium/net/ProxyChangeListenerTest.java",
"javatests/src/org/chromium/net/X509UtilTest.java",
]
srcjar_deps = [ ":net_tests_jni" ]
deps = [
":net_java",
":net_java_test_support",
"//base:base_java",
"//base:base_java_test_support",
"//third_party/android_sdk:android_test_mock_java",
"//third_party/androidx:androidx_test_monitor_java",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/jni_zero:jni_zero_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
]
}
java_cpp_template("net_errors_java") {
sources = [ "java/NetError.template" ]
inputs = [ "../base/net_error_list.h" ]
}
java_cpp_enum("net_java_test_support_enums_srcjar") {
sources = [
"../test/embedded_test_server/embedded_test_server.h",
"../test/url_request/url_request_failed_job.h",
]
}
java_cpp_enum("net_android_java_enums_srcjar") {
sources = [
"../base/network_change_notifier.h",
"../socket/socket_tag.cc",
"cert_verify_result_android.h",
"keystore.h",
"network_change_notifier_android.cc",
"traffic_stats.cc",
]
}
robolectric_binary("net_junit_tests") {
sources = [
"junit/src/org/chromium/net/HttpNegotiateAuthenticatorTest.java",
"junit/src/org/chromium/net/NetworkTrafficAnnotationTagTest.java",
]
deps = [
":net_java",
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
"//third_party/hamcrest:hamcrest_java",
"//third_party/jni_zero:jni_zero_java",
]
}

View File

@@ -0,0 +1,28 @@
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/android/jni_string.h"
#include "net/http/http_util.h"
#include "net/net_jni_headers/HttpUtil_jni.h"
#include "url/gurl.h"
using base::android::ConvertJavaStringToUTF8;
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
namespace net {
jboolean JNI_HttpUtil_IsAllowedHeader(
JNIEnv* env,
const JavaParamRef<jstring>& j_header_name,
const JavaParamRef<jstring>& j_header_value) {
std::string header_name(ConvertJavaStringToUTF8(env, j_header_name));
std::string header_value(ConvertJavaStringToUTF8(env, j_header_value));
return HttpUtil::IsValidHeaderName(header_name) &&
HttpUtil::IsSafeHeader(header_name, header_value) &&
HttpUtil::IsValidHeaderValue(header_value);
}
} // namespace net

View File

@@ -0,0 +1,35 @@
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/android/cert_verify_result_android.h"
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "net/net_jni_headers/AndroidCertVerifyResult_jni.h"
using base::android::AttachCurrentThread;
using base::android::JavaArrayOfByteArrayToStringVector;
using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;
namespace net::android {
void ExtractCertVerifyResult(const JavaRef<jobject>& result,
CertVerifyStatusAndroid* status,
bool* is_issued_by_known_root,
std::vector<std::string>* verified_chain) {
JNIEnv* env = AttachCurrentThread();
*status = static_cast<CertVerifyStatusAndroid>(
Java_AndroidCertVerifyResult_getStatus(env, result));
*is_issued_by_known_root =
Java_AndroidCertVerifyResult_isIssuedByKnownRoot(env, result);
ScopedJavaLocalRef<jobjectArray> chain_byte_array =
Java_AndroidCertVerifyResult_getCertificateChainEncoded(env, result);
JavaArrayOfByteArrayToStringVector(env, chain_byte_array, verified_chain);
}
} // namespace net::android

View File

@@ -0,0 +1,49 @@
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_ANDROID_CERT_VERIFY_RESULT_ANDROID_H_
#define NET_ANDROID_CERT_VERIFY_RESULT_ANDROID_H_
#include <jni.h>
#include <string>
#include <vector>
#include "base/android/scoped_java_ref.h"
namespace net::android {
// The list of certificate verification results returned from Java side to the
// C++ side.
//
// A Java counterpart will be generated for this enum.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.net
enum CertVerifyStatusAndroid {
// Certificate is trusted.
CERT_VERIFY_STATUS_ANDROID_OK = 0,
// Certificate verification could not be conducted.
CERT_VERIFY_STATUS_ANDROID_FAILED = -1,
// Certificate is not trusted due to non-trusted root of the certificate
// chain.
CERT_VERIFY_STATUS_ANDROID_NO_TRUSTED_ROOT = -2,
// Certificate is not trusted because it has expired.
CERT_VERIFY_STATUS_ANDROID_EXPIRED = -3,
// Certificate is not trusted because it is not valid yet.
CERT_VERIFY_STATUS_ANDROID_NOT_YET_VALID = -4,
// Certificate is not trusted because it could not be parsed.
CERT_VERIFY_STATUS_ANDROID_UNABLE_TO_PARSE = -5,
// Certificate is not trusted because it has an extendedKeyUsage field, but
// its value is not correct for a web server.
CERT_VERIFY_STATUS_ANDROID_INCORRECT_KEY_USAGE = -6,
};
// Extract parameters out of an AndroidCertVerifyResult object.
void ExtractCertVerifyResult(const base::android::JavaRef<jobject>& result,
CertVerifyStatusAndroid* status,
bool* is_issued_by_known_root,
std::vector<std::string>* verified_chain);
} // namespace net::android
#endif // NET_ANDROID_CERT_VERIFY_RESULT_ANDROID_H_

View File

@@ -0,0 +1,198 @@
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/android/dummy_spnego_authenticator.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/base64.h"
#include "net/android/net_test_support_provider_jni/DummySpnegoAuthenticator_jni.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::android::JavaParamRef;
namespace net {
// iso.org.dod.internet.security.mechanism.snego (1.3.6.1.5.5.2)
// From RFC 4178, which uses SNEGO not SPNEGO.
static const unsigned char kSpnegoOid[] = {0x2b, 0x06, 0x01, 0x05, 0x05, 0x02};
gss_OID_desc CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL = {
std::size(kSpnegoOid), const_cast<unsigned char*>(kSpnegoOid)};
gss_OID CHROME_GSS_SPNEGO_MECH_OID_DESC = &CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL;
namespace {
// gss_OID helpers.
// NOTE: gss_OID's do not own the data they point to, which should be static.
void ClearOid(gss_OID dest) {
if (!dest)
return;
dest->length = 0;
dest->elements = nullptr;
}
void SetOid(gss_OID dest, const void* src, size_t length) {
if (!dest)
return;
ClearOid(dest);
if (!src)
return;
dest->length = length;
if (length)
dest->elements = const_cast<void*>(src);
}
void CopyOid(gss_OID dest, const gss_OID_desc* src) {
if (!dest)
return;
ClearOid(dest);
if (!src)
return;
SetOid(dest, src->elements, src->length);
}
} // namespace
namespace test {
GssContextMockImpl::GssContextMockImpl()
: lifetime_rec(0), ctx_flags(0), locally_initiated(0), open(0) {
ClearOid(&mech_type);
}
GssContextMockImpl::GssContextMockImpl(const GssContextMockImpl& other)
: src_name(other.src_name),
targ_name(other.targ_name),
lifetime_rec(other.lifetime_rec),
ctx_flags(other.ctx_flags),
locally_initiated(other.locally_initiated),
open(other.open) {
CopyOid(&mech_type, &other.mech_type);
}
GssContextMockImpl::GssContextMockImpl(const char* src_name_in,
const char* targ_name_in,
uint32_t lifetime_rec_in,
const gss_OID_desc& mech_type_in,
uint32_t ctx_flags_in,
int locally_initiated_in,
int open_in)
: src_name(src_name_in ? src_name_in : ""),
targ_name(targ_name_in ? targ_name_in : ""),
lifetime_rec(lifetime_rec_in),
ctx_flags(ctx_flags_in),
locally_initiated(locally_initiated_in),
open(open_in) {
CopyOid(&mech_type, &mech_type_in);
}
GssContextMockImpl::~GssContextMockImpl() {
ClearOid(&mech_type);
}
} // namespace test
namespace android {
DummySpnegoAuthenticator::SecurityContextQuery::SecurityContextQuery(
const std::string& in_expected_package,
uint32_t in_response_code,
uint32_t in_minor_response_code,
const test::GssContextMockImpl& in_context_info,
const std::string& in_expected_input_token,
const std::string& in_output_token)
: expected_package(in_expected_package),
response_code(in_response_code),
minor_response_code(in_minor_response_code),
context_info(in_context_info),
expected_input_token(in_expected_input_token),
output_token(in_output_token) {
}
DummySpnegoAuthenticator::SecurityContextQuery::SecurityContextQuery(
const std::string& in_expected_package,
uint32_t in_response_code,
uint32_t in_minor_response_code,
const test::GssContextMockImpl& in_context_info,
const char* in_expected_input_token,
const char* in_output_token)
: expected_package(in_expected_package),
response_code(in_response_code),
minor_response_code(in_minor_response_code),
context_info(in_context_info) {
if (in_expected_input_token)
expected_input_token = in_expected_input_token;
if (in_output_token)
output_token = in_output_token;
}
DummySpnegoAuthenticator::SecurityContextQuery::SecurityContextQuery()
: response_code(0), minor_response_code(0) {
}
DummySpnegoAuthenticator::SecurityContextQuery::SecurityContextQuery(
const SecurityContextQuery& other) = default;
DummySpnegoAuthenticator::SecurityContextQuery::~SecurityContextQuery() =
default;
base::android::ScopedJavaLocalRef<jstring>
DummySpnegoAuthenticator::SecurityContextQuery::GetTokenToReturn(JNIEnv* env) {
return base::android::ConvertUTF8ToJavaString(env, output_token.c_str());
}
int DummySpnegoAuthenticator::SecurityContextQuery::GetResult(JNIEnv* /*env*/) {
return response_code;
}
void DummySpnegoAuthenticator::SecurityContextQuery::CheckGetTokenArguments(
JNIEnv* env,
const JavaParamRef<jstring>& j_incoming_token) {
std::string incoming_token =
base::android::ConvertJavaStringToUTF8(env, j_incoming_token);
EXPECT_EQ(expected_input_token, incoming_token);
}
// Needed to satisfy "complex class" clang requirements.
DummySpnegoAuthenticator::DummySpnegoAuthenticator() = default;
DummySpnegoAuthenticator::~DummySpnegoAuthenticator() = default;
void DummySpnegoAuthenticator::EnsureTestAccountExists() {
Java_DummySpnegoAuthenticator_ensureTestAccountExists(
base::android::AttachCurrentThread());
}
void DummySpnegoAuthenticator::RemoveTestAccounts() {
Java_DummySpnegoAuthenticator_removeTestAccounts(
base::android::AttachCurrentThread());
}
void DummySpnegoAuthenticator::ExpectSecurityContext(
const std::string& expected_package,
uint32_t response_code,
uint32_t minor_response_code,
const test::GssContextMockImpl& context_info,
const std::string& expected_input_token,
const std::string& output_token) {
SecurityContextQuery query(expected_package, response_code,
minor_response_code, context_info,
expected_input_token, output_token);
expected_security_queries_.push_back(query);
Java_DummySpnegoAuthenticator_setNativeAuthenticator(
base::android::AttachCurrentThread(), reinterpret_cast<intptr_t>(this));
}
long DummySpnegoAuthenticator::GetNextQuery(JNIEnv* /*env*/) {
CheckQueueNotEmpty();
current_query_ = expected_security_queries_.front();
expected_security_queries_.pop_front();
return reinterpret_cast<intptr_t>(&current_query_);
}
void DummySpnegoAuthenticator::CheckQueueNotEmpty() {
ASSERT_FALSE(expected_security_queries_.empty());
}
} // namespace android
} // namespace net

View File

@@ -0,0 +1,143 @@
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_ANDROID_DUMMY_SPNEGO_AUTHENTICATOR_H_
#define NET_ANDROID_DUMMY_SPNEGO_AUTHENTICATOR_H_
#include <jni.h>
#include <stdint.h>
#include <list>
#include <string>
#include "base/android/scoped_java_ref.h"
#include "base/memory/raw_ptr_exclusion.h"
// Provides an interface for controlling the DummySpnegoAuthenticator service.
// This includes a basic stub of the Mock GSSAPI library, so that OS independent
// Negotiate authentication tests can be run on Android.
namespace net {
// These constant values are arbitrary, and different from the real GSSAPI
// values, but must match those used in DummySpnegoAuthenticator.java
#define GSS_S_COMPLETE 0
#define GSS_S_CONTINUE_NEEDED 1
#define GSS_S_FAILURE 2
typedef struct gss_OID_desc_struct {
uint32_t length;
// This field is not a raw_ptr<> because it was filtered by the rewriter for:
// #global-scope
RAW_PTR_EXCLUSION void* elements;
} gss_OID_desc, *gss_OID;
extern gss_OID CHROME_GSS_SPNEGO_MECH_OID_DESC;
namespace test {
// Copy of class in Mock GSSAPI library.
class GssContextMockImpl {
public:
GssContextMockImpl();
GssContextMockImpl(const GssContextMockImpl& other);
GssContextMockImpl(const char* src_name,
const char* targ_name,
uint32_t lifetime_rec,
const gss_OID_desc& mech_type,
uint32_t ctx_flags,
int locally_initiated,
int open);
~GssContextMockImpl();
void Assign(const GssContextMockImpl& other);
std::string src_name;
std::string targ_name;
int32_t lifetime_rec;
gss_OID_desc mech_type;
int32_t ctx_flags;
int locally_initiated;
int open;
};
} // namespace test
namespace android {
// Interface to Java DummySpnegoAuthenticator.
class DummySpnegoAuthenticator {
public:
struct SecurityContextQuery {
SecurityContextQuery(const std::string& expected_package,
uint32_t response_code,
uint32_t minor_response_code,
const test::GssContextMockImpl& context_info,
const std::string& expected_input_token,
const std::string& output_token);
SecurityContextQuery(const std::string& expected_package,
uint32_t response_code,
uint32_t minor_response_code,
const test::GssContextMockImpl& context_info,
const char* expected_input_token,
const char* output_token);
SecurityContextQuery();
SecurityContextQuery(const SecurityContextQuery& other);
~SecurityContextQuery();
// Note that many of these fields only exist for compatibility with the
// non-Android version of the tests. Only the response_code and tokens are
// used or checked on Android.
std::string expected_package;
uint32_t response_code;
uint32_t minor_response_code;
test::GssContextMockImpl context_info;
std::string expected_input_token;
std::string output_token;
// Java callable members
base::android::ScopedJavaLocalRef<jstring> GetTokenToReturn(JNIEnv* env);
int GetResult(JNIEnv* env);
// Called from Java to check the arguments passed to the GetToken. Has to
// be in C++ since these tests are driven by googletest, and can only report
// failures through the googletest C++ API.
void CheckGetTokenArguments(
JNIEnv* env,
const base::android::JavaParamRef<jstring>& incoming_token);
};
DummySpnegoAuthenticator();
~DummySpnegoAuthenticator();
void ExpectSecurityContext(const std::string& expected_package,
uint32_t response_code,
uint32_t minor_response_code,
const test::GssContextMockImpl& context_info,
const std::string& expected_input_token,
const std::string& output_token);
static void EnsureTestAccountExists();
static void RemoveTestAccounts();
long GetNextQuery(JNIEnv* env);
private:
// Abandon the test if the query queue is empty. Has to be a void function to
// allow use of ASSERT_FALSE.
void CheckQueueNotEmpty();
std::list<SecurityContextQuery> expected_security_queries_;
// Needed to keep the current query alive once it has been pulled from the
// queue. This is simpler than transferring its ownership to Java.
SecurityContextQuery current_query_;
};
} // namespace android
using MockAuthLibrary = android::DummySpnegoAuthenticator;
} // namespace net
#endif // NET_ANDROID_DUMMY_SPNEGO_AUTHENTICATOR_H_

View File

@@ -0,0 +1,23 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/android/jni_string.h"
#include "net/net_jni_headers/GURLUtils_jni.h"
#include "url/gurl.h"
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
namespace net {
ScopedJavaLocalRef<jstring> JNI_GURLUtils_GetOrigin(
JNIEnv* env,
const JavaParamRef<jstring>& url) {
GURL host(base::android::ConvertJavaStringToUTF16(env, url));
return base::android::ConvertUTF8ToJavaString(
env, host.DeprecatedGetOriginAsURL().spec());
}
} // namespace net

View File

@@ -0,0 +1,169 @@
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/android/http_auth_negotiate_android.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/task/single_thread_task_runner.h"
#include "net/base/auth.h"
#include "net/base/net_errors.h"
#include "net/http/http_auth.h"
#include "net/http/http_auth_challenge_tokenizer.h"
#include "net/http/http_auth_multi_round_parse.h"
#include "net/http/http_auth_preferences.h"
#include "net/log/net_log_with_source.h"
#include "net/net_jni_headers/HttpNegotiateAuthenticator_jni.h"
using base::android::AttachCurrentThread;
using base::android::ConvertUTF8ToJavaString;
using base::android::ConvertJavaStringToUTF8;
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
namespace net::android {
JavaNegotiateResultWrapper::JavaNegotiateResultWrapper(
const scoped_refptr<base::TaskRunner>& callback_task_runner,
base::OnceCallback<void(int, const std::string&)> thread_safe_callback)
: callback_task_runner_(callback_task_runner),
thread_safe_callback_(std::move(thread_safe_callback)) {}
JavaNegotiateResultWrapper::~JavaNegotiateResultWrapper() = default;
void JavaNegotiateResultWrapper::SetResult(JNIEnv* env,
const JavaParamRef<jobject>& obj,
int result,
const JavaParamRef<jstring>& token) {
// This will be called on the UI thread, so we have to post a task back to the
// correct thread to actually save the result
std::string raw_token;
if (token.obj())
raw_token = ConvertJavaStringToUTF8(env, token);
// Always post, even if we are on the same thread. This guarantees that the
// result will be delayed until after the request has completed, which
// simplifies the logic. In practice the result will only ever come back on
// the original thread in an obscure error case.
callback_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(thread_safe_callback_), result, raw_token));
// We will always get precisely one call to set result for each call to
// getNextAuthToken, so we can now delete the callback object, and must
// do so to avoid a memory leak.
delete this;
}
HttpAuthNegotiateAndroid::HttpAuthNegotiateAndroid(
const HttpAuthPreferences* prefs)
: prefs_(prefs) {
JNIEnv* env = AttachCurrentThread();
java_authenticator_.Reset(Java_HttpNegotiateAuthenticator_create(
env, ConvertUTF8ToJavaString(env, GetAuthAndroidNegotiateAccountType())));
}
HttpAuthNegotiateAndroid::~HttpAuthNegotiateAndroid() = default;
bool HttpAuthNegotiateAndroid::Init(const NetLogWithSource& net_log) {
return true;
}
bool HttpAuthNegotiateAndroid::NeedsIdentity() const {
return false;
}
bool HttpAuthNegotiateAndroid::AllowsExplicitCredentials() const {
return false;
}
HttpAuth::AuthorizationResult HttpAuthNegotiateAndroid::ParseChallenge(
net::HttpAuthChallengeTokenizer* tok) {
if (first_challenge_) {
first_challenge_ = false;
return net::ParseFirstRoundChallenge(HttpAuth::AUTH_SCHEME_NEGOTIATE, tok);
}
std::string decoded_auth_token;
return net::ParseLaterRoundChallenge(HttpAuth::AUTH_SCHEME_NEGOTIATE, tok,
&server_auth_token_,
&decoded_auth_token);
}
int HttpAuthNegotiateAndroid::GenerateAuthTokenAndroid(
const AuthCredentials* credentials,
const std::string& spn,
const std::string& channel_bindings,
std::string* auth_token,
net::CompletionOnceCallback callback) {
return GenerateAuthToken(credentials, spn, channel_bindings, auth_token,
NetLogWithSource(), std::move(callback));
}
int HttpAuthNegotiateAndroid::GenerateAuthToken(
const AuthCredentials* credentials,
const std::string& spn,
const std::string& channel_bindings,
std::string* auth_token,
const NetLogWithSource& net_log,
net::CompletionOnceCallback callback) {
if (GetAuthAndroidNegotiateAccountType().empty()) {
// This can happen if there is a policy change, removing the account type,
// in the middle of a negotiation.
return ERR_UNSUPPORTED_AUTH_SCHEME;
}
DCHECK(auth_token);
DCHECK(completion_callback_.is_null());
DCHECK(!callback.is_null());
auth_token_ = auth_token;
completion_callback_ = std::move(callback);
scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner =
base::SingleThreadTaskRunner::GetCurrentDefault();
base::OnceCallback<void(int, const std::string&)> thread_safe_callback =
base::BindOnce(&HttpAuthNegotiateAndroid::SetResultInternal,
weak_factory_.GetWeakPtr());
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> java_server_auth_token =
ConvertUTF8ToJavaString(env, server_auth_token_);
ScopedJavaLocalRef<jstring> java_spn = ConvertUTF8ToJavaString(env, spn);
// It is intentional that callback_wrapper is not owned or deleted by the
// HttpAuthNegotiateAndroid object. The Java code will call the callback
// asynchronously on a different thread, and needs an object to call it on. As
// such, the callback_wrapper must not be deleted until the callback has been
// called, whatever happens to the HttpAuthNegotiateAndroid object.
//
// Unfortunately we have no automated way of managing C++ objects owned by
// Java, so the Java code must simply be written to guarantee that the
// callback is, in the end, called.
JavaNegotiateResultWrapper* callback_wrapper = new JavaNegotiateResultWrapper(
callback_task_runner, std::move(thread_safe_callback));
Java_HttpNegotiateAuthenticator_getNextAuthToken(
env, java_authenticator_, reinterpret_cast<intptr_t>(callback_wrapper),
java_spn, java_server_auth_token, can_delegate());
return ERR_IO_PENDING;
}
void HttpAuthNegotiateAndroid::SetDelegation(
HttpAuth::DelegationType delegation_type) {
DCHECK_NE(delegation_type, HttpAuth::DelegationType::kByKdcPolicy);
can_delegate_ = delegation_type == HttpAuth::DelegationType::kUnconstrained;
}
std::string HttpAuthNegotiateAndroid::GetAuthAndroidNegotiateAccountType()
const {
return prefs_->AuthAndroidNegotiateAccountType();
}
void HttpAuthNegotiateAndroid::SetResultInternal(int result,
const std::string& raw_token) {
DCHECK(auth_token_);
DCHECK(!completion_callback_.is_null());
if (result == OK)
*auth_token_ = "Negotiate " + raw_token;
std::move(completion_callback_).Run(result);
}
} // namespace net::android

View File

@@ -0,0 +1,127 @@
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_ANDROID_HTTP_AUTH_NEGOTIATE_ANDROID_H_
#define NET_ANDROID_HTTP_AUTH_NEGOTIATE_ANDROID_H_
#include <jni.h>
#include <memory>
#include <string>
#include "base/android/jni_android.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "net/base/completion_once_callback.h"
#include "net/base/net_export.h"
#include "net/http/http_auth.h"
#include "net/http/http_auth_mechanism.h"
namespace base {
class TaskRunner;
}
namespace net {
class HttpAuthChallengeTokenizer;
class HttpAuthPreferences;
namespace android {
// This class provides a threadsafe wrapper for SetResult, which is called from
// Java. A new instance of this class is needed for each call, and the instance
// destroys itself when the callback is received. It is written to allow
// setResult to be called on any thread, but in practice they will be called
// on the application's main thread.
//
// We cannot use a Callback object here, because there is no way of invoking the
// Run method from Java.
class NET_EXPORT_PRIVATE JavaNegotiateResultWrapper {
public:
scoped_refptr<base::TaskRunner> callback_task_runner_;
base::OnceCallback<void(int, const std::string&)> thread_safe_callback_;
JavaNegotiateResultWrapper(
const scoped_refptr<base::TaskRunner>& callback_task_runner,
base::OnceCallback<void(int, const std::string&)> thread_safe_callback);
void SetResult(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
int result,
const base::android::JavaParamRef<jstring>& token);
private:
// Class is only allowed to delete itself, nobody else is allowed to delete.
~JavaNegotiateResultWrapper();
};
// Class providing Negotiate (SPNEGO/Kerberos) authentication support on
// Android. The actual authentication is done through an Android authenticator
// provided by third parties who want Kerberos support. This class simply
// provides a bridge to the Java code, and hence to the service. See
// https://drive.google.com/open?id=1G7WAaYEKMzj16PTHT_cIYuKXJG6bBcrQ7QQBQ6ihOcQ&authuser=1
// for the full details.
class NET_EXPORT_PRIVATE HttpAuthNegotiateAndroid : public HttpAuthMechanism {
public:
// Creates an object for one negotiation session. |prefs| are the
// authentication preferences. In particular they include the Android account
// type, which is used to connect to the correct Android Authenticator.
explicit HttpAuthNegotiateAndroid(const HttpAuthPreferences* prefs);
HttpAuthNegotiateAndroid(const HttpAuthNegotiateAndroid&) = delete;
HttpAuthNegotiateAndroid& operator=(const HttpAuthNegotiateAndroid&) = delete;
~HttpAuthNegotiateAndroid() override;
// HttpAuthMechanism implementation:
bool Init(const NetLogWithSource& net_log) override;
bool NeedsIdentity() const override;
bool AllowsExplicitCredentials() const override;
HttpAuth::AuthorizationResult ParseChallenge(
HttpAuthChallengeTokenizer* tok) override;
int GenerateAuthToken(const AuthCredentials* credentials,
const std::string& spn,
const std::string& channel_bindings,
std::string* auth_token,
const NetLogWithSource& net_log,
CompletionOnceCallback callback) override;
void SetDelegation(HttpAuth::DelegationType delegation_type) override;
// Unlike the platform agnostic GenerateAuthToken(), the Android specific
// version doesn't require a NetLogWithSource. The call is made across service
// boundaries, so currently the goings-on within the GenerateAuthToken()
// handler is outside the scope of the NetLog.
int GenerateAuthTokenAndroid(const AuthCredentials* credentials,
const std::string& spn,
const std::string& channel_bindings,
std::string* auth_token,
CompletionOnceCallback callback);
bool can_delegate() const { return can_delegate_; }
void set_can_delegate(bool can_delegate) { can_delegate_ = can_delegate; }
const std::string& server_auth_token() const { return server_auth_token_; }
void set_server_auth_token(const std::string& server_auth_token) {
server_auth_token_ = server_auth_token;
}
std::string GetAuthAndroidNegotiateAccountType() const;
private:
void SetResultInternal(int result, const std::string& token);
raw_ptr<const HttpAuthPreferences> prefs_ = nullptr;
bool can_delegate_ = false;
bool first_challenge_ = true;
std::string server_auth_token_;
raw_ptr<std::string> auth_token_ = nullptr;
base::android::ScopedJavaGlobalRef<jobject> java_authenticator_;
net::CompletionOnceCallback completion_callback_;
base::WeakPtrFactory<HttpAuthNegotiateAndroid> weak_factory_{this};
};
} // namespace android
} // namespace net
#endif // NET_ANDROID_HTTP_AUTH_NEGOTIATE_ANDROID_H_

View File

@@ -0,0 +1,24 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net;
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@IntDef({
#define NET_ERROR(name, value) NetError.ERR_##name,
#include "net/base/net_error_list.h"
#undef NET_ERROR
NetError.OK
})
@Retention(RetentionPolicy.SOURCE)
public @interface NetError {
int OK = 0;
#define NET_ERROR(name, value) int ERR_##name = value;
#include "net/base/net_error_list.h"
#undef NET_ERROR
}

111
src/net/android/keystore.cc Normal file
View File

@@ -0,0 +1,111 @@
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/android/keystore.h"
#include <vector>
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/check.h"
#include "net/net_jni_headers/AndroidKeyStore_jni.h"
using base::android::AttachCurrentThread;
using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
using base::android::HasException;
using base::android::JavaByteArrayToByteVector;
using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;
using base::android::ToJavaByteArray;
namespace net::android {
std::string GetPrivateKeyClassName(const JavaRef<jobject>& key) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> name =
Java_AndroidKeyStore_getPrivateKeyClassName(env, key);
return ConvertJavaStringToUTF8(env, name);
}
bool PrivateKeySupportsSignature(const base::android::JavaRef<jobject>& key,
base::StringPiece algorithm) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> algorithm_ref =
ConvertUTF8ToJavaString(env, algorithm);
DCHECK(!algorithm_ref.is_null());
jboolean result =
Java_AndroidKeyStore_privateKeySupportsSignature(env, key, algorithm_ref);
return !HasException(env) && result;
}
bool PrivateKeySupportsCipher(const base::android::JavaRef<jobject>& key,
base::StringPiece algorithm) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> algorithm_ref =
ConvertUTF8ToJavaString(env, algorithm);
DCHECK(!algorithm_ref.is_null());
jboolean result =
Java_AndroidKeyStore_privateKeySupportsCipher(env, key, algorithm_ref);
return !HasException(env) && result;
}
bool SignWithPrivateKey(const JavaRef<jobject>& private_key_ref,
base::StringPiece algorithm,
base::span<const uint8_t> input,
std::vector<uint8_t>* signature) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> algorithm_ref =
ConvertUTF8ToJavaString(env, algorithm);
DCHECK(!algorithm_ref.is_null());
// Convert message to byte[] array.
ScopedJavaLocalRef<jbyteArray> input_ref = ToJavaByteArray(env, input);
DCHECK(!input_ref.is_null());
// Invoke platform API
ScopedJavaLocalRef<jbyteArray> signature_ref =
Java_AndroidKeyStore_signWithPrivateKey(env, private_key_ref,
algorithm_ref, input_ref);
if (HasException(env) || signature_ref.is_null())
return false;
// Write signature to string.
JavaByteArrayToByteVector(env, signature_ref, signature);
return true;
}
bool EncryptWithPrivateKey(const JavaRef<jobject>& private_key_ref,
base::StringPiece algorithm,
base::span<const uint8_t> input,
std::vector<uint8_t>* ciphertext) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> algorithm_ref =
ConvertUTF8ToJavaString(env, algorithm);
DCHECK(!algorithm_ref.is_null());
// Convert message to byte[] array.
ScopedJavaLocalRef<jbyteArray> input_ref = ToJavaByteArray(env, input);
DCHECK(!input_ref.is_null());
// Invoke platform API
ScopedJavaLocalRef<jbyteArray> ciphertext_ref =
Java_AndroidKeyStore_encryptWithPrivateKey(env, private_key_ref,
algorithm_ref, input_ref);
if (HasException(env) || ciphertext_ref.is_null())
return false;
// Write ciphertext to string.
JavaByteArrayToByteVector(env, ciphertext_ref, ciphertext);
return true;
}
} // namespace net::android

View File

@@ -0,0 +1,75 @@
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_ANDROID_KEYSTORE_H_
#define NET_ANDROID_KEYSTORE_H_
#include <jni.h>
#include <stdint.h>
#include <string>
#include <vector>
#include "base/android/scoped_java_ref.h"
#include "base/containers/span.h"
#include "base/strings/string_piece.h"
// Misc functions to access the Android platform KeyStore.
namespace net::android {
// Define a list of constants describing private key types. The
// values are shared with Java through org.chromium.net.PrivateKeyType.
// Example: PRIVATE_KEY_TYPE_RSA.
//
// A Java counterpart will be generated for this enum.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.net
enum PrivateKeyType {
PRIVATE_KEY_TYPE_RSA = 0,
// Obsolete: PRIVATE_KEY_TYPE_DSA = 1,
PRIVATE_KEY_TYPE_ECDSA = 2,
PRIVATE_KEY_TYPE_INVALID = 255,
};
// Returns the name of the class which implements the private key.
std::string GetPrivateKeyClassName(const base::android::JavaRef<jobject>& key);
// Returns whether |key| supports the signature algorithm |algorithm|.
bool PrivateKeySupportsSignature(const base::android::JavaRef<jobject>& key,
base::StringPiece algorithm);
// Returns whether |key| supports the encryption algorithm |algorithm|.
bool PrivateKeySupportsCipher(const base::android::JavaRef<jobject>& key,
base::StringPiece algorithm);
// Compute the signature of a given input using a private key. For more
// details, please read the comments for the signWithPrivateKey method in
// AndroidKeyStore.java.
//
// |private_key| is a JNI reference for the private key.
// |algorithm| is the name of the algorithm to sign.
// |input| is the input to sign.
// |signature| will receive the signature on success.
// Returns true on success, false on failure.
bool SignWithPrivateKey(const base::android::JavaRef<jobject>& private_key,
base::StringPiece algorithm,
base::span<const uint8_t> input,
std::vector<uint8_t>* signature);
// Encrypts a given input using a private key. For more details, please read the
// comments for the encryptWithPrivateKey method in AndroidKeyStore.java.
//
// |private_key| is a JNI reference for the private key.
// |algorithm| is the name of the algorithm to use.
// |input| is the input to encrypt.
// |ciphertext| will receive the ciphertext on success.
// Returns true on success, false on failure.
bool EncryptWithPrivateKey(const base::android::JavaRef<jobject>& private_key,
base::StringPiece algorithm,
base::span<const uint8_t> input,
std::vector<uint8_t>* ciphertext);
} // namespace net::android
#endif // NET_ANDROID_KEYSTORE_H_

View File

@@ -0,0 +1,274 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
////////////////////////////////////////////////////////////////////////////////
// Threading considerations:
//
// This class is designed to meet various threading guarantees starting from the
// ones imposed by NetworkChangeNotifier:
// - The notifier can be constructed on any thread.
// - GetCurrentConnectionType() can be called on any thread.
//
// The fact that this implementation of NetworkChangeNotifier is backed by a
// Java side singleton class (see NetworkChangeNotifier.java) adds another
// threading constraint:
// - The calls to the Java side (stateful) object must be performed from a
// single thread. This object happens to be a singleton which is used on the
// application side on the main thread. Therefore all the method calls from
// the native NetworkChangeNotifierAndroid class to its Java counterpart are
// performed on the main thread.
//
// This leads to a design involving the following native classes:
// 1) NetworkChangeNotifierFactoryAndroid ('factory')
// 2) NetworkChangeNotifierDelegateAndroid ('delegate')
// 3) NetworkChangeNotifierAndroid ('notifier')
//
// The factory constructs and owns the delegate. The factory is constructed and
// destroyed on the main thread which makes it construct and destroy the
// delegate on the main thread too. This guarantees that the calls to the Java
// side are performed on the main thread.
// Note that after the factory's construction, the factory's creation method can
// be called from any thread since the delegate's construction (performing the
// JNI calls) already happened on the main thread (when the factory was
// constructed).
//
////////////////////////////////////////////////////////////////////////////////
// Propagation of network change notifications:
//
// When the factory is requested to create a new instance of the notifier, the
// factory passes the delegate to the notifier (without transferring ownership).
// Note that there is a one-to-one mapping between the factory and the
// delegate as explained above. But the factory naturally creates multiple
// instances of the notifier. That means that there is a one-to-many mapping
// between delegate and notifier (i.e. a single delegate can be shared by
// multiple notifiers).
// At construction the notifier (which is also an observer) subscribes to
// notifications fired by the delegate. These notifications, received by the
// delegate (and forwarded to the notifier(s)), are sent by the Java side
// notifier (see NetworkChangeNotifier.java) and are initiated by the Android
// platform.
// Notifications from the Java side always arrive on the main thread. The
// delegate then forwards these notifications to the threads of each observer
// (network change notifier). The network change notifier then processes the
// state change, and notifies each of its observers on their threads.
//
// This can also be seen as:
// Android platform -> NetworkChangeNotifier (Java) ->
// NetworkChangeNotifierDelegateAndroid -> NetworkChangeNotifierAndroid.
#include "net/android/network_change_notifier_android.h"
#include <string>
#include <unordered_set>
#include "base/android/build_info.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread.h"
#include "net/base/address_tracker_linux.h"
namespace net {
// Expose handles::kInvalidNetworkHandle out to Java as NetId.INVALID. The
// notion of a NetID is an Android framework one, see android.net.Network.netId.
// NetworkChangeNotifierAndroid implements handles::NetworkHandle to simply be
// the NetID.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.net
enum NetId {
// Cannot use |handles::kInvalidNetworkHandle| here as the Java generator
// fails, instead enforce their equality with CHECK in
// NetworkChangeNotifierAndroid().
INVALID = -1
};
// Thread on which we can run DnsConfigService, which requires a TYPE_IO
// message loop to monitor /system/etc/hosts.
class NetworkChangeNotifierAndroid::BlockingThreadObjects {
public:
BlockingThreadObjects()
: address_tracker_(
base::DoNothing(),
base::DoNothing(),
// We're only interested in tunnel interface changes.
base::BindRepeating(NotifyNetworkChangeNotifierObservers),
std::unordered_set<std::string>()) {}
BlockingThreadObjects(const BlockingThreadObjects&) = delete;
BlockingThreadObjects& operator=(const BlockingThreadObjects&) = delete;
void Init() {
address_tracker_.Init();
}
static void NotifyNetworkChangeNotifierObservers() {
NetworkChangeNotifier::NotifyObserversOfIPAddressChange();
NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange();
}
private:
// Used to detect tunnel state changes.
internal::AddressTrackerLinux address_tracker_;
};
NetworkChangeNotifierAndroid::~NetworkChangeNotifierAndroid() {
ClearGlobalPointer();
delegate_->UnregisterObserver(this);
}
NetworkChangeNotifier::ConnectionType
NetworkChangeNotifierAndroid::GetCurrentConnectionType() const {
return delegate_->GetCurrentConnectionType();
}
NetworkChangeNotifier::ConnectionCost
NetworkChangeNotifierAndroid::GetCurrentConnectionCost() {
return delegate_->GetCurrentConnectionCost();
}
NetworkChangeNotifier::ConnectionSubtype
NetworkChangeNotifierAndroid::GetCurrentConnectionSubtype() const {
return delegate_->GetCurrentConnectionSubtype();
}
void NetworkChangeNotifierAndroid::GetCurrentMaxBandwidthAndConnectionType(
double* max_bandwidth_mbps,
ConnectionType* connection_type) const {
delegate_->GetCurrentMaxBandwidthAndConnectionType(max_bandwidth_mbps,
connection_type);
}
void NetworkChangeNotifierAndroid::ForceNetworkHandlesSupportedForTesting() {
force_network_handles_supported_for_testing_ = true;
}
bool NetworkChangeNotifierAndroid::AreNetworkHandlesCurrentlySupported() const {
// Notifications for API using handles::NetworkHandles and querying using
// handles::NetworkHandles only implemented for Android versions >= L.
return force_network_handles_supported_for_testing_ ||
(base::android::BuildInfo::GetInstance()->sdk_int() >=
base::android::SDK_VERSION_LOLLIPOP &&
!delegate_->RegisterNetworkCallbackFailed());
}
void NetworkChangeNotifierAndroid::GetCurrentConnectedNetworks(
NetworkChangeNotifier::NetworkList* networks) const {
delegate_->GetCurrentlyConnectedNetworks(networks);
}
NetworkChangeNotifier::ConnectionType
NetworkChangeNotifierAndroid::GetCurrentNetworkConnectionType(
handles::NetworkHandle network) const {
return delegate_->GetNetworkConnectionType(network);
}
handles::NetworkHandle NetworkChangeNotifierAndroid::GetCurrentDefaultNetwork()
const {
return delegate_->GetCurrentDefaultNetwork();
}
void NetworkChangeNotifierAndroid::OnConnectionTypeChanged() {
BlockingThreadObjects::NotifyNetworkChangeNotifierObservers();
}
void NetworkChangeNotifierAndroid::OnConnectionCostChanged() {
NetworkChangeNotifier::NotifyObserversOfConnectionCostChange();
}
void NetworkChangeNotifierAndroid::OnMaxBandwidthChanged(
double max_bandwidth_mbps,
ConnectionType type) {
NetworkChangeNotifier::NotifyObserversOfMaxBandwidthChange(max_bandwidth_mbps,
type);
}
void NetworkChangeNotifierAndroid::OnNetworkConnected(
handles::NetworkHandle network) {
NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange(
NetworkChangeType::kConnected, network);
}
void NetworkChangeNotifierAndroid::OnNetworkSoonToDisconnect(
handles::NetworkHandle network) {
NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange(
NetworkChangeType::kSoonToDisconnect, network);
}
void NetworkChangeNotifierAndroid::OnNetworkDisconnected(
handles::NetworkHandle network) {
NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange(
NetworkChangeType::kDisconnected, network);
}
void NetworkChangeNotifierAndroid::OnNetworkMadeDefault(
handles::NetworkHandle network) {
NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange(
NetworkChangeType::kMadeDefault, network);
}
void NetworkChangeNotifierAndroid::OnDefaultNetworkActive() {
NetworkChangeNotifier::NotifyObserversOfDefaultNetworkActive();
}
NetworkChangeNotifierAndroid::NetworkChangeNotifierAndroid(
NetworkChangeNotifierDelegateAndroid* delegate)
: NetworkChangeNotifier(NetworkChangeCalculatorParamsAndroid()),
delegate_(delegate),
blocking_thread_objects_(nullptr, base::OnTaskRunnerDeleter(nullptr)) {
static_assert(NetId::INVALID == handles::kInvalidNetworkHandle,
"handles::kInvalidNetworkHandle doesn't match NetId::INVALID");
delegate_->RegisterObserver(this);
// Since Android P, ConnectivityManager's signals include VPNs so we don't
// need to use AddressTrackerLinux.
if (base::android::BuildInfo::GetInstance()->sdk_int() <
base::android::SDK_VERSION_P) {
// |blocking_thread_objects_| will live on this runner.
scoped_refptr<base::SequencedTaskRunner> blocking_thread_runner =
base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()});
blocking_thread_objects_ =
std::unique_ptr<BlockingThreadObjects, base::OnTaskRunnerDeleter>(
new BlockingThreadObjects(),
// Ensure |blocking_thread_objects_| lives on
// |blocking_thread_runner| to prevent races where
// NetworkChangeNotifierAndroid outlives
// TaskEnvironment. https://crbug.com/938126
base::OnTaskRunnerDeleter(blocking_thread_runner));
blocking_thread_runner->PostTask(
FROM_HERE,
base::BindOnce(&BlockingThreadObjects::Init,
// The Unretained pointer is safe here because it's
// posted before the deleter can post.
base::Unretained(blocking_thread_objects_.get())));
}
}
// static
NetworkChangeNotifier::NetworkChangeCalculatorParams
NetworkChangeNotifierAndroid::NetworkChangeCalculatorParamsAndroid() {
NetworkChangeCalculatorParams params;
// IPAddressChanged is produced immediately prior to ConnectionTypeChanged
// so delay IPAddressChanged so they get merged with the following
// ConnectionTypeChanged signal.
params.ip_address_offline_delay_ = base::Seconds(1);
params.ip_address_online_delay_ = base::Seconds(1);
params.connection_type_offline_delay_ = base::Seconds(0);
params.connection_type_online_delay_ = base::Seconds(0);
return params;
}
bool NetworkChangeNotifierAndroid::IsDefaultNetworkActiveInternal() {
return delegate_->IsDefaultNetworkActive();
}
void NetworkChangeNotifierAndroid::DefaultNetworkActiveObserverAdded() {
delegate_->DefaultNetworkActiveObserverAdded();
}
void NetworkChangeNotifierAndroid::DefaultNetworkActiveObserverRemoved() {
delegate_->DefaultNetworkActiveObserverRemoved();
}
} // namespace net

View File

@@ -0,0 +1,126 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_ANDROID_NETWORK_CHANGE_NOTIFIER_ANDROID_H_
#define NET_ANDROID_NETWORK_CHANGE_NOTIFIER_ANDROID_H_
#include <memory>
#include "base/android/jni_android.h"
#include "base/compiler_specific.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "net/android/network_change_notifier_delegate_android.h"
#include "net/base/net_export.h"
#include "net/base/network_change_notifier.h"
#include "net/base/network_handle.h"
namespace base {
struct OnTaskRunnerDeleter;
} // namespace base
namespace net {
class NetworkChangeNotifierAndroidTest;
class NetworkChangeNotifierFactoryAndroid;
// NetworkChangeNotifierAndroid observes network events from the Android
// notification system and forwards them to observers.
//
// The implementation is complicated by the differing lifetime and thread
// affinity requirements of Android notifications and of NetworkChangeNotifier.
//
// High-level overview:
// NetworkChangeNotifier.java - Receives notifications from Android system, and
// notifies native code via JNI (on the main application thread).
// NetworkChangeNotifierDelegateAndroid ('Delegate') - Listens for notifications
// sent via JNI on the main application thread, and forwards them to observers
// on their threads. Owned by Factory, lives exclusively on main application
// thread.
// NetworkChangeNotifierFactoryAndroid ('Factory') - Creates the Delegate on the
// main thread to receive JNI events, and vends Notifiers. Lives exclusively
// on main application thread, and outlives all other classes.
// NetworkChangeNotifierAndroid ('Notifier') - Receives event notifications from
// the Delegate. Processes and forwards these events to the
// NetworkChangeNotifier observers on their threads. May live on any thread
// and be called by any thread.
//
// For more details, see the implementation file.
//
// Note: Alongside of NetworkChangeNotifier.java there is
// NetworkActiveNotifier.java, which handles notifications for when the system
// default network goes in to a high power state. These are handled separately
// since listening to them is expensive (they are fired often) and currently
// only bidi streams connection status check uses them.
class NET_EXPORT_PRIVATE NetworkChangeNotifierAndroid
: public NetworkChangeNotifier,
public NetworkChangeNotifierDelegateAndroid::Observer {
public:
NetworkChangeNotifierAndroid(const NetworkChangeNotifierAndroid&) = delete;
NetworkChangeNotifierAndroid& operator=(const NetworkChangeNotifierAndroid&) =
delete;
~NetworkChangeNotifierAndroid() override;
// NetworkChangeNotifier:
ConnectionType GetCurrentConnectionType() const override;
ConnectionCost GetCurrentConnectionCost() override;
// Requires ACCESS_WIFI_STATE permission in order to provide precise WiFi link
// speed.
void GetCurrentMaxBandwidthAndConnectionType(
double* max_bandwidth_mbps,
ConnectionType* connection_type) const override;
bool AreNetworkHandlesCurrentlySupported() const override;
void GetCurrentConnectedNetworks(NetworkList* network_list) const override;
ConnectionType GetCurrentNetworkConnectionType(
handles::NetworkHandle network) const override;
NetworkChangeNotifier::ConnectionSubtype GetCurrentConnectionSubtype()
const override;
handles::NetworkHandle GetCurrentDefaultNetwork() const override;
bool IsDefaultNetworkActiveInternal() override;
// NetworkChangeNotifierDelegateAndroid::Observer:
void OnConnectionTypeChanged() override;
void OnConnectionCostChanged() override;
void OnMaxBandwidthChanged(double max_bandwidth_mbps,
ConnectionType type) override;
void OnNetworkConnected(handles::NetworkHandle network) override;
void OnNetworkSoonToDisconnect(handles::NetworkHandle network) override;
void OnNetworkDisconnected(handles::NetworkHandle network) override;
void OnNetworkMadeDefault(handles::NetworkHandle network) override;
void OnDefaultNetworkActive() override;
// Promote GetMaxBandwidthMbpsForConnectionSubtype to public for the Android
// delegate class.
using NetworkChangeNotifier::GetMaxBandwidthMbpsForConnectionSubtype;
static NetworkChangeCalculatorParams NetworkChangeCalculatorParamsAndroid();
void DefaultNetworkActiveObserverAdded() override;
void DefaultNetworkActiveObserverRemoved() override;
private:
friend class NetworkChangeNotifierAndroidTest;
friend class NetworkChangeNotifierFactoryAndroid;
class BlockingThreadObjects;
// Enable handles::NetworkHandles support for tests.
void ForceNetworkHandlesSupportedForTesting();
explicit NetworkChangeNotifierAndroid(
NetworkChangeNotifierDelegateAndroid* delegate);
const raw_ptr<NetworkChangeNotifierDelegateAndroid> delegate_;
// A collection of objects that must live on blocking sequences. These objects
// listen for notifications and relay the notifications to the registered
// observers without posting back to the thread the object was created on.
// Also used for DnsConfigService which also must live on blocking sequences.
std::unique_ptr<BlockingThreadObjects, base::OnTaskRunnerDeleter>
blocking_thread_objects_;
bool force_network_handles_supported_for_testing_ = false;
};
} // namespace net
#endif // NET_ANDROID_NETWORK_CHANGE_NOTIFIER_ANDROID_H_

View File

@@ -0,0 +1,499 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/android/network_change_notifier_delegate_android.h"
#include "base/android/build_info.h"
#include "base/android/jni_array.h"
#include "base/check.h"
#include "base/notreached.h"
#include "net/android/network_change_notifier_android.h"
#include "net/net_jni_headers/NetworkActiveNotifier_jni.h"
#include "net/net_jni_headers/NetworkChangeNotifier_jni.h"
using base::android::JavaParamRef;
using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;
namespace net {
namespace {
// Converts a Java side connection type (integer) to
// the native side NetworkChangeNotifier::ConnectionType.
NetworkChangeNotifier::ConnectionType ConvertConnectionType(
jint connection_type) {
switch (connection_type) {
case NetworkChangeNotifier::CONNECTION_UNKNOWN:
case NetworkChangeNotifier::CONNECTION_ETHERNET:
case NetworkChangeNotifier::CONNECTION_WIFI:
case NetworkChangeNotifier::CONNECTION_2G:
case NetworkChangeNotifier::CONNECTION_3G:
case NetworkChangeNotifier::CONNECTION_4G:
case NetworkChangeNotifier::CONNECTION_5G:
case NetworkChangeNotifier::CONNECTION_NONE:
case NetworkChangeNotifier::CONNECTION_BLUETOOTH:
break;
default:
NOTREACHED() << "Unknown connection type received: " << connection_type;
return NetworkChangeNotifier::CONNECTION_UNKNOWN;
}
return static_cast<NetworkChangeNotifier::ConnectionType>(connection_type);
}
// Converts a Java side connection cost (integer) to
// the native side NetworkChangeNotifier::ConnectionCost.
NetworkChangeNotifier::ConnectionCost ConvertConnectionCost(
jint connection_cost) {
switch (connection_cost) {
case NetworkChangeNotifier::CONNECTION_COST_UNKNOWN:
case NetworkChangeNotifier::CONNECTION_COST_UNMETERED:
case NetworkChangeNotifier::CONNECTION_COST_METERED:
break;
default:
NOTREACHED() << "Unknown connection cost received: " << connection_cost;
return NetworkChangeNotifier::CONNECTION_COST_UNKNOWN;
}
return static_cast<NetworkChangeNotifier::ConnectionCost>(connection_cost);
}
// Converts a Java side connection type (integer) to
// the native side NetworkChangeNotifier::ConnectionType.
NetworkChangeNotifier::ConnectionSubtype ConvertConnectionSubtype(
jint subtype) {
DCHECK(subtype >= 0 && subtype <= NetworkChangeNotifier::SUBTYPE_LAST);
return static_cast<NetworkChangeNotifier::ConnectionSubtype>(subtype);
}
} // namespace
// static
void NetworkChangeNotifierDelegateAndroid::JavaLongArrayToNetworkMap(
JNIEnv* env,
const JavaRef<jlongArray>& long_array,
NetworkMap* network_map) {
std::vector<int64_t> int64_list;
base::android::JavaLongArrayToInt64Vector(env, long_array, &int64_list);
network_map->clear();
for (auto i = int64_list.begin(); i != int64_list.end(); ++i) {
handles::NetworkHandle network_handle = *i;
CHECK(++i != int64_list.end());
(*network_map)[network_handle] = static_cast<ConnectionType>(*i);
}
}
NetworkChangeNotifierDelegateAndroid::NetworkChangeNotifierDelegateAndroid()
: java_network_change_notifier_(Java_NetworkChangeNotifier_init(
base::android::AttachCurrentThread())),
register_network_callback_failed_(
Java_NetworkChangeNotifier_registerNetworkCallbackFailed(
base::android::AttachCurrentThread(),
java_network_change_notifier_)) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_addNativeObserver(
env, java_network_change_notifier_, reinterpret_cast<intptr_t>(this));
SetCurrentConnectionType(
ConvertConnectionType(Java_NetworkChangeNotifier_getCurrentConnectionType(
env, java_network_change_notifier_)));
SetCurrentConnectionCost(
ConvertConnectionCost(Java_NetworkChangeNotifier_getCurrentConnectionCost(
env, java_network_change_notifier_)));
SetCurrentMaxBandwidth(
NetworkChangeNotifierAndroid::GetMaxBandwidthMbpsForConnectionSubtype(
GetCurrentConnectionSubtype()));
SetCurrentDefaultNetwork(Java_NetworkChangeNotifier_getCurrentDefaultNetId(
env, java_network_change_notifier_));
NetworkMap network_map;
ScopedJavaLocalRef<jlongArray> networks_and_types =
Java_NetworkChangeNotifier_getCurrentNetworksAndTypes(
env, java_network_change_notifier_);
JavaLongArrayToNetworkMap(env, networks_and_types, &network_map);
SetCurrentNetworksAndTypes(network_map);
java_network_active_notifier_ = Java_NetworkActiveNotifier_build(
base::android::AttachCurrentThread(), reinterpret_cast<intptr_t>(this));
}
NetworkChangeNotifierDelegateAndroid::~NetworkChangeNotifierDelegateAndroid() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK_EQ(default_network_active_observers_, 0);
{
base::AutoLock auto_lock(observer_lock_);
DCHECK(!observer_);
}
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_removeNativeObserver(
env, java_network_change_notifier_, reinterpret_cast<intptr_t>(this));
}
NetworkChangeNotifier::ConnectionType
NetworkChangeNotifierDelegateAndroid::GetCurrentConnectionType() const {
base::AutoLock auto_lock(connection_lock_);
return connection_type_;
}
NetworkChangeNotifier::ConnectionCost
NetworkChangeNotifierDelegateAndroid::GetCurrentConnectionCost() {
base::AutoLock auto_lock(connection_lock_);
return connection_cost_;
}
NetworkChangeNotifier::ConnectionSubtype
NetworkChangeNotifierDelegateAndroid::GetCurrentConnectionSubtype() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return ConvertConnectionSubtype(
Java_NetworkChangeNotifier_getCurrentConnectionSubtype(
base::android::AttachCurrentThread(), java_network_change_notifier_));
}
void NetworkChangeNotifierDelegateAndroid::
GetCurrentMaxBandwidthAndConnectionType(
double* max_bandwidth_mbps,
ConnectionType* connection_type) const {
base::AutoLock auto_lock(connection_lock_);
*connection_type = connection_type_;
*max_bandwidth_mbps = connection_max_bandwidth_;
}
NetworkChangeNotifier::ConnectionType
NetworkChangeNotifierDelegateAndroid::GetNetworkConnectionType(
handles::NetworkHandle network) const {
base::AutoLock auto_lock(connection_lock_);
auto network_entry = network_map_.find(network);
if (network_entry == network_map_.end())
return ConnectionType::CONNECTION_UNKNOWN;
return network_entry->second;
}
handles::NetworkHandle
NetworkChangeNotifierDelegateAndroid::GetCurrentDefaultNetwork() const {
base::AutoLock auto_lock(connection_lock_);
return default_network_;
}
void NetworkChangeNotifierDelegateAndroid::GetCurrentlyConnectedNetworks(
NetworkList* network_list) const {
network_list->clear();
base::AutoLock auto_lock(connection_lock_);
for (auto i : network_map_)
network_list->push_back(i.first);
}
bool NetworkChangeNotifierDelegateAndroid::IsDefaultNetworkActive() {
JNIEnv* env = base::android::AttachCurrentThread();
return Java_NetworkActiveNotifier_isDefaultNetworkActive(
env, java_network_active_notifier_);
}
void NetworkChangeNotifierDelegateAndroid::NotifyConnectionCostChanged(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
jint new_connection_cost) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
const ConnectionCost actual_connection_cost =
ConvertConnectionCost(new_connection_cost);
SetCurrentConnectionCost(actual_connection_cost);
base::AutoLock auto_lock(observer_lock_);
if (observer_)
observer_->OnConnectionCostChanged();
}
void NetworkChangeNotifierDelegateAndroid::NotifyConnectionTypeChanged(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
jint new_connection_type,
jlong default_netid) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
const ConnectionType actual_connection_type = ConvertConnectionType(
new_connection_type);
SetCurrentConnectionType(actual_connection_type);
handles::NetworkHandle default_network = default_netid;
if (default_network != GetCurrentDefaultNetwork()) {
SetCurrentDefaultNetwork(default_network);
bool default_exists;
{
base::AutoLock auto_lock(connection_lock_);
// |default_network| may be an invalid value (i.e. -1) in cases where
// the device is disconnected or when run on Android versions prior to L,
// in which case |default_exists| will correctly be false and no
// OnNetworkMadeDefault notification will be sent.
default_exists = network_map_.find(default_network) != network_map_.end();
}
// Android Lollipop had race conditions where CONNECTIVITY_ACTION intents
// were sent out before the network was actually made the default.
// Delay sending the OnNetworkMadeDefault notification until we are
// actually notified that the network connected in NotifyOfNetworkConnect.
if (default_exists) {
base::AutoLock auto_lock(observer_lock_);
if (observer_)
observer_->OnNetworkMadeDefault(default_network);
}
}
base::AutoLock auto_lock(observer_lock_);
if (observer_)
observer_->OnConnectionTypeChanged();
}
jint NetworkChangeNotifierDelegateAndroid::GetConnectionType(JNIEnv*,
jobject) const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return GetCurrentConnectionType();
}
jint NetworkChangeNotifierDelegateAndroid::GetConnectionCost(JNIEnv*, jobject) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return GetCurrentConnectionCost();
}
void NetworkChangeNotifierDelegateAndroid::NotifyMaxBandwidthChanged(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
jint subtype) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
double new_max_bandwidth =
NetworkChangeNotifierAndroid::GetMaxBandwidthMbpsForConnectionSubtype(
ConvertConnectionSubtype(subtype));
SetCurrentMaxBandwidth(new_max_bandwidth);
const ConnectionType connection_type = GetCurrentConnectionType();
base::AutoLock auto_lock(observer_lock_);
if (observer_) {
observer_->OnMaxBandwidthChanged(new_max_bandwidth, connection_type);
}
}
void NetworkChangeNotifierDelegateAndroid::NotifyOfNetworkConnect(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
jlong net_id,
jint connection_type) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
handles::NetworkHandle network = net_id;
bool already_exists;
bool is_default_network;
{
base::AutoLock auto_lock(connection_lock_);
already_exists = network_map_.find(network) != network_map_.end();
network_map_[network] = static_cast<ConnectionType>(connection_type);
is_default_network = (network == default_network_);
}
// Android Lollipop would send many duplicate notifications.
// This was later fixed in Android Marshmallow.
// Deduplicate them here by avoiding sending duplicate notifications.
if (!already_exists) {
base::AutoLock auto_lock(observer_lock_);
if (observer_) {
observer_->OnNetworkConnected(network);
if (is_default_network)
observer_->OnNetworkMadeDefault(network);
}
}
}
void NetworkChangeNotifierDelegateAndroid::NotifyOfNetworkSoonToDisconnect(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
jlong net_id) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
handles::NetworkHandle network = net_id;
{
base::AutoLock auto_lock(connection_lock_);
if (network_map_.find(network) == network_map_.end())
return;
}
base::AutoLock auto_lock(observer_lock_);
if (observer_)
observer_->OnNetworkSoonToDisconnect(network);
}
void NetworkChangeNotifierDelegateAndroid::NotifyOfNetworkDisconnect(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
jlong net_id) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
handles::NetworkHandle network = net_id;
{
base::AutoLock auto_lock(connection_lock_);
if (network == default_network_)
default_network_ = handles::kInvalidNetworkHandle;
if (network_map_.erase(network) == 0)
return;
}
base::AutoLock auto_lock(observer_lock_);
if (observer_)
observer_->OnNetworkDisconnected(network);
}
void NetworkChangeNotifierDelegateAndroid::NotifyPurgeActiveNetworkList(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
const JavaParamRef<jlongArray>& active_networks) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
NetworkList active_network_list;
base::android::JavaLongArrayToInt64Vector(env, active_networks,
&active_network_list);
NetworkList disconnected_networks;
{
base::AutoLock auto_lock(connection_lock_);
for (auto i : network_map_) {
bool found = false;
for (auto j : active_network_list) {
if (j == i.first) {
found = true;
break;
}
}
if (!found) {
disconnected_networks.push_back(i.first);
}
}
}
for (auto disconnected_network : disconnected_networks)
NotifyOfNetworkDisconnect(env, obj, disconnected_network);
}
void NetworkChangeNotifierDelegateAndroid::NotifyOfDefaultNetworkActive(
JNIEnv* env) {
base::AutoLock auto_lock(observer_lock_);
if (observer_)
observer_->OnDefaultNetworkActive();
}
void NetworkChangeNotifierDelegateAndroid::RegisterObserver(
Observer* observer) {
base::AutoLock auto_lock(observer_lock_);
DCHECK(!observer_);
observer_ = observer;
}
void NetworkChangeNotifierDelegateAndroid::UnregisterObserver(
Observer* observer) {
base::AutoLock auto_lock(observer_lock_);
DCHECK_EQ(observer_, observer);
observer_ = nullptr;
}
void NetworkChangeNotifierDelegateAndroid::DefaultNetworkActiveObserverAdded() {
if (default_network_active_observers_.fetch_add(1) == 0)
EnableDefaultNetworkActiveNotifications();
}
void NetworkChangeNotifierDelegateAndroid::
DefaultNetworkActiveObserverRemoved() {
if (default_network_active_observers_.fetch_sub(1) == 1)
DisableDefaultNetworkActiveNotifications();
}
void NetworkChangeNotifierDelegateAndroid::
EnableDefaultNetworkActiveNotifications() {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkActiveNotifier_enableNotifications(env,
java_network_active_notifier_);
}
void NetworkChangeNotifierDelegateAndroid::
DisableDefaultNetworkActiveNotifications() {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkActiveNotifier_disableNotifications(
env, java_network_active_notifier_);
}
void NetworkChangeNotifierDelegateAndroid::SetCurrentConnectionType(
ConnectionType new_connection_type) {
base::AutoLock auto_lock(connection_lock_);
connection_type_ = new_connection_type;
}
void NetworkChangeNotifierDelegateAndroid::SetCurrentConnectionCost(
ConnectionCost new_connection_cost) {
base::AutoLock auto_lock(connection_lock_);
connection_cost_ = new_connection_cost;
}
void NetworkChangeNotifierDelegateAndroid::SetCurrentMaxBandwidth(
double max_bandwidth) {
base::AutoLock auto_lock(connection_lock_);
connection_max_bandwidth_ = max_bandwidth;
}
void NetworkChangeNotifierDelegateAndroid::SetCurrentDefaultNetwork(
handles::NetworkHandle default_network) {
base::AutoLock auto_lock(connection_lock_);
default_network_ = default_network;
}
void NetworkChangeNotifierDelegateAndroid::SetCurrentNetworksAndTypes(
NetworkMap network_map) {
base::AutoLock auto_lock(connection_lock_);
network_map_ = network_map;
}
void NetworkChangeNotifierDelegateAndroid::SetOnline() {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_forceConnectivityState(env, true);
}
void NetworkChangeNotifierDelegateAndroid::SetOffline() {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_forceConnectivityState(env, false);
}
void NetworkChangeNotifierDelegateAndroid::FakeNetworkConnected(
handles::NetworkHandle network,
ConnectionType type) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_fakeNetworkConnected(env, network, type);
}
void NetworkChangeNotifierDelegateAndroid::FakeNetworkSoonToBeDisconnected(
handles::NetworkHandle network) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_fakeNetworkSoonToBeDisconnected(env, network);
}
void NetworkChangeNotifierDelegateAndroid::FakeNetworkDisconnected(
handles::NetworkHandle network) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_fakeNetworkDisconnected(env, network);
}
void NetworkChangeNotifierDelegateAndroid::FakePurgeActiveNetworkList(
NetworkChangeNotifier::NetworkList networks) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_fakePurgeActiveNetworkList(
env, base::android::ToJavaLongArray(env, networks));
}
void NetworkChangeNotifierDelegateAndroid::FakeDefaultNetwork(
handles::NetworkHandle network,
ConnectionType type) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_fakeDefaultNetwork(env, network, type);
}
void NetworkChangeNotifierDelegateAndroid::FakeConnectionCostChanged(
ConnectionCost cost) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_fakeConnectionCostChanged(env, cost);
}
void NetworkChangeNotifierDelegateAndroid::FakeConnectionSubtypeChanged(
ConnectionSubtype subtype) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_fakeConnectionSubtypeChanged(env, subtype);
}
void NetworkChangeNotifierDelegateAndroid::FakeDefaultNetworkActive() {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkActiveNotifier_fakeDefaultNetworkActive(
env, java_network_active_notifier_);
}
void NetworkChangeNotifierDelegateAndroid::
EnableNetworkChangeNotifierAutoDetectForTest() {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_setAutoDetectConnectivityState(env, true);
}
} // namespace net

View File

@@ -0,0 +1,233 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_ANDROID_NETWORK_CHANGE_NOTIFIER_DELEGATE_ANDROID_H_
#define NET_ANDROID_NETWORK_CHANGE_NOTIFIER_DELEGATE_ANDROID_H_
#include <atomic>
#include <map>
#include "base/android/jni_android.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list_threadsafe.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "net/base/net_export.h"
#include "net/base/network_change_notifier.h"
#include "net/base/network_handle.h"
namespace net {
// Delegate used to thread-safely notify NetworkChangeNotifierAndroid whenever a
// network connection change notification is signaled by the Java side (on the
// JNI thread).
// All the methods exposed below must be called exclusively on the JNI thread
// unless otherwise stated (e.g. RegisterObserver()/UnregisterObserver()).
class NET_EXPORT_PRIVATE NetworkChangeNotifierDelegateAndroid {
public:
typedef NetworkChangeNotifier::ConnectionCost ConnectionCost;
typedef NetworkChangeNotifier::ConnectionType ConnectionType;
typedef NetworkChangeNotifier::ConnectionSubtype ConnectionSubtype;
typedef NetworkChangeNotifier::NetworkList NetworkList;
// Observer interface implemented by NetworkChangeNotifierAndroid which
// subscribes to network change notifications fired by the delegate (and
// initiated by the Java side).
class Observer : public NetworkChangeNotifier::NetworkObserver {
public:
~Observer() override = default;
// Updates the current connection type.
virtual void OnConnectionTypeChanged() = 0;
// Updates the current connection cost.
virtual void OnConnectionCostChanged() = 0;
// Updates the current max bandwidth.
virtual void OnMaxBandwidthChanged(double max_bandwidth_mbps,
ConnectionType connection_type) = 0;
// Notifies that the default network has gone into a high power mode.
virtual void OnDefaultNetworkActive() = 0;
};
// Initializes native (C++) side of NetworkChangeNotifierAndroid that
// communicates with Java NetworkChangeNotifier class. The Java
// NetworkChangeNotifier must have been previously initialized with calls
// like this:
// // Creates global singleton Java NetworkChangeNotifier class instance.
// NetworkChangeNotifier.init();
// // Creates Java NetworkChangeNotifierAutoDetect class instance.
// NetworkChangeNotifier.registerToReceiveNotificationsAlways();
NetworkChangeNotifierDelegateAndroid();
NetworkChangeNotifierDelegateAndroid(
const NetworkChangeNotifierDelegateAndroid&) = delete;
NetworkChangeNotifierDelegateAndroid& operator=(
const NetworkChangeNotifierDelegateAndroid&) = delete;
~NetworkChangeNotifierDelegateAndroid();
// Called from NetworkChangeNotifier.java on the JNI thread whenever
// the connection type changes. This updates the current connection type seen
// by this class and forwards the notification to the observers that
// subscribed through RegisterObserver().
void NotifyConnectionTypeChanged(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
jint new_connection_type,
jlong default_netid);
jint GetConnectionType(JNIEnv* env, jobject obj) const;
// Called from NetworkChangeNotifier.java on the JNI thread whenever
// the connection cost changes. This updates the current connection cost seen
// by this class and forwards the notification to the observers that
// subscribed through RegisterObserver().
void NotifyConnectionCostChanged(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
jint new_connection_cost);
jint GetConnectionCost(JNIEnv* env, jobject obj);
// Called from NetworkChangeNotifier.java on the JNI thread whenever
// the maximum bandwidth of the connection changes. This updates the current
// max bandwidth seen by this class and forwards the notification to the
// observers that subscribed through RegisterObserver().
void NotifyMaxBandwidthChanged(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
jint subtype);
// Called from NetworkChangeNotifier.java on the JNI thread to push
// down notifications of network connectivity events. These functions in
// turn:
// 1) Update |network_map_| and |default_network_|.
// 2) Push notifications to NetworkChangeNotifier which in turn pushes
// notifications to its NetworkObservers. Note that these functions
// perform valuable transformations on the signals like deduplicating.
// For descriptions of what individual calls mean, see
// NetworkChangeNotifierAutoDetect.Observer functions of the same names.
void NotifyOfNetworkConnect(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
jlong net_id,
jint connection_type);
void NotifyOfNetworkSoonToDisconnect(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
jlong net_id);
void NotifyOfNetworkDisconnect(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
jlong net_id);
void NotifyPurgeActiveNetworkList(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
const base::android::JavaParamRef<jlongArray>& active_networks);
// Called from NetworkActiveNotifier.java on the JNI thread to push down
// notifications of default network going in to high power mode.
void NotifyOfDefaultNetworkActive(JNIEnv* env);
// Registers/unregisters the observer which receives notifications from this
// delegate. Notifications may be dispatched to the observer from any thread.
// |observer| must not invoke (Register|Unregister)Observer() when receiving a
// notification, because it would cause a reentrant lock acquisition.
// |observer| must unregister itself before
// ~NetworkChangeNotifierDelegateAndroid().
void RegisterObserver(Observer* observer);
void UnregisterObserver(Observer* observer);
// Called by NetworkChangeNotifierAndroid to report when a
// DefaultNetworkActiveObserver has been added (or removed) so that the
// delegate can act on that (possibly enabling or disabling default network
// active notifications).
void DefaultNetworkActiveObserverRemoved();
void DefaultNetworkActiveObserverAdded();
// These methods are simply implementations of NetworkChangeNotifier APIs of
// the same name. They can be called from any thread.
ConnectionCost GetCurrentConnectionCost();
ConnectionType GetCurrentConnectionType() const;
void GetCurrentMaxBandwidthAndConnectionType(
double* max_bandwidth_mbps,
ConnectionType* connection_type) const;
ConnectionType GetNetworkConnectionType(handles::NetworkHandle network) const;
handles::NetworkHandle GetCurrentDefaultNetwork() const;
void GetCurrentlyConnectedNetworks(NetworkList* network_list) const;
bool IsDefaultNetworkActive();
// Can only be called from the main (Java) thread.
NetworkChangeNotifier::ConnectionSubtype GetCurrentConnectionSubtype() const;
// Returns true if NetworkCallback failed to register, indicating that
// network-specific callbacks will not be issued.
bool RegisterNetworkCallbackFailed() const {
return register_network_callback_failed_;
}
static void EnableNetworkChangeNotifierAutoDetectForTest();
private:
friend class BaseNetworkChangeNotifierAndroidTest;
// Map of active connected networks and their connection type.
typedef std::map<handles::NetworkHandle, ConnectionType> NetworkMap;
// Converts a Java long[] into a NetworkMap. Expects long[] to contain
// repeated instances of: handles::NetworkHandle, ConnectionType
static void JavaLongArrayToNetworkMap(
JNIEnv* env,
const base::android::JavaRef<jlongArray>& long_array,
NetworkMap* network_map);
// These can be selectively enabled/disabled as they might be expensive to
// listen to since they could be fired often.
void EnableDefaultNetworkActiveNotifications();
void DisableDefaultNetworkActiveNotifications();
// Setters that grab appropriate lock.
void SetCurrentConnectionCost(ConnectionCost connection_cost);
void SetCurrentConnectionType(ConnectionType connection_type);
void SetCurrentMaxBandwidth(double max_bandwidth);
void SetCurrentDefaultNetwork(handles::NetworkHandle default_network);
void SetCurrentNetworksAndTypes(NetworkMap network_map);
// Methods calling the Java side exposed for testing.
void SetOnline();
void SetOffline();
void FakeNetworkConnected(handles::NetworkHandle network,
ConnectionType type);
void FakeNetworkSoonToBeDisconnected(handles::NetworkHandle network);
void FakeNetworkDisconnected(handles::NetworkHandle network);
void FakePurgeActiveNetworkList(NetworkList networks);
void FakeDefaultNetwork(handles::NetworkHandle network, ConnectionType type);
void FakeConnectionCostChanged(ConnectionCost cost);
void FakeConnectionSubtypeChanged(ConnectionSubtype subtype);
void FakeDefaultNetworkActive();
THREAD_CHECKER(thread_checker_);
base::Lock observer_lock_;
raw_ptr<Observer> observer_ GUARDED_BY(observer_lock_) = nullptr;
const base::android::ScopedJavaGlobalRef<jobject>
java_network_change_notifier_;
// True if NetworkCallback failed to register, indicating that
// network-specific callbacks will not be issued.
const bool register_network_callback_failed_;
base::android::ScopedJavaGlobalRef<jobject> java_network_active_notifier_;
mutable base::Lock connection_lock_; // Protects the state below.
ConnectionType connection_type_;
ConnectionCost connection_cost_;
double connection_max_bandwidth_;
handles::NetworkHandle default_network_;
NetworkMap network_map_;
// Used to enable/disable default network active notifications on the Java
// side.
std::atomic_int default_network_active_observers_ = 0;
};
} // namespace net
#endif // NET_ANDROID_NETWORK_CHANGE_NOTIFIER_DELEGATE_ANDROID_H_

View File

@@ -0,0 +1,25 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/android/network_change_notifier_factory_android.h"
#include "base/memory/ptr_util.h"
#include "net/android/network_change_notifier_android.h"
namespace net {
NetworkChangeNotifierFactoryAndroid::NetworkChangeNotifierFactoryAndroid() =
default;
NetworkChangeNotifierFactoryAndroid::~NetworkChangeNotifierFactoryAndroid() =
default;
std::unique_ptr<NetworkChangeNotifier>
NetworkChangeNotifierFactoryAndroid::CreateInstanceWithInitialTypes(
NetworkChangeNotifier::ConnectionType /*initial_type*/,
NetworkChangeNotifier::ConnectionSubtype /*initial_subtype*/) {
return base::WrapUnique(new NetworkChangeNotifierAndroid(&delegate_));
}
} // namespace net

View File

@@ -0,0 +1,49 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_ANDROID_NETWORK_CHANGE_NOTIFIER_FACTORY_ANDROID_H_
#define NET_ANDROID_NETWORK_CHANGE_NOTIFIER_FACTORY_ANDROID_H_
#include <memory>
#include "base/compiler_specific.h"
#include "net/android/network_change_notifier_delegate_android.h"
#include "net/base/net_export.h"
#include "net/base/network_change_notifier_factory.h"
namespace net {
class NetworkChangeNotifier;
class NetworkChangeNotifierDelegateAndroid;
// NetworkChangeNotifierFactory creates Android-specific specialization of
// NetworkChangeNotifier. See network_change_notifier_android.h for more
// details.
class NET_EXPORT NetworkChangeNotifierFactoryAndroid :
public NetworkChangeNotifierFactory {
public:
// Must be called on the JNI thread.
NetworkChangeNotifierFactoryAndroid();
NetworkChangeNotifierFactoryAndroid(
const NetworkChangeNotifierFactoryAndroid&) = delete;
NetworkChangeNotifierFactoryAndroid& operator=(
const NetworkChangeNotifierFactoryAndroid&) = delete;
// Must be called on the JNI thread.
~NetworkChangeNotifierFactoryAndroid() override;
// NetworkChangeNotifierFactory:
std::unique_ptr<NetworkChangeNotifier> CreateInstanceWithInitialTypes(
NetworkChangeNotifier::ConnectionType /*initial_type*/,
NetworkChangeNotifier::ConnectionSubtype /*initial_subtype*/) override;
private:
// Delegate passed to the instances created by this class.
NetworkChangeNotifierDelegateAndroid delegate_;
};
} // namespace net
#endif // NET_ANDROID_NETWORK_CHANGE_NOTIFIER_FACTORY_ANDROID_H_

View File

@@ -0,0 +1,347 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/android/network_library.h"
#include <dlfcn.h>
#include <string>
#include <vector>
#include "base/android/build_info.h"
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/check_op.h"
#include "base/native_library.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/scoped_blocking_call.h"
#include "net/base/net_errors.h"
#include "net/dns/public/dns_protocol.h"
#include "net/net_jni_headers/AndroidNetworkLibrary_jni.h"
#include "net/net_jni_headers/DnsStatus_jni.h"
using base::android::AttachCurrentThread;
using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaArrayOfByteArrayToStringVector;
using base::android::ScopedJavaLocalRef;
using base::android::ToJavaArrayOfByteArray;
using base::android::ToJavaByteArray;
namespace net::android {
std::vector<std::string> GetUserAddedRoots() {
std::vector<std::string> roots;
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobjectArray> roots_byte_array =
Java_AndroidNetworkLibrary_getUserAddedRoots(env);
JavaArrayOfByteArrayToStringVector(env, roots_byte_array, &roots);
return roots;
}
void VerifyX509CertChain(const std::vector<std::string>& cert_chain,
base::StringPiece auth_type,
base::StringPiece host,
CertVerifyStatusAndroid* status,
bool* is_issued_by_known_root,
std::vector<std::string>* verified_chain) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobjectArray> chain_byte_array =
ToJavaArrayOfByteArray(env, cert_chain);
DCHECK(!chain_byte_array.is_null());
ScopedJavaLocalRef<jstring> auth_string =
ConvertUTF8ToJavaString(env, auth_type);
DCHECK(!auth_string.is_null());
ScopedJavaLocalRef<jstring> host_string =
ConvertUTF8ToJavaString(env, host);
DCHECK(!host_string.is_null());
ScopedJavaLocalRef<jobject> result =
Java_AndroidNetworkLibrary_verifyServerCertificates(
env, chain_byte_array, auth_string, host_string);
ExtractCertVerifyResult(result, status, is_issued_by_known_root,
verified_chain);
}
void AddTestRootCertificate(const uint8_t* cert, size_t len) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> cert_array = ToJavaByteArray(env, cert, len);
DCHECK(!cert_array.is_null());
Java_AndroidNetworkLibrary_addTestRootCertificate(env, cert_array);
}
void ClearTestRootCertificates() {
JNIEnv* env = AttachCurrentThread();
Java_AndroidNetworkLibrary_clearTestRootCertificates(env);
}
bool IsCleartextPermitted(base::StringPiece host) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> host_string = ConvertUTF8ToJavaString(env, host);
return Java_AndroidNetworkLibrary_isCleartextPermitted(env, host_string);
}
bool HaveOnlyLoopbackAddresses() {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
JNIEnv* env = AttachCurrentThread();
return Java_AndroidNetworkLibrary_haveOnlyLoopbackAddresses(env);
}
bool GetMimeTypeFromExtension(base::StringPiece extension,
std::string* result) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> extension_string =
ConvertUTF8ToJavaString(env, extension);
ScopedJavaLocalRef<jstring> ret =
Java_AndroidNetworkLibrary_getMimeTypeFromExtension(env,
extension_string);
if (!ret.obj())
return false;
*result = ConvertJavaStringToUTF8(ret);
return true;
}
std::string GetTelephonyNetworkOperator() {
return base::android::ConvertJavaStringToUTF8(
Java_AndroidNetworkLibrary_getNetworkOperator(
base::android::AttachCurrentThread()));
}
bool GetIsRoaming() {
return Java_AndroidNetworkLibrary_getIsRoaming(
base::android::AttachCurrentThread());
}
bool GetIsCaptivePortal() {
return Java_AndroidNetworkLibrary_getIsCaptivePortal(
base::android::AttachCurrentThread());
}
std::string GetWifiSSID() {
return base::android::ConvertJavaStringToUTF8(
Java_AndroidNetworkLibrary_getWifiSSID(
base::android::AttachCurrentThread()));
}
void SetWifiEnabledForTesting(bool enabled) {
Java_AndroidNetworkLibrary_setWifiEnabledForTesting(
base::android::AttachCurrentThread(), enabled);
}
std::optional<int32_t> GetWifiSignalLevel() {
const int count_buckets = 5;
int signal_strength = Java_AndroidNetworkLibrary_getWifiSignalLevel(
base::android::AttachCurrentThread(), count_buckets);
if (signal_strength < 0)
return std::nullopt;
DCHECK_LE(0, signal_strength);
DCHECK_GE(count_buckets - 1, signal_strength);
return signal_strength;
}
namespace {
bool GetDnsServersInternal(JNIEnv* env,
const base::android::JavaRef<jobject>& dns_status,
std::vector<IPEndPoint>* dns_servers,
bool* dns_over_tls_active,
std::string* dns_over_tls_hostname,
std::vector<std::string>* search_suffixes) {
// Parse the DNS servers.
std::vector<std::vector<uint8_t>> dns_servers_data;
base::android::JavaArrayOfByteArrayToBytesVector(
env, Java_DnsStatus_getDnsServers(env, dns_status), &dns_servers_data);
for (const std::vector<uint8_t>& dns_address_data : dns_servers_data) {
IPAddress dns_address(dns_address_data.data(), dns_address_data.size());
IPEndPoint dns_server(dns_address, dns_protocol::kDefaultPort);
dns_servers->push_back(dns_server);
}
*dns_over_tls_active = Java_DnsStatus_getPrivateDnsActive(env, dns_status);
*dns_over_tls_hostname = base::android::ConvertJavaStringToUTF8(
Java_DnsStatus_getPrivateDnsServerName(env, dns_status));
std::string search_suffixes_str = base::android::ConvertJavaStringToUTF8(
Java_DnsStatus_getSearchDomains(env, dns_status));
*search_suffixes =
base::SplitString(search_suffixes_str, ",", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
return !dns_servers->empty();
}
} // namespace
bool GetCurrentDnsServers(std::vector<IPEndPoint>* dns_servers,
bool* dns_over_tls_active,
std::string* dns_over_tls_hostname,
std::vector<std::string>* search_suffixes) {
DCHECK_GE(base::android::BuildInfo::GetInstance()->sdk_int(),
base::android::SDK_VERSION_MARSHMALLOW);
JNIEnv* env = AttachCurrentThread();
// Get the DNS status for the current default network.
ScopedJavaLocalRef<jobject> result =
Java_AndroidNetworkLibrary_getCurrentDnsStatus(env);
if (result.is_null())
return false;
return GetDnsServersInternal(env, result, dns_servers, dns_over_tls_active,
dns_over_tls_hostname, search_suffixes);
}
bool GetDnsServersForNetwork(std::vector<IPEndPoint>* dns_servers,
bool* dns_over_tls_active,
std::string* dns_over_tls_hostname,
std::vector<std::string>* search_suffixes,
handles::NetworkHandle network) {
DCHECK_GE(base::android::BuildInfo::GetInstance()->sdk_int(),
base::android::SDK_VERSION_P);
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> result =
Java_AndroidNetworkLibrary_getDnsStatusForNetwork(env, network);
if (result.is_null())
return false;
return GetDnsServersInternal(env, result, dns_servers, dns_over_tls_active,
dns_over_tls_hostname, search_suffixes);
}
bool ReportBadDefaultNetwork() {
return Java_AndroidNetworkLibrary_reportBadDefaultNetwork(
AttachCurrentThread());
}
void TagSocket(SocketDescriptor socket, uid_t uid, int32_t tag) {
Java_AndroidNetworkLibrary_tagSocket(AttachCurrentThread(), socket, uid, tag);
}
namespace {
using LollipopSetNetworkForSocket = int (*)(unsigned net_id, int socket_fd);
using MarshmallowSetNetworkForSocket = int (*)(int64_t net_id, int socket_fd);
MarshmallowSetNetworkForSocket GetMarshmallowSetNetworkForSocket() {
// On Android M and newer releases use supported NDK API.
base::FilePath file(base::GetNativeLibraryName("android"));
// See declaration of android_setsocknetwork() here:
// http://androidxref.com/6.0.0_r1/xref/development/ndk/platforms/android-M/include/android/multinetwork.h#65
// Function cannot be called directly as it will cause app to fail to load on
// pre-marshmallow devices.
void* dl = dlopen(file.value().c_str(), RTLD_NOW);
return reinterpret_cast<MarshmallowSetNetworkForSocket>(
dlsym(dl, "android_setsocknetwork"));
}
LollipopSetNetworkForSocket GetLollipopSetNetworkForSocket() {
// On Android L use setNetworkForSocket from libnetd_client.so. Android's netd
// client library should always be loaded in our address space as it shims
// socket().
base::FilePath file(base::GetNativeLibraryName("netd_client"));
// Use RTLD_NOW to match Android's prior loading of the library:
// http://androidxref.com/6.0.0_r5/xref/bionic/libc/bionic/NetdClient.cpp#37
// Use RTLD_NOLOAD to assert that the library is already loaded and avoid
// doing any disk IO.
void* dl = dlopen(file.value().c_str(), RTLD_NOW | RTLD_NOLOAD);
return reinterpret_cast<LollipopSetNetworkForSocket>(
dlsym(dl, "setNetworkForSocket"));
}
} // namespace
int BindToNetwork(SocketDescriptor socket, handles::NetworkHandle network) {
DCHECK_NE(socket, kInvalidSocket);
if (network == handles::kInvalidNetworkHandle)
return ERR_INVALID_ARGUMENT;
// Android prior to Lollipop didn't have support for binding sockets to
// networks.
if (base::android::BuildInfo::GetInstance()->sdk_int() <
base::android::SDK_VERSION_LOLLIPOP)
return ERR_NOT_IMPLEMENTED;
int rv;
if (base::android::BuildInfo::GetInstance()->sdk_int() >=
base::android::SDK_VERSION_MARSHMALLOW) {
static MarshmallowSetNetworkForSocket marshmallow_set_network_for_socket =
GetMarshmallowSetNetworkForSocket();
if (!marshmallow_set_network_for_socket)
return ERR_NOT_IMPLEMENTED;
rv = marshmallow_set_network_for_socket(network, socket);
if (rv)
rv = errno;
} else {
static LollipopSetNetworkForSocket lollipop_set_network_for_socket =
GetLollipopSetNetworkForSocket();
if (!lollipop_set_network_for_socket)
return ERR_NOT_IMPLEMENTED;
rv = -lollipop_set_network_for_socket(network, socket);
}
// If |network| has since disconnected, |rv| will be ENONET. Surface this as
// ERR_NETWORK_CHANGED, rather than MapSystemError(ENONET) which gives back
// the less descriptive ERR_FAILED.
if (rv == ENONET)
return ERR_NETWORK_CHANGED;
return MapSystemError(rv);
}
namespace {
using MarshmallowGetAddrInfoForNetwork = int (*)(int64_t network,
const char* node,
const char* service,
const struct addrinfo* hints,
struct addrinfo** res);
MarshmallowGetAddrInfoForNetwork GetMarshmallowGetAddrInfoForNetwork() {
// On Android M and newer releases use supported NDK API.
base::FilePath file(base::GetNativeLibraryName("android"));
// See declaration of android_getaddrinfofornetwork() here:
// https://developer.android.com/ndk/reference/group/networking#android_getaddrinfofornetwork
// Function cannot be called directly as it will cause app to fail to load on
// pre-marshmallow devices.
void* dl = dlopen(file.value().c_str(), RTLD_NOW);
return reinterpret_cast<MarshmallowGetAddrInfoForNetwork>(
dlsym(dl, "android_getaddrinfofornetwork"));
}
} // namespace
NET_EXPORT_PRIVATE int GetAddrInfoForNetwork(handles::NetworkHandle network,
const char* node,
const char* service,
const struct addrinfo* hints,
struct addrinfo** res) {
if (network == handles::kInvalidNetworkHandle) {
errno = EINVAL;
return EAI_SYSTEM;
}
if (base::android::BuildInfo::GetInstance()->sdk_int() <
base::android::SDK_VERSION_MARSHMALLOW) {
errno = ENOSYS;
return EAI_SYSTEM;
}
static MarshmallowGetAddrInfoForNetwork get_addrinfo_for_network =
GetMarshmallowGetAddrInfoForNetwork();
if (!get_addrinfo_for_network) {
errno = ENOSYS;
return EAI_SYSTEM;
}
return get_addrinfo_for_network(network, node, service, hints, res);
}
} // namespace net::android

View File

@@ -0,0 +1,155 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_ANDROID_NETWORK_LIBRARY_H_
#define NET_ANDROID_NETWORK_LIBRARY_H_
#include <android/multinetwork.h>
#include <jni.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <optional>
#include <string>
#include <vector>
#include "base/functional/callback.h"
#include "base/strings/string_piece.h"
#include "net/android/cert_verify_result_android.h"
#include "net/base/ip_endpoint.h"
#include "net/base/mime_util.h"
#include "net/base/net_export.h"
#include "net/base/network_handle.h"
#include "net/socket/socket_descriptor.h"
namespace net::android {
// Get the list of user-added roots from Android.
// |roots| is a list of DER-encoded user-added roots from Android.
std::vector<std::string> GetUserAddedRoots();
// |cert_chain| is DER encoded chain of certificates, with the server's own
// certificate listed first.
// |auth_type| is as per the Java X509Certificate.checkServerTrusted method.
void VerifyX509CertChain(const std::vector<std::string>& cert_chain,
base::StringPiece auth_type,
base::StringPiece host,
CertVerifyStatusAndroid* status,
bool* is_issued_by_known_root,
std::vector<std::string>* verified_chain);
// Adds a certificate as a root trust certificate to the trust manager.
// |cert| is DER encoded certificate, |len| is its length in bytes.
void AddTestRootCertificate(const uint8_t* cert, size_t len);
// Removes all root certificates added by |AddTestRootCertificate| calls.
void ClearTestRootCertificates();
// Returns true if cleartext traffic to |host| is allowed by the app. Always
// true on L and older.
bool IsCleartextPermitted(base::StringPiece host);
// Returns true if it can determine that only loopback addresses are configured.
// i.e. if only 127.0.0.1 and ::1 are routable.
// Also returns false if it cannot determine this.
bool HaveOnlyLoopbackAddresses();
// Get the mime type (if any) that is associated with the file extension.
// Returns true if a corresponding mime type exists.
bool GetMimeTypeFromExtension(base::StringPiece extension, std::string* result);
// Returns MCC+MNC (mobile country code + mobile network code) as
// the numeric name of the current registered operator. This function
// potentially blocks the thread, so use with care.
NET_EXPORT std::string GetTelephonyNetworkOperator();
// Returns true if the device is roaming on the currently active network. When
// true, it suggests that use of data may incur extra costs.
NET_EXPORT bool GetIsRoaming();
// Returns true if the system's captive portal probe was blocked for the current
// default data network. The method will return false if the captive portal
// probe was not blocked, the login process to the captive portal has been
// successfully completed, or if the captive portal status can't be determined.
// Requires ACCESS_NETWORK_STATE permission. Only available on Android
// Marshmallow and later versions. Returns false on earlier versions.
NET_EXPORT bool GetIsCaptivePortal();
// Gets the SSID of the currently associated WiFi access point if there is one,
// and it is available. SSID may not be available if the app does not have
// permissions to access it. On Android M+, the app accessing SSID needs to have
// ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION. If there is no WiFi access
// point or its SSID is unavailable, an empty string is returned.
NET_EXPORT_PRIVATE std::string GetWifiSSID();
// Call WifiManager.setWifiEnabled.
NET_EXPORT_PRIVATE void SetWifiEnabledForTesting(bool enabled);
// Returns the signal strength level (between 0 and 4, both inclusive) of the
// currently registered Wifi connection. If the value is unavailable, an
// empty value is returned.
NET_EXPORT_PRIVATE std::optional<int32_t> GetWifiSignalLevel();
// Gets the DNS servers for the current default network and puts them in
// `dns_servers`. Sets `dns_over_tls_active` and `dns_over_tls_hostname` based
// on the private DNS settings. `dns_over_tls_hostname` will only be non-empty
// if `dns_over_tls_active` is true.
// Only callable on Marshmallow and newer releases.
// Returns false when a valid server config could not be read.
NET_EXPORT_PRIVATE bool GetCurrentDnsServers(
std::vector<IPEndPoint>* dns_servers,
bool* dns_over_tls_active,
std::string* dns_over_tls_hostname,
std::vector<std::string>* search_suffixes);
using DnsServerGetter =
base::RepeatingCallback<bool(std::vector<IPEndPoint>* dns_servers,
bool* dns_over_tls_active,
std::string* dns_over_tls_hostname,
std::vector<std::string>* search_suffixes)>;
// Works as GetCurrentDnsServers but gets info specific to `network` instead
// of the current default network.
// Only callable on Pie and newer releases.
// Returns false when a valid server config could not be read.
NET_EXPORT_PRIVATE bool GetDnsServersForNetwork(
std::vector<IPEndPoint>* dns_servers,
bool* dns_over_tls_active,
std::string* dns_over_tls_hostname,
std::vector<std::string>* search_suffixes,
handles::NetworkHandle network);
// Reports to the framework that the current default network appears to have
// connectivity issues. This may serve as a signal for the OS to consider
// switching to a different default network. Returns |true| if successfully
// reported to the OS, or |false| if not supported.
NET_EXPORT_PRIVATE bool ReportBadDefaultNetwork();
// Apply TrafficStats tag |tag| and UID |uid| to |socket|. Future network
// traffic used by |socket| will be attributed to |uid| and |tag|.
NET_EXPORT_PRIVATE void TagSocket(SocketDescriptor socket,
uid_t uid,
int32_t tag);
// Binds this socket to `network`. All data traffic on the socket will be sent
// and received via `network`. This call will fail if `network` has
// disconnected. Communication using this socket will fail if `network`
// disconnects.
// Returns a net error code.
NET_EXPORT_PRIVATE int BindToNetwork(SocketDescriptor socket,
handles::NetworkHandle network);
// Perform hostname resolution via the DNS servers associated with `network`.
// All arguments are used identically as those passed to Android NDK API
// android_getaddrinfofornetwork:
// https://developer.android.com/ndk/reference/group/networking#group___networking_1ga0ae9e15612e6411855e295476a98ceee
NET_EXPORT_PRIVATE int GetAddrInfoForNetwork(handles::NetworkHandle network,
const char* node,
const char* service,
const struct addrinfo* hints,
struct addrinfo** res);
} // namespace net::android
#endif // NET_ANDROID_NETWORK_LIBRARY_H_

View File

@@ -0,0 +1,358 @@
#!/usr/bin/env python
# Copyright 2012 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Generator script for proxy tests.
See AndroidProxySelectorTest.java
and net/proxy_resolution/proxy_config_service_android_unittest.cc
To generate C++, run this script without arguments.
To generate Java, run this script with -j argument.
Note that this generator is not run as part of the build process because
we are assuming that these test cases will not change often.
"""
import optparse
test_cases = [
{
"name": "NoProxy",
"description" : "Test direct mapping when no proxy defined.",
"properties" : {
},
"mappings" : {
"http://example.com/" : "DIRECT",
"ftp://example.com/" : "DIRECT",
"https://example.com/" : "DIRECT",
}
},
{
"name": "HttpProxyHostAndPort",
"description" : "Test http.proxyHost and http.proxyPort works.",
"properties" : {
"http.proxyHost" : "httpproxy.com",
"http.proxyPort" : "8080",
},
"mappings" : {
"http://example.com/" : "PROXY httpproxy.com:8080",
"ftp://example.com/" : "DIRECT",
"https://example.com/" : "DIRECT",
}
},
{
"name": "HttpProxyHostOnly",
"description" : "We should get the default port (80) for proxied hosts.",
"properties" : {
"http.proxyHost" : "httpproxy.com",
},
"mappings" : {
"http://example.com/" : "PROXY httpproxy.com:80",
"ftp://example.com/" : "DIRECT",
"https://example.com/" : "DIRECT",
}
},
{
"name": "HttpProxyPortOnly",
"description" :
"http.proxyPort only should not result in any hosts being proxied.",
"properties" : {
"http.proxyPort" : "8080",
},
"mappings" : {
"http://example.com/" : "DIRECT",
"ftp://example.com/" : "DIRECT",
"https://example.com/" : "DIRECT"
}
},
{
"name": "HttpNonProxyHosts1",
"description" : "Test that HTTP non proxy hosts are mapped correctly",
"properties" : {
"http.nonProxyHosts" : "slashdot.org",
"http.proxyHost" : "httpproxy.com",
"http.proxyPort" : "8080",
},
"mappings" : {
"http://example.com/" : "PROXY httpproxy.com:8080",
"http://slashdot.org/" : "DIRECT",
}
},
{
"name": "HttpNonProxyHosts2",
"description" : "Test that | pattern works.",
"properties" : {
"http.nonProxyHosts" : "slashdot.org|freecode.net",
"http.proxyHost" : "httpproxy.com",
"http.proxyPort" : "8080",
},
"mappings" : {
"http://example.com/" : "PROXY httpproxy.com:8080",
"http://slashdot.org/" : "DIRECT",
"http://freecode.net/" : "DIRECT",
}
},
{
"name": "HttpNonProxyHosts3",
"description" : "Test that * pattern works.",
"properties" : {
"http.nonProxyHosts" : "*example.com",
"http.proxyHost" : "httpproxy.com",
"http.proxyPort" : "8080",
},
"mappings" : {
"http://example.com/" : "DIRECT",
"http://www.example.com/" : "DIRECT",
"http://slashdot.org/" : "PROXY httpproxy.com:8080",
}
},
{
"name": "FtpNonProxyHosts",
"description" : "Test that FTP non proxy hosts are mapped correctly",
"properties" : {
"ftp.nonProxyHosts" : "slashdot.org",
"ftp.proxyHost" : "httpproxy.com",
"ftp.proxyPort" : "8080",
},
"mappings" : {
"http://example.com/" : "DIRECT",
"ftp://example.com/" : "PROXY httpproxy.com:8080",
}
},
{
"name": "FtpProxyHostAndPort",
"description" : "Test ftp.proxyHost and ftp.proxyPort works.",
"properties" : {
"ftp.proxyHost" : "httpproxy.com",
"ftp.proxyPort" : "8080",
},
"mappings" : {
"ftp://example.com/" : "PROXY httpproxy.com:8080",
"http://example.com/" : "DIRECT",
"https://example.com/" : "DIRECT",
}
},
{
"name": "FtpProxyHostOnly",
"description" : "Test ftp.proxyHost and default port.",
"properties" : {
"ftp.proxyHost" : "httpproxy.com",
},
"mappings" : {
"ftp://example.com/" : "PROXY httpproxy.com:80",
"http://example.com/" : "DIRECT",
"https://example.com/" : "DIRECT",
}
},
{
"name": "HttpsProxyHostAndPort",
"description" : "Test https.proxyHost and https.proxyPort works.",
"properties" : {
"https.proxyHost" : "httpproxy.com",
"https.proxyPort" : "8080",
},
"mappings" : {
"https://example.com/" : "PROXY httpproxy.com:8080",
"http://example.com/" : "DIRECT",
"ftp://example.com/" : "DIRECT",
}
},
{
"name": "HttpsProxyHostOnly",
"description" : "Test https.proxyHost and default port.",
# Chromium differs from the Android platform by connecting to port 80 for
# HTTPS connections by default, hence cpp-only.
"cpp-only" : "",
"properties" : {
"https.proxyHost" : "httpproxy.com",
},
"mappings" : {
"https://example.com/" : "PROXY httpproxy.com:80",
"http://example.com/" : "DIRECT",
"ftp://example.com/" : "DIRECT",
}
},
{
"name": "HttpProxyHostIPv6",
"description" : "Test IPv6 https.proxyHost and default port.",
"cpp-only" : "",
"properties" : {
"http.proxyHost" : "a:b:c::d:1",
},
"mappings" : {
"http://example.com/" : "PROXY [a:b:c::d:1]:80",
"ftp://example.com/" : "DIRECT",
}
},
{
"name": "HttpProxyHostAndPortIPv6",
"description" : "Test IPv6 http.proxyHost and http.proxyPort works.",
"cpp-only" : "",
"properties" : {
"http.proxyHost" : "a:b:c::d:1",
"http.proxyPort" : "8080",
},
"mappings" : {
"http://example.com/" : "PROXY [a:b:c::d:1]:8080",
"ftp://example.com/" : "DIRECT",
}
},
{
"name": "HttpProxyHostAndInvalidPort",
"description" : "Test invalid http.proxyPort does not crash.",
"cpp-only" : "",
"properties" : {
"http.proxyHost" : "a:b:c::d:1",
"http.proxyPort" : "65536",
},
"mappings" : {
"http://example.com/" : "DIRECT",
"ftp://example.com/" : "DIRECT",
}
},
{
"name": "DefaultProxyExplictPort",
"description" :
"Default http proxy is used if a scheme-specific one is not found.",
"properties" : {
"proxyHost" : "defaultproxy.com",
"proxyPort" : "8080",
"ftp.proxyHost" : "httpproxy.com",
"ftp.proxyPort" : "8080",
},
"mappings" : {
"http://example.com/" : "PROXY defaultproxy.com:8080",
"https://example.com/" : "PROXY defaultproxy.com:8080",
"ftp://example.com/" : "PROXY httpproxy.com:8080",
}
},
{
"name": "DefaultProxyDefaultPort",
"description" : "Check that the default proxy port is as expected.",
# Chromium differs from the Android platform by connecting to port 80 for
# HTTPS connections by default, hence cpp-only.
"cpp-only" : "",
"properties" : {
"proxyHost" : "defaultproxy.com",
},
"mappings" : {
"http://example.com/" : "PROXY defaultproxy.com:80",
"https://example.com/" : "PROXY defaultproxy.com:80",
}
},
{
"name": "FallbackToSocks",
"description" : "SOCKS proxy is used if scheme-specific one is not found.",
"properties" : {
"http.proxyHost" : "defaultproxy.com",
"socksProxyHost" : "socksproxy.com"
},
"mappings" : {
"http://example.com/" : "PROXY defaultproxy.com:80",
"https://example.com/" : "SOCKS5 socksproxy.com:1080",
"ftp://example.com" : "SOCKS5 socksproxy.com:1080",
}
},
{
"name": "SocksExplicitPort",
"description" : "SOCKS proxy port is used if specified",
"properties" : {
"socksProxyHost" : "socksproxy.com",
"socksProxyPort" : "9000",
},
"mappings" : {
"http://example.com/" : "SOCKS5 socksproxy.com:9000",
}
},
{
"name": "HttpProxySupercedesSocks",
"description" : "SOCKS proxy is ignored if default HTTP proxy defined.",
"properties" : {
"proxyHost" : "defaultproxy.com",
"socksProxyHost" : "socksproxy.com",
"socksProxyPort" : "9000",
},
"mappings" : {
"http://example.com/" : "PROXY defaultproxy.com:80",
}
},
]
class GenerateCPlusPlus:
"""Generate C++ test cases"""
def Generate(self):
for test_case in test_cases:
print ("TEST_F(ProxyConfigServiceAndroidTest, %s) {" % test_case["name"])
if "description" in test_case:
self._GenerateDescription(test_case["description"]);
self._GenerateConfiguration(test_case["properties"])
self._GenerateMappings(test_case["mappings"])
print "}"
print ""
def _GenerateDescription(self, description):
print " // %s" % description
def _GenerateConfiguration(self, properties):
for key in sorted(properties.iterkeys()):
print " AddProperty(\"%s\", \"%s\");" % (key, properties[key])
print " ProxySettingsChanged();"
def _GenerateMappings(self, mappings):
for url in sorted(mappings.iterkeys()):
print " TestMapping(\"%s\", \"%s\");" % (url, mappings[url])
class GenerateJava:
"""Generate Java test cases"""
def Generate(self):
for test_case in test_cases:
if "cpp-only" in test_case:
continue
if "description" in test_case:
self._GenerateDescription(test_case["description"]);
print " @SmallTest"
print " @Feature({\"AndroidWebView\"})"
print " public void test%s() throws Exception {" % test_case["name"]
self._GenerateConfiguration(test_case["properties"])
self._GenerateMappings(test_case["mappings"])
print " }"
print ""
def _GenerateDescription(self, description):
print " /**"
print " * %s" % description
print " *"
print " * @throws Exception"
print " */"
def _GenerateConfiguration(self, properties):
for key in sorted(properties.iterkeys()):
print " System.setProperty(\"%s\", \"%s\");" % (
key, properties[key])
def _GenerateMappings(self, mappings):
for url in sorted(mappings.iterkeys()):
mapping = mappings[url]
if 'HTTPS' in mapping:
mapping = mapping.replace('HTTPS', 'PROXY')
print " checkMapping(\"%s\", \"%s\");" % (url, mapping)
def main():
parser = optparse.OptionParser()
parser.add_option("-j", "--java",
action="store_true", dest="java");
(options, args) = parser.parse_args();
if options.java:
generator = GenerateJava()
else:
generator = GenerateCPlusPlus()
generator.Generate()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,42 @@
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/android/traffic_stats.h"
#include "net/net_jni_headers/AndroidTrafficStats_jni.h"
namespace net::android::traffic_stats {
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.net
enum TrafficStatsError {
// Value returned by AndroidTrafficStats APIs when a valid value is
// unavailable.
ERROR_NOT_SUPPORTED = 0,
};
bool GetTotalTxBytes(int64_t* bytes) {
JNIEnv* env = jni_zero::AttachCurrentThread();
*bytes = Java_AndroidTrafficStats_getTotalTxBytes(env);
return *bytes != ERROR_NOT_SUPPORTED;
}
bool GetTotalRxBytes(int64_t* bytes) {
JNIEnv* env = jni_zero::AttachCurrentThread();
*bytes = Java_AndroidTrafficStats_getTotalRxBytes(env);
return *bytes != ERROR_NOT_SUPPORTED;
}
bool GetCurrentUidTxBytes(int64_t* bytes) {
JNIEnv* env = jni_zero::AttachCurrentThread();
*bytes = Java_AndroidTrafficStats_getCurrentUidTxBytes(env);
return *bytes != ERROR_NOT_SUPPORTED;
}
bool GetCurrentUidRxBytes(int64_t* bytes) {
JNIEnv* env = jni_zero::AttachCurrentThread();
*bytes = Java_AndroidTrafficStats_getCurrentUidRxBytes(env);
return *bytes != ERROR_NOT_SUPPORTED;
}
} // namespace net::android::traffic_stats

View File

@@ -0,0 +1,48 @@
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_ANDROID_TRAFFIC_STATS_H_
#define NET_ANDROID_TRAFFIC_STATS_H_
// This file provides functions that interact with TrafficStats APIs that are
// provided on Android.
#include <jni.h>
#include <stdint.h>
#include "net/base/net_export.h"
namespace net::android::traffic_stats {
// Returns true if the number of bytes transmitted since device boot is
// available and sets |*bytes| to that value. Counts packets across all network
// interfaces, and always increases monotonically since device boot.
// Statistics are measured at the network layer, so they include both TCP and
// UDP usage. |bytes| must not be nullptr.
NET_EXPORT bool GetTotalTxBytes(int64_t* bytes);
// Returns true if the number of bytes received since device boot is
// available and sets |*bytes| to that value. Counts packets across all network
// interfaces, and always increases monotonically since device boot.
// Statistics are measured at the network layer, so they include both TCP and
// UDP usage. |bytes| must not be nullptr.
NET_EXPORT bool GetTotalRxBytes(int64_t* bytes);
// Returns true if the number of bytes attributed to caller's UID since device
// boot are available and sets |*bytes| to that value. Counts packets across
// all network interfaces, and always increases monotonically since device
// boot. Statistics are measured at the network layer, so they include both TCP
// and UDP usage. |bytes| must not be nullptr.
NET_EXPORT bool GetCurrentUidTxBytes(int64_t* bytes);
// Returns true if the number of bytes attributed to caller's UID since device
// boot are available and sets |*bytes| to that value. Counts packets across
// all network interfaces, and always increases monotonically since device
// boot. Statistics are measured at the network layer, so they include both TCP
// and UDP usage. |bytes| must not be nullptr.
NET_EXPORT bool GetCurrentUidRxBytes(int64_t* bytes);
} // namespace net::android::traffic_stats
#endif // NET_ANDROID_TRAFFIC_STATS_H_

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2014 The Chromium Authors
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.chromium.native_test"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- Explicitly set the attribute requestLegacyExternalStorage to "true"
since it is "false" by default on apps targeting Android 10, and that
breaks test listing. See
https://developer.android.com/training/data-storage#scoped-storage and
https://developer.android.com/training/data-storage/compatibility. -->
<application android:label="NativeTests"
android:requestLegacyExternalStorage="true"
android:name="org.chromium.native_test.NativeTestApplication">
<activity android:name=".NativeUnitTestActivity"
android:label="NativeTest"
android:configChanges="orientation|keyboardHidden"
android:exported="true"
android:process=":test_process">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="org.chromium.net.test.DummySpnegoAuthenticatorService"
android:exported="false"
android:process=":test_process">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/dummy_spnego_authenticator" />
</service>
</application>
<instrumentation android:name="org.chromium.build.gtest_apk.NativeTestInstrumentationTestRunner"
android:targetPackage="org.chromium.native_test"
android:label="Instrumentation entry point for org.chromium.native_test" />
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"/>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="org.chromium.test.DummySpnegoAuthenticator"
android:label="DummySpengoAuthenticator"
android:icon="@mipmap/app_icon"
android:smallIcon="@mipmap/app_icon"
android:customTokens="true"
android:accountPreferences="@xml/dummy_spnego_account_preferences"/>

18
src/net/base/BUILD.gn Normal file
View File

@@ -0,0 +1,18 @@
# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
if (is_android) {
import("//build/config/android/rules.gni")
java_cpp_features("java_features_srcjar") {
# External code should depend on ":features_java" instead.
visibility = [ ":*" ]
sources = [ "features.cc" ]
template = "android/java_templates/NetFeatures.java.tmpl"
}
android_library("features_java") {
srcjar_deps = [ ":java_features_srcjar" ]
}
}

3
src/net/base/DEPS Normal file
View File

@@ -0,0 +1,3 @@
include_rules = [
"+grit", # For generated headers
]

8
src/net/base/OWNERS Normal file
View File

@@ -0,0 +1,8 @@
per-file *_fuchsia*=file://build/fuchsia/OWNERS
per-file network_anonymization_key*=brgoldstein@google.com
per-file network_isolation_key*=brgoldstein@google.com
per-file isolation_info*=brgoldstein@google.com
# For security review of MIME sniffing to avoid introducing security bugs
per-file mime_sniffer*=set noparent
per-file mime_sniffer*=file://net/base/SECURITY_OWNERS

View File

@@ -0,0 +1,2 @@
rsleevi@chromium.org
mmenke@chromium.org

View File

@@ -0,0 +1,49 @@
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/address_family.h"
#include "base/notreached.h"
#include "net/base/ip_address.h"
#include "net/base/sys_addrinfo.h"
namespace net {
AddressFamily GetAddressFamily(const IPAddress& address) {
if (address.IsIPv4()) {
return ADDRESS_FAMILY_IPV4;
} else if (address.IsIPv6()) {
return ADDRESS_FAMILY_IPV6;
} else {
return ADDRESS_FAMILY_UNSPECIFIED;
}
}
int ConvertAddressFamily(AddressFamily address_family) {
switch (address_family) {
case ADDRESS_FAMILY_UNSPECIFIED:
return AF_UNSPEC;
case ADDRESS_FAMILY_IPV4:
return AF_INET;
case ADDRESS_FAMILY_IPV6:
return AF_INET6;
}
NOTREACHED();
return AF_UNSPEC;
}
AddressFamily ToAddressFamily(int family) {
switch (family) {
case AF_INET:
return ADDRESS_FAMILY_IPV4;
case AF_INET6:
return ADDRESS_FAMILY_IPV6;
case AF_UNSPEC:
return ADDRESS_FAMILY_UNSPECIFIED;
}
NOTREACHED();
return ADDRESS_FAMILY_UNSPECIFIED;
}
} // namespace net

View File

@@ -0,0 +1,49 @@
// Copyright 2010 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_ADDRESS_FAMILY_H_
#define NET_BASE_ADDRESS_FAMILY_H_
#include "net/base/net_export.h"
namespace net {
class IPAddress;
// Enum wrapper around the address family types supported by host resolver
// procedures.
enum AddressFamily {
ADDRESS_FAMILY_UNSPECIFIED, // AF_UNSPEC
ADDRESS_FAMILY_IPV4, // AF_INET
ADDRESS_FAMILY_IPV6, // AF_INET6
ADDRESS_FAMILY_LAST = ADDRESS_FAMILY_IPV6
};
// HostResolverFlags is a bitflag enum used by host resolver procedures to
// determine the value of addrinfo.ai_flags and work around getaddrinfo
// peculiarities.
enum {
HOST_RESOLVER_CANONNAME = 1 << 0, // AI_CANONNAME
// Hint to the resolver proc that only loopback addresses are configured.
HOST_RESOLVER_LOOPBACK_ONLY = 1 << 1,
// Indicate the address family was set because no IPv6 support was detected.
HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6 = 1 << 2,
// The resolver should avoid resolving using multicast protocols (LLMNR or
// mDNS).
HOST_RESOLVER_AVOID_MULTICAST = 1 << 3
};
typedef int HostResolverFlags;
// Returns AddressFamily for |address|.
NET_EXPORT AddressFamily GetAddressFamily(const IPAddress& address);
// Maps the given AddressFamily to either AF_INET, AF_INET6 or AF_UNSPEC.
NET_EXPORT int ConvertAddressFamily(AddressFamily address_family);
// Maps AF_INET, AF_INET6 or AF_UNSPEC to an AddressFamily.
NET_EXPORT AddressFamily ToAddressFamily(int family);
} // namespace net
#endif // NET_BASE_ADDRESS_FAMILY_H_

View File

@@ -0,0 +1,154 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/address_list.h"
#include <iterator>
#include <string>
#include <utility>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/logging.h"
#include "base/values.h"
#include "net/base/sys_addrinfo.h"
namespace net {
AddressList::AddressList() = default;
AddressList::AddressList(const AddressList&) = default;
AddressList& AddressList::operator=(const AddressList&) = default;
AddressList::AddressList(AddressList&&) = default;
AddressList& AddressList::operator=(AddressList&&) = default;
AddressList::~AddressList() = default;
AddressList::AddressList(const IPEndPoint& endpoint) {
push_back(endpoint);
}
AddressList::AddressList(const IPEndPoint& endpoint,
std::vector<std::string> aliases)
: dns_aliases_(std::move(aliases)) {
push_back(endpoint);
}
AddressList::AddressList(std::vector<IPEndPoint> endpoints)
: endpoints_(std::move(endpoints)) {}
// static
AddressList AddressList::CreateFromIPAddress(const IPAddress& address,
uint16_t port) {
return AddressList(IPEndPoint(address, port));
}
// static
AddressList AddressList::CreateFromIPAddressList(
const IPAddressList& addresses,
std::vector<std::string> aliases) {
AddressList list;
for (const auto& address : addresses) {
list.push_back(IPEndPoint(address, 0));
}
list.SetDnsAliases(std::move(aliases));
return list;
}
// static
AddressList AddressList::CreateFromAddrinfo(const struct addrinfo* head) {
DCHECK(head);
AddressList list;
if (head->ai_canonname) {
std::vector<std::string> aliases({std::string(head->ai_canonname)});
list.SetDnsAliases(std::move(aliases));
}
for (const struct addrinfo* ai = head; ai; ai = ai->ai_next) {
IPEndPoint ipe;
// NOTE: Ignoring non-INET* families.
if (ipe.FromSockAddr(ai->ai_addr, static_cast<socklen_t>(ai->ai_addrlen)))
list.push_back(ipe);
else
DLOG(WARNING) << "Unknown family found in addrinfo: " << ai->ai_family;
}
return list;
}
// static
AddressList AddressList::CopyWithPort(const AddressList& list, uint16_t port) {
AddressList out;
out.SetDnsAliases(list.dns_aliases());
for (const auto& i : list)
out.push_back(IPEndPoint(i.address(), port));
return out;
}
void AddressList::SetDefaultCanonicalName() {
DCHECK(!empty());
DCHECK(dns_aliases_.empty());
SetDnsAliases({front().ToStringWithoutPort()});
}
void AddressList::SetDnsAliases(std::vector<std::string> aliases) {
// TODO(cammie): Track down the callers who use {""} for `aliases` and
// update so that we can enforce by DCHECK below.
// The empty canonical name is represented by a empty `dns_aliases_`
// vector, so in this case we reset the field.
if (aliases == std::vector<std::string>({""})) {
dns_aliases_ = std::vector<std::string>();
return;
}
dns_aliases_ = std::move(aliases);
}
void AddressList::AppendDnsAliases(std::vector<std::string> aliases) {
DCHECK(aliases != std::vector<std::string>({""}));
using iter_t = std::vector<std::string>::iterator;
dns_aliases_.insert(dns_aliases_.end(),
std::move_iterator<iter_t>(aliases.begin()),
std::move_iterator<iter_t>(aliases.end()));
}
base::Value::Dict AddressList::NetLogParams() const {
base::Value::Dict dict;
base::Value::List address_list;
for (const auto& ip_endpoint : *this)
address_list.Append(ip_endpoint.ToString());
dict.Set("address_list", std::move(address_list));
base::Value::List alias_list;
for (const std::string& alias : dns_aliases_)
alias_list.Append(alias);
dict.Set("aliases", std::move(alias_list));
return dict;
}
void AddressList::Deduplicate() {
if (size() > 1) {
std::vector<std::pair<IPEndPoint, int>> make_me_into_a_map(size());
for (auto& addr : *this)
make_me_into_a_map.emplace_back(addr, 0);
base::flat_map<IPEndPoint, int> inserted(std::move(make_me_into_a_map));
std::vector<IPEndPoint> deduplicated_addresses;
deduplicated_addresses.reserve(inserted.size());
for (const auto& addr : *this) {
int& count = inserted[addr];
if (!count) {
deduplicated_addresses.push_back(addr);
++count;
}
}
endpoints_.swap(deduplicated_addresses);
}
}
} // namespace net

121
src/net/base/address_list.h Normal file
View File

@@ -0,0 +1,121 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_ADDRESS_LIST_H_
#define NET_BASE_ADDRESS_LIST_H_
#include <stdint.h>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "base/compiler_specific.h"
#include "base/values.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_export.h"
struct addrinfo;
namespace base {
class Value;
}
namespace net {
class IPAddress;
class NET_EXPORT AddressList {
public:
AddressList();
AddressList(const AddressList&);
AddressList& operator=(const AddressList&);
AddressList(AddressList&&);
AddressList& operator=(AddressList&&);
~AddressList();
// Creates an address list for a single IP endpoint.
explicit AddressList(const IPEndPoint& endpoint);
// Creates an address list for a single IP endpoint and a list of DNS aliases.
AddressList(const IPEndPoint& endpoint, std::vector<std::string> aliases);
// Creates an address list for a list of IP endpoints.
explicit AddressList(std::vector<IPEndPoint> endpoints);
static AddressList CreateFromIPAddress(const IPAddress& address,
uint16_t port);
static AddressList CreateFromIPAddressList(const IPAddressList& addresses,
std::vector<std::string> aliases);
// Copies the data from `head` and the chained list into an AddressList.
static AddressList CreateFromAddrinfo(const struct addrinfo* head);
// Returns a copy of `list` with port on each element set to |port|.
static AddressList CopyWithPort(const AddressList& list, uint16_t port);
bool operator==(const AddressList& other) const {
return std::tie(endpoints_, dns_aliases_) ==
std::tie(other.endpoints_, other.dns_aliases_);
}
bool operator!=(const AddressList& other) const { return !(*this == other); }
// Sets the first entry of `dns_aliases_` to the literal of the first IP
// address on the list. Assumes that `dns_aliases_` is empty.
void SetDefaultCanonicalName();
// The alias chain in no particular order.
const std::vector<std::string>& dns_aliases() const { return dns_aliases_; }
void SetDnsAliases(std::vector<std::string> aliases);
void AppendDnsAliases(std::vector<std::string> aliases);
// Creates a value representation of the address list, appropriate for
// inclusion in a NetLog.
base::Value::Dict NetLogParams() const;
// Deduplicates the stored addresses while otherwise preserving their order.
void Deduplicate();
using iterator = std::vector<IPEndPoint>::iterator;
using const_iterator = std::vector<IPEndPoint>::const_iterator;
size_t size() const { return endpoints_.size(); }
bool empty() const { return endpoints_.empty(); }
void clear() { endpoints_.clear(); }
void reserve(size_t count) { endpoints_.reserve(count); }
size_t capacity() const { return endpoints_.capacity(); }
IPEndPoint& operator[](size_t index) { return endpoints_[index]; }
const IPEndPoint& operator[](size_t index) const { return endpoints_[index]; }
IPEndPoint& front() { return endpoints_.front(); }
const IPEndPoint& front() const { return endpoints_.front(); }
IPEndPoint& back() { return endpoints_.back(); }
const IPEndPoint& back() const { return endpoints_.back(); }
void push_back(const IPEndPoint& val) { endpoints_.push_back(val); }
template <typename InputIt>
void insert(iterator pos, InputIt first, InputIt last) {
endpoints_.insert(pos, first, last);
}
iterator begin() { return endpoints_.begin(); }
const_iterator begin() const { return endpoints_.begin(); }
iterator end() { return endpoints_.end(); }
const_iterator end() const { return endpoints_.end(); }
const std::vector<net::IPEndPoint>& endpoints() const { return endpoints_; }
std::vector<net::IPEndPoint>& endpoints() { return endpoints_; }
private:
std::vector<IPEndPoint> endpoints_;
// In no particular order.
std::vector<std::string> dns_aliases_;
};
} // namespace net
#endif // NET_BASE_ADDRESS_LIST_H_

View File

@@ -0,0 +1,57 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/address_map_cache_linux.h"
#include <linux/rtnetlink.h>
#include "base/synchronization/lock.h"
namespace net {
AddressMapCacheLinux::AddressMapCacheLinux() = default;
AddressMapCacheLinux::~AddressMapCacheLinux() = default;
AddressMapOwnerLinux::AddressMap AddressMapCacheLinux::GetAddressMap() const {
base::AutoLock autolock(lock_);
return cached_address_map_;
}
std::unordered_set<int> AddressMapCacheLinux::GetOnlineLinks() const {
base::AutoLock autolock(lock_);
return cached_online_links_;
}
AddressMapCacheLinux* AddressMapCacheLinux::GetAddressMapCacheLinux() {
return this;
}
void AddressMapCacheLinux::SetCachedInfo(AddressMap address_map,
std::unordered_set<int> online_links) {
base::AutoLock autolock(lock_);
cached_address_map_ = std::move(address_map);
cached_online_links_ = std::move(online_links);
}
void AddressMapCacheLinux::ApplyDiffs(const AddressMapDiff& addr_diff,
const OnlineLinksDiff& links_diff) {
base::AutoLock autolock(lock_);
for (const auto& [address, msg_opt] : addr_diff) {
if (msg_opt.has_value()) {
cached_address_map_[address] = msg_opt.value();
} else {
cached_address_map_.erase(address);
}
}
for (const auto& [if_index, is_now_online] : links_diff) {
if (is_now_online) {
cached_online_links_.insert(if_index);
} else {
cached_online_links_.erase(if_index);
}
}
}
} // namespace net

View File

@@ -0,0 +1,58 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_ADDRESS_MAP_CACHE_LINUX_H_
#define NET_BASE_ADDRESS_MAP_CACHE_LINUX_H_
#include <map>
#include <string>
#include <unordered_set>
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "net/base/address_map_linux.h"
#include "net/base/net_export.h"
namespace net {
// This caches AddressMap and the set of online links (see AddressMapOwnerLinux)
// so AddressTrackerLinux doesn't need to always be running in every process.
// This class is thread-safe.
class NET_EXPORT AddressMapCacheLinux : public AddressMapOwnerLinux {
public:
AddressMapCacheLinux();
AddressMapCacheLinux(const AddressMapCacheLinux&) = delete;
AddressMapCacheLinux& operator=(const AddressMapCacheLinux&) = delete;
~AddressMapCacheLinux() override;
// AddressMapOwnerLinux implementation:
AddressMap GetAddressMap() const override;
std::unordered_set<int> GetOnlineLinks() const override;
AddressMapCacheLinux* GetAddressMapCacheLinux() override;
// Sets `cached_address_map_` and `cached_online_links_`. This should normally
// only be used to set the initial AddressMap and set of online links.
void SetCachedInfo(AddressMap address_map,
std::unordered_set<int> online_links);
// Takes the diffs and applies them (atomically) to `cached_address_map_` and
// `cached_online_links_`.
// Once this method returns, calls on other threads to GetAddressMap() and
// GetOnlineLinks() that happen-after this call should return the updated
// data.
void ApplyDiffs(const AddressMapDiff& addr_diff,
const OnlineLinksDiff& links_diff);
private:
mutable base::Lock lock_;
AddressMap cached_address_map_ GUARDED_BY(lock_);
std::unordered_set<int> cached_online_links_ GUARDED_BY(lock_);
};
} // namespace net
#endif // NET_BASE_ADDRESS_MAP_CACHE_LINUX_H_

View File

@@ -0,0 +1,18 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/address_map_linux.h"
#include <linux/rtnetlink.h>
namespace net {
internal::AddressTrackerLinux* AddressMapOwnerLinux::GetAddressTrackerLinux() {
return nullptr;
}
AddressMapCacheLinux* AddressMapOwnerLinux::GetAddressMapCacheLinux() {
return nullptr;
}
} // namespace net

View File

@@ -0,0 +1,76 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_ADDRESS_MAP_LINUX_H_
#define NET_BASE_ADDRESS_MAP_LINUX_H_
#include <map>
#include <optional>
#include <unordered_set>
#include "base/containers/flat_map.h"
#include "base/functional/callback_forward.h"
#include "net/base/ip_address.h"
#include "net/base/net_export.h"
struct ifaddrmsg;
namespace net {
class AddressMapCacheLinux;
namespace internal {
class AddressTrackerLinux;
}
// Various components of //net need to access a real-time-updated AddressMap
// (see comments below). For example, AddressSorterPosix (used in DNS
// resolution) and GetNetworkList() (used in many places).
// The methods defined in this interface should be safe to call from any thread.
class NET_EXPORT AddressMapOwnerLinux {
public:
// A map from net::IPAddress to netlink's ifaddrmsg, which includes
// information about the network interface that the IP address is associated
// with (e.g. interface index).
using AddressMap = std::map<IPAddress, struct ifaddrmsg>;
// Represents a diff between one AddressMap and a new one. IPAddresses that
// map to std::nullopt have been deleted from the map, and IPAddresses that
// map to non-nullopt have been added or updated.
using AddressMapDiff =
base::flat_map<IPAddress, std::optional<struct ifaddrmsg>>;
// Represents a diff between one set of online links and new one. Interface
// indices that map to true are newly online and indices that map to false are
// newly offline.
using OnlineLinksDiff = base::flat_map<int, bool>;
// A callback for diffs, to be used by AddressTrackerLinux.
using DiffCallback =
base::RepeatingCallback<void(const AddressMapDiff& addr_diff,
const OnlineLinksDiff&)>;
AddressMapOwnerLinux() = default;
AddressMapOwnerLinux(const AddressMapOwnerLinux&) = delete;
AddressMapOwnerLinux& operator=(const AddressMapOwnerLinux&) = delete;
virtual ~AddressMapOwnerLinux() = default;
// These functions can be called on any thread. Implementations should use
// locking if necessary.
// Returns the current AddressMap.
virtual AddressMap GetAddressMap() const = 0;
// Returns set of interface indices for online interfaces.
virtual std::unordered_set<int> GetOnlineLinks() const = 0;
// These are the concrete implementations of AddressMapOwnerLinux and these
// functions serve as an ad-hoc dynamic cast to the concrete implementation,
// so this base class is not polluted with methods that end up unimplemented
// in one subclass.
virtual internal::AddressTrackerLinux* GetAddressTrackerLinux();
virtual AddressMapCacheLinux* GetAddressMapCacheLinux();
};
} // namespace net
#endif // NET_BASE_ADDRESS_MAP_LINUX_H_

View File

@@ -0,0 +1,696 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/address_tracker_linux.h"
#include <errno.h>
#include <linux/if.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <optional>
#include <utility>
#include <vector>
#include "base/check.h"
#include "base/dcheck_is_on.h"
#include "base/files/scoped_file.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/page_size.h"
#include "base/posix/eintr_wrapper.h"
#include "base/sequence_checker.h"
#include "base/task/current_thread.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "net/base/network_interfaces_linux.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/build_info.h"
#endif
namespace net::internal {
namespace {
// Some kernel functions such as wireless_send_event and rtnetlink_ifinfo_prep
// may send spurious messages over rtnetlink. RTM_NEWLINK messages where
// ifi_change == 0 and rta_type == IFLA_WIRELESS should be ignored.
bool IgnoreWirelessChange(const struct ifinfomsg* msg, int length) {
for (const struct rtattr* attr = IFLA_RTA(msg); RTA_OK(attr, length);
attr = RTA_NEXT(attr, length)) {
if (attr->rta_type == IFLA_WIRELESS && msg->ifi_change == 0)
return true;
}
return false;
}
// Retrieves address from NETLINK address message.
// Sets |really_deprecated| for IPv6 addresses with preferred lifetimes of 0.
// Precondition: |header| must already be validated with NLMSG_OK.
bool GetAddress(const struct nlmsghdr* header,
int header_length,
IPAddress* out,
bool* really_deprecated) {
if (really_deprecated)
*really_deprecated = false;
// Extract the message and update |header_length| to be the number of
// remaining bytes.
const struct ifaddrmsg* msg =
reinterpret_cast<const struct ifaddrmsg*>(NLMSG_DATA(header));
header_length -= NLMSG_HDRLEN;
size_t address_length = 0;
switch (msg->ifa_family) {
case AF_INET:
address_length = IPAddress::kIPv4AddressSize;
break;
case AF_INET6:
address_length = IPAddress::kIPv6AddressSize;
break;
default:
// Unknown family.
return false;
}
// Use IFA_ADDRESS unless IFA_LOCAL is present. This behavior here is based on
// getaddrinfo in glibc (check_pf.c). Judging from kernel implementation of
// NETLINK, IPv4 addresses have only the IFA_ADDRESS attribute, while IPv6
// have the IFA_LOCAL attribute.
uint8_t* address = nullptr;
uint8_t* local = nullptr;
int length = IFA_PAYLOAD(header);
if (length > header_length) {
LOG(ERROR) << "ifaddrmsg length exceeds bounds";
return false;
}
for (const struct rtattr* attr =
reinterpret_cast<const struct rtattr*>(IFA_RTA(msg));
RTA_OK(attr, length); attr = RTA_NEXT(attr, length)) {
switch (attr->rta_type) {
case IFA_ADDRESS:
if (RTA_PAYLOAD(attr) < address_length) {
LOG(ERROR) << "attr does not have enough bytes to read an address";
return false;
}
address = reinterpret_cast<uint8_t*>(RTA_DATA(attr));
break;
case IFA_LOCAL:
if (RTA_PAYLOAD(attr) < address_length) {
LOG(ERROR) << "attr does not have enough bytes to read an address";
return false;
}
local = reinterpret_cast<uint8_t*>(RTA_DATA(attr));
break;
case IFA_CACHEINFO: {
if (RTA_PAYLOAD(attr) < sizeof(struct ifa_cacheinfo)) {
LOG(ERROR)
<< "attr does not have enough bytes to read an ifa_cacheinfo";
return false;
}
const struct ifa_cacheinfo* cache_info =
reinterpret_cast<const struct ifa_cacheinfo*>(RTA_DATA(attr));
if (really_deprecated)
*really_deprecated = (cache_info->ifa_prefered == 0);
} break;
default:
break;
}
}
if (local)
address = local;
if (!address)
return false;
*out = IPAddress(address, address_length);
return true;
}
// SafelyCastNetlinkMsgData<T> performs a bounds check before casting |header|'s
// data to a |T*|. When the bounds check fails, returns nullptr.
template <typename T>
T* SafelyCastNetlinkMsgData(const struct nlmsghdr* header, int length) {
DCHECK(NLMSG_OK(header, static_cast<__u32>(length)));
if (length <= 0 || static_cast<size_t>(length) < NLMSG_HDRLEN + sizeof(T))
return nullptr;
return reinterpret_cast<const T*>(NLMSG_DATA(header));
}
} // namespace
// static
char* AddressTrackerLinux::GetInterfaceName(int interface_index, char* buf) {
memset(buf, 0, IFNAMSIZ);
base::ScopedFD ioctl_socket = GetSocketForIoctl();
if (!ioctl_socket.is_valid())
return buf;
struct ifreq ifr = {};
ifr.ifr_ifindex = interface_index;
if (ioctl(ioctl_socket.get(), SIOCGIFNAME, &ifr) == 0)
strncpy(buf, ifr.ifr_name, IFNAMSIZ - 1);
return buf;
}
AddressTrackerLinux::AddressTrackerLinux()
: get_interface_name_(GetInterfaceName),
address_callback_(base::DoNothing()),
link_callback_(base::DoNothing()),
tunnel_callback_(base::DoNothing()),
ignored_interfaces_(),
connection_type_initialized_cv_(&connection_type_lock_),
tracking_(false) {}
AddressTrackerLinux::AddressTrackerLinux(
const base::RepeatingClosure& address_callback,
const base::RepeatingClosure& link_callback,
const base::RepeatingClosure& tunnel_callback,
const std::unordered_set<std::string>& ignored_interfaces,
scoped_refptr<base::SequencedTaskRunner> blocking_thread_runner)
: get_interface_name_(GetInterfaceName),
address_callback_(address_callback),
link_callback_(link_callback),
tunnel_callback_(tunnel_callback),
ignored_interfaces_(ignored_interfaces),
connection_type_initialized_cv_(&connection_type_lock_),
tracking_(true),
sequenced_task_runner_(std::move(blocking_thread_runner)) {
DCHECK(!address_callback.is_null());
DCHECK(!link_callback.is_null());
DETACH_FROM_SEQUENCE(sequence_checker_);
}
AddressTrackerLinux::~AddressTrackerLinux() = default;
void AddressTrackerLinux::InitWithFdForTesting(base::ScopedFD fd) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
netlink_fd_ = std::move(fd);
DumpInitialAddressesAndWatch();
}
void AddressTrackerLinux::Init() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#if BUILDFLAG(IS_ANDROID)
// RTM_GETLINK stopped working in Android 11 (see
// https://developer.android.com/preview/privacy/mac-address),
// so AddressTrackerLinux should not be used in later versions
// of Android. Chromium code doesn't need it past Android P.
DCHECK_LT(base::android::BuildInfo::GetInstance()->sdk_int(),
base::android::SDK_VERSION_P);
#endif
netlink_fd_.reset(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE));
if (!netlink_fd_.is_valid()) {
PLOG(ERROR) << "Could not create NETLINK socket";
AbortAndForceOnline();
return;
}
int rv;
if (tracking_) {
// Request notifications.
struct sockaddr_nl addr = {};
addr.nl_family = AF_NETLINK;
addr.nl_pid = 0; // Let the kernel select a unique value.
// TODO(szym): Track RTMGRP_LINK as well for ifi_type,
// http://crbug.com/113993
addr.nl_groups =
RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY | RTMGRP_LINK;
rv = bind(netlink_fd_.get(), reinterpret_cast<struct sockaddr*>(&addr),
sizeof(addr));
if (rv < 0) {
PLOG(ERROR) << "Could not bind NETLINK socket";
AbortAndForceOnline();
return;
}
}
DumpInitialAddressesAndWatch();
}
bool AddressTrackerLinux::DidTrackingInitSucceedForTesting() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(tracking_);
return watcher_ != nullptr;
}
void AddressTrackerLinux::AbortAndForceOnline() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
watcher_.reset();
netlink_fd_.reset();
AddressTrackerAutoLock lock(*this, connection_type_lock_);
current_connection_type_ = NetworkChangeNotifier::CONNECTION_UNKNOWN;
connection_type_initialized_ = true;
connection_type_initialized_cv_.Broadcast();
}
AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
AddressTrackerAutoLock lock(*this, address_map_lock_);
return address_map_;
}
std::unordered_set<int> AddressTrackerLinux::GetOnlineLinks() const {
AddressTrackerAutoLock lock(*this, online_links_lock_);
return online_links_;
}
AddressTrackerLinux* AddressTrackerLinux::GetAddressTrackerLinux() {
return this;
}
std::pair<AddressTrackerLinux::AddressMap, std::unordered_set<int>>
AddressTrackerLinux::GetInitialDataAndStartRecordingDiffs() {
DCHECK(tracking_);
AddressTrackerAutoLock lock_address_map(*this, address_map_lock_);
AddressTrackerAutoLock lock_online_links(*this, online_links_lock_);
address_map_diff_ = AddressMapDiff();
online_links_diff_ = OnlineLinksDiff();
return {address_map_, online_links_};
}
void AddressTrackerLinux::SetDiffCallback(DiffCallback diff_callback) {
DCHECK(tracking_);
DCHECK(sequenced_task_runner_);
if (!sequenced_task_runner_->RunsTasksInCurrentSequence()) {
sequenced_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&AddressTrackerLinux::SetDiffCallback,
weak_ptr_factory_.GetWeakPtr(),
std::move(diff_callback)));
return;
}
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#if DCHECK_IS_ON()
{
// GetInitialDataAndStartRecordingDiffs() must be called before
// SetDiffCallback().
AddressTrackerAutoLock lock_address_map(*this, address_map_lock_);
AddressTrackerAutoLock lock_online_links(*this, online_links_lock_);
DCHECK(address_map_diff_.has_value());
DCHECK(online_links_diff_.has_value());
}
#endif // DCHECK_IS_ON()
diff_callback_ = std::move(diff_callback);
RunDiffCallback();
}
bool AddressTrackerLinux::IsInterfaceIgnored(int interface_index) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ignored_interfaces_.empty())
return false;
char buf[IFNAMSIZ] = {0};
const char* interface_name = get_interface_name_(interface_index, buf);
return ignored_interfaces_.find(interface_name) != ignored_interfaces_.end();
}
NetworkChangeNotifier::ConnectionType
AddressTrackerLinux::GetCurrentConnectionType() {
// http://crbug.com/125097
base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
AddressTrackerAutoLock lock(*this, connection_type_lock_);
// Make sure the initial connection type is set before returning.
threads_waiting_for_connection_type_initialization_++;
while (!connection_type_initialized_) {
connection_type_initialized_cv_.Wait();
}
threads_waiting_for_connection_type_initialization_--;
return current_connection_type_;
}
void AddressTrackerLinux::DumpInitialAddressesAndWatch() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Request dump of addresses.
struct sockaddr_nl peer = {};
peer.nl_family = AF_NETLINK;
struct {
struct nlmsghdr header;
struct rtgenmsg msg;
} request = {};
request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
request.header.nlmsg_type = RTM_GETADDR;
request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
request.header.nlmsg_pid = 0; // This field is opaque to netlink.
request.msg.rtgen_family = AF_UNSPEC;
int rv = HANDLE_EINTR(
sendto(netlink_fd_.get(), &request, request.header.nlmsg_len, 0,
reinterpret_cast<struct sockaddr*>(&peer), sizeof(peer)));
if (rv < 0) {
PLOG(ERROR) << "Could not send NETLINK request";
AbortAndForceOnline();
return;
}
// Consume pending message to populate the AddressMap, but don't notify.
// Sending another request without first reading responses results in EBUSY.
bool address_changed;
bool link_changed;
bool tunnel_changed;
ReadMessages(&address_changed, &link_changed, &tunnel_changed);
// Request dump of link state
request.header.nlmsg_type = RTM_GETLINK;
rv = HANDLE_EINTR(
sendto(netlink_fd_.get(), &request, request.header.nlmsg_len, 0,
reinterpret_cast<struct sockaddr*>(&peer), sizeof(peer)));
if (rv < 0) {
PLOG(ERROR) << "Could not send NETLINK request";
AbortAndForceOnline();
return;
}
// Consume pending message to populate links_online_, but don't notify.
ReadMessages(&address_changed, &link_changed, &tunnel_changed);
{
AddressTrackerAutoLock lock(*this, connection_type_lock_);
connection_type_initialized_ = true;
connection_type_initialized_cv_.Broadcast();
}
if (tracking_) {
DCHECK(!sequenced_task_runner_ ||
sequenced_task_runner_->RunsTasksInCurrentSequence());
watcher_ = base::FileDescriptorWatcher::WatchReadable(
netlink_fd_.get(),
base::BindRepeating(&AddressTrackerLinux::OnFileCanReadWithoutBlocking,
base::Unretained(this)));
}
}
void AddressTrackerLinux::ReadMessages(bool* address_changed,
bool* link_changed,
bool* tunnel_changed) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
*address_changed = false;
*link_changed = false;
*tunnel_changed = false;
bool first_loop = true;
// Varying sources have different opinions regarding the buffer size needed
// for netlink messages to avoid truncation:
// - The official documentation on netlink says messages are generally 8kb
// or the system page size, whichever is *larger*:
// https://www.kernel.org/doc/html/v6.2/userspace-api/netlink/intro.html#buffer-sizing
// - The kernel headers would imply that messages are generally the system
// page size or 8kb, whichever is *smaller*:
// https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/include/linux/netlink.h?h=v6.2.2#n226
// (libmnl follows this.)
// - The netlink(7) man page's example always uses a fixed size 8kb buffer:
// https://man7.org/linux/man-pages/man7/netlink.7.html
// Here, we follow the guidelines in the documentation, for two primary
// reasons:
// - Erring on the side of a larger size is the safer way to go to avoid
// MSG_TRUNC.
// - Since this is heap-allocated anyway, there's no risk to the stack by
// using the larger size.
constexpr size_t kMinNetlinkBufferSize = 8 * 1024;
std::vector<char> buffer(
std::max(base::GetPageSize(), kMinNetlinkBufferSize));
{
std::optional<base::ScopedBlockingCall> blocking_call;
if (tracking_) {
// If the loop below takes a long time to run, a new thread should added
// to the current thread pool to ensure forward progress of all tasks.
blocking_call.emplace(FROM_HERE, base::BlockingType::MAY_BLOCK);
}
for (;;) {
int rv =
HANDLE_EINTR(recv(netlink_fd_.get(), buffer.data(), buffer.size(),
// Block the first time through loop.
first_loop ? 0 : MSG_DONTWAIT));
first_loop = false;
if (rv == 0) {
LOG(ERROR) << "Unexpected shutdown of NETLINK socket.";
return;
}
if (rv < 0) {
if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
break;
PLOG(ERROR) << "Failed to recv from netlink socket";
return;
}
HandleMessage(buffer.data(), rv, address_changed, link_changed,
tunnel_changed);
}
}
if (*link_changed || *address_changed)
UpdateCurrentConnectionType();
}
void AddressTrackerLinux::HandleMessage(const char* buffer,
int length,
bool* address_changed,
bool* link_changed,
bool* tunnel_changed) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(buffer);
// Note that NLMSG_NEXT decrements |length| to reflect the number of bytes
// remaining in |buffer|.
for (const struct nlmsghdr* header =
reinterpret_cast<const struct nlmsghdr*>(buffer);
length >= 0 && NLMSG_OK(header, static_cast<__u32>(length));
header = NLMSG_NEXT(header, length)) {
// The |header| pointer should never precede |buffer|.
DCHECK_LE(buffer, reinterpret_cast<const char*>(header));
switch (header->nlmsg_type) {
case NLMSG_DONE:
return;
case NLMSG_ERROR: {
const struct nlmsgerr* msg =
SafelyCastNetlinkMsgData<const struct nlmsgerr>(header, length);
if (msg == nullptr)
return;
LOG(ERROR) << "Unexpected netlink error " << msg->error << ".";
} return;
case RTM_NEWADDR: {
IPAddress address;
bool really_deprecated;
const struct ifaddrmsg* msg =
SafelyCastNetlinkMsgData<const struct ifaddrmsg>(header, length);
if (msg == nullptr)
return;
if (IsInterfaceIgnored(msg->ifa_index))
break;
if (GetAddress(header, length, &address, &really_deprecated)) {
struct ifaddrmsg msg_copy = *msg;
AddressTrackerAutoLock lock(*this, address_map_lock_);
// Routers may frequently (every few seconds) output the IPv6 ULA
// prefix which can cause the linux kernel to frequently output two
// back-to-back messages, one without the deprecated flag and one with
// the deprecated flag but both with preferred lifetimes of 0. Avoid
// interpreting this as an actual change by canonicalizing the two
// messages by setting the deprecated flag based on the preferred
// lifetime also. http://crbug.com/268042
if (really_deprecated)
msg_copy.ifa_flags |= IFA_F_DEPRECATED;
// Only indicate change if the address is new or ifaddrmsg info has
// changed.
auto it = address_map_.find(address);
if (it == address_map_.end()) {
address_map_.insert(it, std::pair(address, msg_copy));
*address_changed = true;
} else if (memcmp(&it->second, &msg_copy, sizeof(msg_copy))) {
it->second = msg_copy;
*address_changed = true;
}
if (*address_changed && address_map_diff_.has_value()) {
(*address_map_diff_)[address] = msg_copy;
}
}
} break;
case RTM_DELADDR: {
IPAddress address;
const struct ifaddrmsg* msg =
SafelyCastNetlinkMsgData<const struct ifaddrmsg>(header, length);
if (msg == nullptr)
return;
if (IsInterfaceIgnored(msg->ifa_index))
break;
if (GetAddress(header, length, &address, nullptr)) {
AddressTrackerAutoLock lock(*this, address_map_lock_);
if (address_map_.erase(address)) {
*address_changed = true;
if (address_map_diff_.has_value()) {
(*address_map_diff_)[address] = std::nullopt;
}
}
}
} break;
case RTM_NEWLINK: {
const struct ifinfomsg* msg =
SafelyCastNetlinkMsgData<const struct ifinfomsg>(header, length);
if (msg == nullptr)
return;
if (IsInterfaceIgnored(msg->ifi_index))
break;
if (IgnoreWirelessChange(msg, IFLA_PAYLOAD(header))) {
VLOG(2) << "Ignoring RTM_NEWLINK message";
break;
}
if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) &&
(msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) {
AddressTrackerAutoLock lock(*this, online_links_lock_);
if (online_links_.insert(msg->ifi_index).second) {
*link_changed = true;
if (online_links_diff_.has_value()) {
(*online_links_diff_)[msg->ifi_index] = true;
}
if (IsTunnelInterface(msg->ifi_index))
*tunnel_changed = true;
}
} else {
AddressTrackerAutoLock lock(*this, online_links_lock_);
if (online_links_.erase(msg->ifi_index)) {
*link_changed = true;
if (online_links_diff_.has_value()) {
(*online_links_diff_)[msg->ifi_index] = false;
}
if (IsTunnelInterface(msg->ifi_index))
*tunnel_changed = true;
}
}
} break;
case RTM_DELLINK: {
const struct ifinfomsg* msg =
SafelyCastNetlinkMsgData<const struct ifinfomsg>(header, length);
if (msg == nullptr)
return;
if (IsInterfaceIgnored(msg->ifi_index))
break;
AddressTrackerAutoLock lock(*this, online_links_lock_);
if (online_links_.erase(msg->ifi_index)) {
*link_changed = true;
if (online_links_diff_.has_value()) {
(*online_links_diff_)[msg->ifi_index] = false;
}
if (IsTunnelInterface(msg->ifi_index))
*tunnel_changed = true;
}
} break;
default:
break;
}
}
}
void AddressTrackerLinux::OnFileCanReadWithoutBlocking() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bool address_changed;
bool link_changed;
bool tunnel_changed;
ReadMessages(&address_changed, &link_changed, &tunnel_changed);
if (diff_callback_) {
RunDiffCallback();
}
if (address_changed) {
address_callback_.Run();
}
if (link_changed) {
link_callback_.Run();
}
if (tunnel_changed) {
tunnel_callback_.Run();
}
}
bool AddressTrackerLinux::IsTunnelInterface(int interface_index) const {
char buf[IFNAMSIZ] = {0};
return IsTunnelInterfaceName(get_interface_name_(interface_index, buf));
}
// static
bool AddressTrackerLinux::IsTunnelInterfaceName(const char* name) {
// Linux kernel drivers/net/tun.c uses "tun" name prefix.
return strncmp(name, "tun", 3) == 0;
}
void AddressTrackerLinux::UpdateCurrentConnectionType() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
AddressTrackerLinux::AddressMap address_map = GetAddressMap();
std::unordered_set<int> online_links = GetOnlineLinks();
// Strip out tunnel interfaces from online_links
for (auto it = online_links.cbegin(); it != online_links.cend();) {
if (IsTunnelInterface(*it)) {
it = online_links.erase(it);
} else {
++it;
}
}
NetworkInterfaceList networks;
NetworkChangeNotifier::ConnectionType type =
NetworkChangeNotifier::CONNECTION_NONE;
if (GetNetworkListImpl(&networks, 0, online_links, address_map,
get_interface_name_)) {
type = NetworkChangeNotifier::ConnectionTypeFromInterfaceList(networks);
} else {
type = online_links.empty() ? NetworkChangeNotifier::CONNECTION_NONE
: NetworkChangeNotifier::CONNECTION_UNKNOWN;
}
AddressTrackerAutoLock lock(*this, connection_type_lock_);
current_connection_type_ = type;
}
void AddressTrackerLinux::RunDiffCallback() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(tracking_);
DCHECK(address_map_diff_.has_value());
DCHECK(online_links_diff_.has_value());
// It's fine to access `address_map_diff_` and `online_links_diff_` without
// any locking here, as the only time they are ever accessed on another thread
// is in GetInitialDataAndStartRecordingDiffs(). But
// GetInitialDataAndStartRecordingDiffs() must be called before
// SetDiffCallback(), which must be called before RunDiffCallback(), so this
// function cannot overlap with any modifications on another thread.
// There should be a diff or the DiffCallback shouldn't be run.
if (address_map_diff_->empty() && online_links_diff_->empty()) {
return;
}
diff_callback_.Run(address_map_diff_.value(), online_links_diff_.value());
address_map_diff_->clear();
online_links_diff_->clear();
}
int AddressTrackerLinux::GetThreadsWaitingForConnectionTypeInitForTesting() {
AddressTrackerAutoLock lock(*this, connection_type_lock_);
return threads_waiting_for_connection_type_initialization_;
}
AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock(
const AddressTrackerLinux& tracker,
base::Lock& lock)
: tracker_(tracker), lock_(lock) {
if (tracker_->tracking_) {
lock_->Acquire();
} else {
DCHECK_CALLED_ON_VALID_SEQUENCE(tracker_->sequence_checker_);
}
}
AddressTrackerLinux::AddressTrackerAutoLock::~AddressTrackerAutoLock() {
if (tracker_->tracking_) {
lock_->AssertAcquired();
lock_->Release();
} else {
DCHECK_CALLED_ON_VALID_SEQUENCE(tracker_->sequence_checker_);
}
}
} // namespace net::internal

View File

@@ -0,0 +1,291 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_ADDRESS_TRACKER_LINUX_H_
#define NET_BASE_ADDRESS_TRACKER_LINUX_H_
#include <sys/socket.h> // Needed to include netlink.
// Mask superfluous definition of |struct net|. This is fixed in Linux 2.6.38.
#define net net_kernel
#include <linux/rtnetlink.h>
#undef net
#include <stddef.h>
#include <map>
#include <memory>
#include <string>
#include <unordered_set>
#include "base/compiler_specific.h"
#include "base/files/file_descriptor_watcher_posix.h"
#include "base/files/scoped_file.h"
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ref.h"
#include "base/sequence_checker.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "base/task/sequenced_task_runner.h"
#include "base/thread_annotations.h"
#include "net/base/address_map_linux.h"
#include "net/base/ip_address.h"
#include "net/base/net_export.h"
#include "net/base/network_change_notifier.h"
namespace net::test {
class AddressTrackerLinuxTest;
}
namespace net::internal {
// Keeps track of network interface addresses using rtnetlink. Used by
// NetworkChangeNotifier to provide signals to registered IPAddressObservers.
//
// In tracking mode, this class should mostly be used on a single sequence,
// except GetAddressMap() and GetOnlineLinks() (AddressMapOwnerLinux overrides)
// which can be called on any thread. The main sequence should be able to block
// (e.g. use a base::SequencedTaskRunner with base::MayBlock()).
//
// In non-tracking mode this should be used on a single thread.
class NET_EXPORT_PRIVATE AddressTrackerLinux : public AddressMapOwnerLinux {
public:
// Non-tracking version constructor: it takes a snapshot of the
// current system configuration. Once Init() returns, the
// configuration is available through GetOnlineLinks() and
// GetAddressMap().
AddressTrackerLinux();
// Tracking version constructor: it will run |address_callback| when
// the AddressMap changes, |link_callback| when the list of online
// links changes, and |tunnel_callback| when the list of online
// tunnels changes.
// |ignored_interfaces| is the list of interfaces to ignore. Changes to an
// ignored interface will not cause any callback to be run. An ignored
// interface will not have entries in GetAddressMap() and GetOnlineLinks().
// NOTE: Only ignore interfaces not used to connect to the internet. Adding
// interfaces used to connect to the internet can cause critical network
// changed signals to be lost allowing incorrect stale state to persist.
//
// |blocking_thread_runner| is the sequence on which this AddressTrackerLinux
// will run. The AddressTrackerLinux can block in tracking mode and so it
// should run on a sequence that can block, e.g. a base::SequencedTaskRunner
// with base::MayBlock(). If nullptr, SetDiffCallback() cannot be used off of
// the AddressTrackerLinux's sequence.
AddressTrackerLinux(const base::RepeatingClosure& address_callback,
const base::RepeatingClosure& link_callback,
const base::RepeatingClosure& tunnel_callback,
const std::unordered_set<std::string>& ignored_interfaces,
scoped_refptr<base::SequencedTaskRunner>
blocking_thread_runner = nullptr);
~AddressTrackerLinux() override;
// In tracking mode, it starts watching the system configuration for
// changes. The current thread must have a MessageLoopForIO. In
// non-tracking mode, once Init() returns, a snapshot of the system
// configuration is available through GetOnlineLinks() and
// GetAddressMap().
void Init();
// Same as Init(), except instead of creating and binding a netlink socket,
// this AddressTrackerLinux will send and receive messages from |fd|.
void InitWithFdForTesting(base::ScopedFD fd);
// AddressMapOwnerLinux implementation (callable on any thread):
AddressMap GetAddressMap() const override;
// Returns set of interface indices for online interfaces.
std::unordered_set<int> GetOnlineLinks() const override;
AddressTrackerLinux* GetAddressTrackerLinux() override;
// This returns the current AddressMap and set of online links, and atomically
// starts recording diffs to those structures. This can be called on any
// thread, and must be called called before SetDiffCallback() below. Available
// only in tracking mode.
std::pair<AddressMap, std::unordered_set<int>>
GetInitialDataAndStartRecordingDiffs();
// Called after GetInitialDataAndStartRecordingDiffs().
//
// Whenever the AddressMap or the set of online links (returned by the above
// two methods) changes, this callback is called on AddressTrackerLinux's
// sequence. On the first call, |diff_callback| is called synchronously with
// the set of diffs that have been built since
// GetInitialDataAndStartRecordingDiffs() was called. If there are none,
// |diff_callback| won't be called.
//
// This is only available in tracking mode. It can be called on any thread,
// but it will post a task to the AddressTrackerLinux's sequence and therefore
// will finish asynchronously. The caller MUST ENSURE that the
// AddressTrackerLinux is not deleted until this task finishes.
// This also requires |sequenced_task_runner_| to be set by the
// AddressTrackerLinux constructor above.
//
// Note that other threads may see updated AddressMaps by calling
// GetAddressMap() before |diff_callback| is ever called.
void SetDiffCallback(DiffCallback diff_callback);
// Implementation of NetworkChangeNotifierLinux::GetCurrentConnectionType().
// Safe to call from any thread, but will block until Init() has completed.
NetworkChangeNotifier::ConnectionType GetCurrentConnectionType();
// Returns the name for the interface with interface index |interface_index|.
// |buf| should be a pointer to an array of size IFNAMSIZ. The returned
// pointer will point to |buf|. This function acts like if_indextoname which
// cannot be used as net/if.h cannot be mixed with linux/if.h. We'll stick
// with exclusively talking to the kernel and not the C library.
static char* GetInterfaceName(int interface_index, char* buf);
// Does |name| refer to a tunnel interface?
static bool IsTunnelInterfaceName(const char* name);
private:
friend class net::test::AddressTrackerLinuxTest;
FRIEND_TEST_ALL_PREFIXES(AddressTrackerLinuxNetlinkTest,
TestInitializeTwoTrackers);
FRIEND_TEST_ALL_PREFIXES(AddressTrackerLinuxNetlinkTest,
TestInitializeTwoTrackersInPidNamespaces);
friend int ChildProcessInitializeTrackerForTesting();
// In tracking mode, holds |lock| while alive. In non-tracking mode,
// enforces single-threaded access.
class SCOPED_LOCKABLE AddressTrackerAutoLock {
public:
AddressTrackerAutoLock(const AddressTrackerLinux& tracker, base::Lock& lock)
EXCLUSIVE_LOCK_FUNCTION(lock);
AddressTrackerAutoLock(const AddressTrackerAutoLock&) = delete;
AddressTrackerAutoLock& operator=(const AddressTrackerAutoLock&) = delete;
~AddressTrackerAutoLock() UNLOCK_FUNCTION();
private:
const raw_ref<const AddressTrackerLinux> tracker_;
const raw_ref<base::Lock> lock_;
};
// A function that returns the name of an interface given the interface index
// in |interface_index|. |ifname| should be a buffer of size IFNAMSIZ. The
// function should return a pointer to |ifname|.
typedef char* (*GetInterfaceNameFunction)(int interface_index, char* ifname);
// Retrieves a dump of the current AddressMap and set of online links as part
// of initialization. Expects |netlink_fd_| to exist already.
void DumpInitialAddressesAndWatch();
// Sets |*address_changed| to indicate whether |address_map_| changed and
// sets |*link_changed| to indicate if |online_links_| changed and sets
// |*tunnel_changed| to indicate if |online_links_| changed with regards to a
// tunnel interface while reading messages from |netlink_fd_|.
//
// If |address_map_| has changed and |address_map_diff_| is not nullopt,
// |*address_map_diff_| is populated with the changes to the AddressMap.
// Similarly, if |online_links_| has changed and |online_links_diff_| is not
// nullopt, |*online_links_diff| is populated with the changes to the set of
// online links.
void ReadMessages(bool* address_changed,
bool* link_changed,
bool* tunnel_changed);
// Sets |*address_changed| to true if |address_map_| changed, sets
// |*link_changed| to true if |online_links_| changed, sets |*tunnel_changed|
// to true if |online_links_| changed with regards to a tunnel interface while
// reading the message from |buffer|.
//
// If |address_map_| has changed and |address_map_diff_| is not nullopt,
// |*address_map_diff_| is populated with the changes to the AddressMap.
// Similarly, if |online_links_| has changed and |online_links_diff_| is not
// nullopt, |*online_links_diff| is populated with the changes to the set of
// online links.
void HandleMessage(const char* buffer,
int length,
bool* address_changed,
bool* link_changed,
bool* tunnel_changed);
// Call when some part of initialization failed; forces online and unblocks.
void AbortAndForceOnline();
// Called by |watcher_| when |netlink_fd_| can be read without blocking.
void OnFileCanReadWithoutBlocking();
// Does |interface_index| refer to a tunnel interface?
bool IsTunnelInterface(int interface_index) const;
// Is interface with index |interface_index| in list of ignored interfaces?
bool IsInterfaceIgnored(int interface_index) const;
// Updates current_connection_type_ based on the network list.
void UpdateCurrentConnectionType();
// Passes |address_map_diff_| and |online_links_diff_| to |diff_callback_| as
// arguments, and then clears them.
void RunDiffCallback();
// Used by AddressTrackerLinuxTest, returns the number of threads waiting
// for |connection_type_initialized_cv_|.
int GetThreadsWaitingForConnectionTypeInitForTesting();
// Used by AddressTrackerLinuxNetlinkTest, returns true iff `Init` succeeded.
// Undefined for non-tracking mode.
bool DidTrackingInitSucceedForTesting() const;
AddressMapDiff& address_map_diff_for_testing() {
AddressTrackerAutoLock lock(*this, address_map_lock_);
return address_map_diff_.value();
}
OnlineLinksDiff& online_links_diff_for_testing() {
AddressTrackerAutoLock lock(*this, online_links_lock_);
return online_links_diff_.value();
}
// Gets the name of an interface given the interface index |interface_index|.
// May return empty string if it fails but should not return NULL. This is
// overridden by tests.
GetInterfaceNameFunction get_interface_name_;
DiffCallback diff_callback_ GUARDED_BY_CONTEXT(sequence_checker_);
base::RepeatingClosure address_callback_
GUARDED_BY_CONTEXT(sequence_checker_);
base::RepeatingClosure link_callback_ GUARDED_BY_CONTEXT(sequence_checker_);
base::RepeatingClosure tunnel_callback_ GUARDED_BY_CONTEXT(sequence_checker_);
// Note that |watcher_| must be inactive when |netlink_fd_| is closed.
base::ScopedFD netlink_fd_ GUARDED_BY_CONTEXT(sequence_checker_);
std::unique_ptr<base::FileDescriptorWatcher::Controller> watcher_
GUARDED_BY_CONTEXT(sequence_checker_);
mutable base::Lock address_map_lock_;
AddressMap address_map_ GUARDED_BY(address_map_lock_);
std::optional<AddressMapDiff> address_map_diff_;
// Set of interface indices for links that are currently online.
mutable base::Lock online_links_lock_ ACQUIRED_AFTER(address_map_lock_);
std::unordered_set<int> online_links_ GUARDED_BY(online_links_lock_);
std::optional<OnlineLinksDiff> online_links_diff_;
// Set of interface names that should be ignored.
const std::unordered_set<std::string> ignored_interfaces_;
base::Lock connection_type_lock_;
bool connection_type_initialized_ GUARDED_BY(connection_type_lock_) = false;
base::ConditionVariable connection_type_initialized_cv_;
NetworkChangeNotifier::ConnectionType current_connection_type_ GUARDED_BY(
connection_type_lock_) = NetworkChangeNotifier::CONNECTION_NONE;
int threads_waiting_for_connection_type_initialization_
GUARDED_BY(connection_type_lock_) = 0;
const bool tracking_;
// This can be set by the tracking constructor.
scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
// This SequenceChecker is still useful so instance variables above can be
// marked GUARDED_BY_CONTEXT(sequence_checker_).
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<AddressTrackerLinux> weak_ptr_factory_{this};
};
} // namespace net::internal
#endif // NET_BASE_ADDRESS_TRACKER_LINUX_H_

View File

@@ -0,0 +1,145 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/address_tracker_linux_test_util.h"
#include <linux/if.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <stdint.h>
#include <string.h>
#include <vector>
#include "base/check_op.h"
#include "base/logging.h"
#include "net/base/ip_address.h"
bool operator==(const struct ifaddrmsg& lhs, const struct ifaddrmsg& rhs) {
return memcmp(&lhs, &rhs, sizeof(struct ifaddrmsg)) == 0;
}
namespace net::test {
NetlinkMessage::NetlinkMessage(uint16_t type) : buffer_(NLMSG_HDRLEN) {
header()->nlmsg_type = type;
Align();
}
NetlinkMessage::~NetlinkMessage() = default;
void NetlinkMessage::AddPayload(const void* data, size_t length) {
CHECK_EQ(static_cast<size_t>(NLMSG_HDRLEN), buffer_.size())
<< "Payload must be added first";
Append(data, length);
Align();
}
void NetlinkMessage::AddAttribute(uint16_t type,
const void* data,
size_t length) {
struct nlattr attr;
attr.nla_len = NLA_HDRLEN + length;
attr.nla_type = type;
Append(&attr, sizeof(attr));
Align();
Append(data, length);
Align();
}
void NetlinkMessage::AppendTo(NetlinkBuffer* output) const {
CHECK_EQ(NLMSG_ALIGN(output->size()), output->size());
output->insert(output->end(), buffer_.begin(), buffer_.end());
}
void NetlinkMessage::Append(const void* data, size_t length) {
const char* chardata = reinterpret_cast<const char*>(data);
buffer_.insert(buffer_.end(), chardata, chardata + length);
}
void NetlinkMessage::Align() {
header()->nlmsg_len = buffer_.size();
buffer_.resize(NLMSG_ALIGN(buffer_.size()));
CHECK(NLMSG_OK(header(), buffer_.size()));
}
#define INFINITY_LIFE_TIME 0xFFFFFFFF
void MakeAddrMessageWithCacheInfo(uint16_t type,
uint8_t flags,
uint8_t family,
int index,
const IPAddress& address,
const IPAddress& local,
uint32_t preferred_lifetime,
NetlinkBuffer* output) {
NetlinkMessage nlmsg(type);
struct ifaddrmsg msg = {};
msg.ifa_family = family;
msg.ifa_flags = flags;
msg.ifa_index = index;
nlmsg.AddPayload(msg);
if (address.size()) {
nlmsg.AddAttribute(IFA_ADDRESS, address.bytes().data(), address.size());
}
if (local.size()) {
nlmsg.AddAttribute(IFA_LOCAL, local.bytes().data(), local.size());
}
struct ifa_cacheinfo cache_info = {};
cache_info.ifa_prefered = preferred_lifetime;
cache_info.ifa_valid = INFINITY_LIFE_TIME;
nlmsg.AddAttribute(IFA_CACHEINFO, &cache_info, sizeof(cache_info));
nlmsg.AppendTo(output);
}
void MakeAddrMessage(uint16_t type,
uint8_t flags,
uint8_t family,
int index,
const IPAddress& address,
const IPAddress& local,
NetlinkBuffer* output) {
MakeAddrMessageWithCacheInfo(type, flags, family, index, address, local,
INFINITY_LIFE_TIME, output);
}
void MakeLinkMessage(uint16_t type,
uint32_t flags,
uint32_t index,
NetlinkBuffer* output,
bool clear_output) {
NetlinkMessage nlmsg(type);
struct ifinfomsg msg = {};
msg.ifi_index = index;
msg.ifi_flags = flags;
msg.ifi_change = 0xFFFFFFFF;
nlmsg.AddPayload(msg);
if (clear_output) {
output->clear();
}
nlmsg.AppendTo(output);
}
// Creates a netlink message generated by wireless_send_event. These events
// should be ignored.
void MakeWirelessLinkMessage(uint16_t type,
uint32_t flags,
uint32_t index,
NetlinkBuffer* output,
bool clear_output) {
NetlinkMessage nlmsg(type);
struct ifinfomsg msg = {};
msg.ifi_index = index;
msg.ifi_flags = flags;
msg.ifi_change = 0;
nlmsg.AddPayload(msg);
char data[8] = {0};
nlmsg.AddAttribute(IFLA_WIRELESS, data, sizeof(data));
if (clear_output) {
output->clear();
}
nlmsg.AppendTo(output);
}
} // namespace net::test

View File

@@ -0,0 +1,77 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_ADDRESS_TRACKER_LINUX_TEST_UTIL_H_
#define NET_BASE_ADDRESS_TRACKER_LINUX_TEST_UTIL_H_
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <stdint.h>
#include <cstddef>
#include <vector>
bool operator==(const struct ifaddrmsg& lhs, const struct ifaddrmsg& rhs);
namespace net {
class IPAddress;
}
namespace net::test {
using NetlinkBuffer = std::vector<char>;
class NetlinkMessage {
public:
explicit NetlinkMessage(uint16_t type);
~NetlinkMessage();
void AddPayload(const void* data, size_t length);
template <typename T>
void AddPayload(const T& data) {
AddPayload(&data, sizeof(data));
}
void AddAttribute(uint16_t type, const void* data, size_t length);
void AppendTo(NetlinkBuffer* output) const;
private:
void Append(const void* data, size_t length);
void Align();
struct nlmsghdr* header() {
return reinterpret_cast<struct nlmsghdr*>(buffer_.data());
}
NetlinkBuffer buffer_;
};
void MakeAddrMessageWithCacheInfo(uint16_t type,
uint8_t flags,
uint8_t family,
int index,
const IPAddress& address,
const IPAddress& local,
uint32_t preferred_lifetime,
NetlinkBuffer* output);
void MakeAddrMessage(uint16_t type,
uint8_t flags,
uint8_t family,
int index,
const IPAddress& address,
const IPAddress& local,
NetlinkBuffer* output);
void MakeLinkMessage(uint16_t type,
uint32_t flags,
uint32_t index,
NetlinkBuffer* output,
bool clear_output = true);
void MakeWirelessLinkMessage(uint16_t type,
uint32_t flags,
uint32_t index,
NetlinkBuffer* output,
bool clear_output = true);
} // namespace net::test
#endif // NET_BASE_ADDRESS_TRACKER_LINUX_TEST_UTIL_H_

View File

@@ -0,0 +1,16 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net;
/**
* Constants for the names of Net Features.
*/
public final class NetFeatures {{
{NATIVE_FEATURES}
// Do not instantiate this class.
private NetFeatures() {{}}
}}

View File

@@ -0,0 +1 @@
This directory contains shared code between the Chrome ports to macOS and iOS.

View File

@@ -0,0 +1,27 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_APPLE_URL_CONVERSIONS_H_
#define NET_BASE_APPLE_URL_CONVERSIONS_H_
#include "net/base/net_export.h"
class GURL;
@class NSURL;
namespace net {
// Method for creating a valid NSURL (compliant with RFC 1738/1808/2396) from a
// valid GURL. This method will return nil if the |url| is not valid.
// Note that NSURLs should *always* be created from GURLs, so that GURL
// sanitization rules are applied everywhere.
NET_EXPORT NSURL* NSURLWithGURL(const GURL& url);
// Method for creating a valid GURL from a NSURL. This method will return an
// empty GURL if the |url| is nil.
NET_EXPORT GURL GURLWithNSURL(NSURL* url);
} // namespace net
#endif // NET_BASE_APPLE_URL_CONVERSIONS_H_

View File

@@ -0,0 +1,54 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "net/base/apple/url_conversions.h"
#import <Foundation/Foundation.h>
#include "base/strings/escape.h"
#include "url/gurl.h"
#include "url/url_canon.h"
namespace net {
NSURL* NSURLWithGURL(const GURL& url) {
if (!url.is_valid()) {
return nil;
}
// NSURL strictly enforces RFC 1738 which requires that certain characters
// are always encoded. These characters are: "<", ">", """, "#", "%", "{",
// "}", "|", "\", "^", "~", "[", "]", and "`".
//
// GURL leaves some of these characters unencoded in the path, query, and
// ref. This function manually encodes those components, and then passes the
// result to NSURL.
GURL::Replacements replacements;
std::string escaped_path = base::EscapeNSURLPrecursor(url.path());
std::string escaped_query = base::EscapeNSURLPrecursor(url.query());
std::string escaped_ref = base::EscapeNSURLPrecursor(url.ref());
if (!escaped_path.empty()) {
replacements.SetPathStr(escaped_path);
}
if (!escaped_query.empty()) {
replacements.SetQueryStr(escaped_query);
}
if (!escaped_ref.empty()) {
replacements.SetRefStr(escaped_ref);
}
GURL escaped_url = url.ReplaceComponents(replacements);
NSString* escaped_url_string =
[NSString stringWithUTF8String:escaped_url.spec().c_str()];
return [NSURL URLWithString:escaped_url_string];
}
GURL GURLWithNSURL(NSURL* url) {
if (url) {
return GURL(url.absoluteString.UTF8String);
}
return GURL();
}
} // namespace net

44
src/net/base/auth.cc Normal file
View File

@@ -0,0 +1,44 @@
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/auth.h"
namespace net {
AuthChallengeInfo::AuthChallengeInfo() = default;
AuthChallengeInfo::AuthChallengeInfo(const AuthChallengeInfo& other) = default;
bool AuthChallengeInfo::MatchesExceptPath(
const AuthChallengeInfo& other) const {
return (is_proxy == other.is_proxy && challenger == other.challenger &&
scheme == other.scheme && realm == other.realm &&
challenge == other.challenge);
}
AuthChallengeInfo::~AuthChallengeInfo() = default;
AuthCredentials::AuthCredentials() = default;
AuthCredentials::AuthCredentials(const std::u16string& username,
const std::u16string& password)
: username_(username), password_(password) {}
AuthCredentials::~AuthCredentials() = default;
void AuthCredentials::Set(const std::u16string& username,
const std::u16string& password) {
username_ = username;
password_ = password;
}
bool AuthCredentials::Equals(const AuthCredentials& other) const {
return username_ == other.username_ && password_ == other.password_;
}
bool AuthCredentials::Empty() const {
return username_.empty() && password_.empty();
}
} // namespace net

93
src/net/base/auth.h Normal file
View File

@@ -0,0 +1,93 @@
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_AUTH_H__
#define NET_BASE_AUTH_H__
#include <string>
#include "net/base/net_export.h"
#include "url/scheme_host_port.h"
namespace net {
// Holds info about an authentication challenge that we may want to display
// to the user.
class NET_EXPORT AuthChallengeInfo {
public:
AuthChallengeInfo();
AuthChallengeInfo(const AuthChallengeInfo& other);
~AuthChallengeInfo();
// Returns true if this AuthChallengeInfo is equal to |other| except for
// |path|. Can be used to determine if the same credentials can be provided
// for two different requests.
bool MatchesExceptPath(const AuthChallengeInfo& other) const;
// Whether this came from a server or a proxy.
bool is_proxy = false;
// The service issuing the challenge.
url::SchemeHostPort challenger;
// The authentication scheme used, such as "basic" or "digest". The encoding
// is ASCII.
std::string scheme;
// The realm of the challenge. May be empty. The encoding is UTF-8.
std::string realm;
// The authentication challenge.
std::string challenge;
// The path on which authentication was requested.
std::string path;
};
// Authentication Credentials for an authentication credentials.
class NET_EXPORT AuthCredentials {
public:
AuthCredentials();
AuthCredentials(const std::u16string& username,
const std::u16string& password);
~AuthCredentials();
// Set the |username| and |password|.
void Set(const std::u16string& username, const std::u16string& password);
// Determines if |this| is equivalent to |other|.
bool Equals(const AuthCredentials& other) const;
// Returns true if all credentials are empty.
bool Empty() const;
const std::u16string& username() const { return username_; }
const std::u16string& password() const { return password_; }
private:
// The username to provide, possibly empty. This should be ASCII only to
// minimize compatibility problems, but arbitrary UTF-16 strings are allowed
// and will be attempted.
std::u16string username_;
// The password to provide, possibly empty. This should be ASCII only to
// minimize compatibility problems, but arbitrary UTF-16 strings are allowed
// and will be attempted.
std::u16string password_;
// Intentionally allowing the implicit copy constructor and assignment
// operators.
};
// Authentication structures
enum AuthState {
AUTH_STATE_DONT_NEED_AUTH,
AUTH_STATE_NEED_AUTH,
AUTH_STATE_HAVE_AUTH,
AUTH_STATE_CANCELED
};
} // namespace net
#endif // NET_BASE_AUTH_H__

View File

@@ -0,0 +1,187 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/backoff_entry.h"
#include <algorithm>
#include <cmath>
#include <limits>
#include "base/check_op.h"
#include "base/numerics/clamped_math.h"
#include "base/numerics/safe_math.h"
#include "base/rand_util.h"
#include "base/time/tick_clock.h"
namespace net {
BackoffEntry::BackoffEntry(const BackoffEntry::Policy* policy)
: BackoffEntry(policy, nullptr) {}
BackoffEntry::BackoffEntry(const BackoffEntry::Policy* policy,
const base::TickClock* clock)
: policy_(policy), clock_(clock) {
DCHECK(policy_);
Reset();
}
BackoffEntry::~BackoffEntry() {
// TODO(joi): Enable this once our clients (e.g. URLRequestThrottlerManager)
// always destroy from the I/O thread.
// DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
void BackoffEntry::InformOfRequest(bool succeeded) {
if (!succeeded) {
++failure_count_;
exponential_backoff_release_time_ = CalculateReleaseTime();
} else {
// We slowly decay the number of times delayed instead of
// resetting it to 0 in order to stay stable if we receive
// successes interleaved between lots of failures. Note that in
// the normal case, the calculated release time (in the next
// statement) will be in the past once the method returns.
if (failure_count_ > 0)
--failure_count_;
// The reason why we are not just cutting the release time to
// GetTimeTicksNow() is on the one hand, it would unset a release
// time set by SetCustomReleaseTime and on the other we would like
// to push every request up to our "horizon" when dealing with
// multiple in-flight requests. Ex: If we send three requests and
// we receive 2 failures and 1 success. The success that follows
// those failures will not reset the release time, further
// requests will then need to wait the delay caused by the 2
// failures.
base::TimeDelta delay;
if (policy_->always_use_initial_delay)
delay = base::Milliseconds(policy_->initial_delay_ms);
exponential_backoff_release_time_ = std::max(
GetTimeTicksNow() + delay, exponential_backoff_release_time_);
}
}
bool BackoffEntry::ShouldRejectRequest() const {
return exponential_backoff_release_time_ > GetTimeTicksNow();
}
base::TimeDelta BackoffEntry::GetTimeUntilRelease() const {
base::TimeTicks now = GetTimeTicksNow();
if (exponential_backoff_release_time_ <= now)
return base::TimeDelta();
return exponential_backoff_release_time_ - now;
}
base::TimeTicks BackoffEntry::GetReleaseTime() const {
return exponential_backoff_release_time_;
}
void BackoffEntry::SetCustomReleaseTime(const base::TimeTicks& release_time) {
exponential_backoff_release_time_ = release_time;
}
bool BackoffEntry::CanDiscard() const {
if (policy_->entry_lifetime_ms == -1)
return false;
base::TimeTicks now = GetTimeTicksNow();
int64_t unused_since_ms =
(now - exponential_backoff_release_time_).InMilliseconds();
// Release time is further than now, we are managing it.
if (unused_since_ms < 0)
return false;
if (failure_count_ > 0) {
// Need to keep track of failures until maximum back-off period
// has passed (since further failures can add to back-off).
return unused_since_ms >= std::max(policy_->maximum_backoff_ms,
policy_->entry_lifetime_ms);
}
// Otherwise, consider the entry is outdated if it hasn't been used for the
// specified lifetime period.
return unused_since_ms >= policy_->entry_lifetime_ms;
}
void BackoffEntry::Reset() {
failure_count_ = 0;
// For legacy reasons, we reset exponential_backoff_release_time_ to the
// uninitialized state. It would also be reasonable to reset it to
// GetTimeTicksNow(). The effects are the same, i.e. ShouldRejectRequest()
// will return false right after Reset().
exponential_backoff_release_time_ = base::TimeTicks();
}
base::TimeTicks BackoffEntry::GetTimeTicksNow() const {
return clock_ ? clock_->NowTicks() : base::TimeTicks::Now();
}
base::TimeTicks BackoffEntry::CalculateReleaseTime() const {
base::ClampedNumeric<int> effective_failure_count =
base::ClampSub(failure_count_, policy_->num_errors_to_ignore).Max(0);
// If always_use_initial_delay is true, it's equivalent to
// the effective_failure_count always being one greater than when it's false.
if (policy_->always_use_initial_delay)
++effective_failure_count;
if (effective_failure_count == 0) {
// Never reduce previously set release horizon, e.g. due to Retry-After
// header.
return std::max(GetTimeTicksNow(), exponential_backoff_release_time_);
}
// The delay is calculated with this formula:
// delay = initial_backoff * multiply_factor^(
// effective_failure_count - 1) * Uniform(1 - jitter_factor, 1]
// Note: if the failure count is too high, |delay_ms| will become infinity
// after the exponential calculation, and then NaN after the jitter is
// accounted for. Both cases are handled by using CheckedNumeric<int64_t> to
// perform the conversion to integers.
double delay_ms = policy_->initial_delay_ms;
delay_ms *= pow(policy_->multiply_factor, effective_failure_count - 1);
delay_ms -= base::RandDouble() * policy_->jitter_factor * delay_ms;
// Do overflow checking in microseconds, the internal unit of TimeTicks.
base::internal::CheckedNumeric<int64_t> backoff_duration_us = delay_ms + 0.5;
backoff_duration_us *= base::Time::kMicrosecondsPerMillisecond;
base::TimeDelta backoff_duration = base::Microseconds(int64_t{
backoff_duration_us.ValueOrDefault(std::numeric_limits<int64_t>::max())});
base::TimeTicks release_time = BackoffDurationToReleaseTime(backoff_duration);
// Never reduce previously set release horizon, e.g. due to Retry-After
// header.
return std::max(release_time, exponential_backoff_release_time_);
}
base::TimeTicks BackoffEntry::BackoffDurationToReleaseTime(
base::TimeDelta backoff_duration) const {
const int64_t kTimeTicksNowUs =
(GetTimeTicksNow() - base::TimeTicks()).InMicroseconds();
// Do overflow checking in microseconds, the internal unit of TimeTicks.
base::internal::CheckedNumeric<int64_t> calculated_release_time_us =
backoff_duration.InMicroseconds();
calculated_release_time_us += kTimeTicksNowUs;
base::internal::CheckedNumeric<int64_t> maximum_release_time_us =
std::numeric_limits<int64_t>::max();
if (policy_->maximum_backoff_ms >= 0) {
maximum_release_time_us = policy_->maximum_backoff_ms;
maximum_release_time_us *= base::Time::kMicrosecondsPerMillisecond;
maximum_release_time_us += kTimeTicksNowUs;
}
// Decide between maximum release time and calculated release time, accounting
// for overflow with both.
int64_t release_time_us = std::min(calculated_release_time_us.ValueOrDefault(
std::numeric_limits<int64_t>::max()),
maximum_release_time_us.ValueOrDefault(
std::numeric_limits<int64_t>::max()));
return base::TimeTicks() + base::Microseconds(release_time_us);
}
} // namespace net

View File

@@ -0,0 +1,136 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_BACKOFF_ENTRY_H_
#define NET_BASE_BACKOFF_ENTRY_H_
#include <stdint.h>
#include "base/memory/raw_ptr.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "net/base/net_export.h"
namespace base {
class TickClock;
}
namespace net {
// Provides the core logic needed for randomized exponential back-off
// on requests to a given resource, given a back-off policy.
//
// This utility class knows nothing about network specifics; it is
// intended for reuse in various networking scenarios.
class NET_EXPORT BackoffEntry {
public:
// The set of parameters that define a back-off policy. When modifying this,
// increment SERIALIZATION_VERSION_NUMBER in backoff_entry_serializer.cc.
struct Policy {
// Number of initial errors (in sequence) to ignore before applying
// exponential back-off rules.
int num_errors_to_ignore;
// Initial delay. The interpretation of this value depends on
// always_use_initial_delay. It's either how long we wait between
// requests before backoff starts, or how much we delay the first request
// after backoff starts.
int initial_delay_ms;
// Factor by which the waiting time will be multiplied.
double multiply_factor;
// Fuzzing percentage. ex: 10% will spread requests randomly
// between 90%-100% of the calculated time.
double jitter_factor;
// Maximum amount of time we are willing to delay our request, -1
// for no maximum.
int64_t maximum_backoff_ms;
// Time to keep an entry from being discarded even when it
// has no significant state, -1 to never discard.
int64_t entry_lifetime_ms;
// If true, we always use a delay of initial_delay_ms, even before
// we've seen num_errors_to_ignore errors. Otherwise, initial_delay_ms
// is the first delay once we start exponential backoff.
//
// So if we're ignoring 1 error, we'll see (N, N, Nm, Nm^2, ...) if true,
// and (0, 0, N, Nm, ...) when false, where N is initial_backoff_ms and
// m is multiply_factor, assuming we've already seen one success.
bool always_use_initial_delay;
};
// Lifetime of policy must enclose lifetime of BackoffEntry. The
// pointer must be valid but is not dereferenced during construction.
explicit BackoffEntry(const Policy* policy);
// Lifetime of policy and clock must enclose lifetime of BackoffEntry.
// |policy| pointer must be valid but isn't dereferenced during construction.
// |clock| pointer may be null.
BackoffEntry(const Policy* policy, const base::TickClock* clock);
BackoffEntry(const BackoffEntry&) = delete;
BackoffEntry& operator=(const BackoffEntry&) = delete;
virtual ~BackoffEntry();
// Inform this item that a request for the network resource it is
// tracking was made, and whether it failed or succeeded.
void InformOfRequest(bool succeeded);
// Returns true if a request for the resource this item tracks should
// be rejected at the present time due to exponential back-off policy.
bool ShouldRejectRequest() const;
// Returns the absolute time after which this entry (given its present
// state) will no longer reject requests.
base::TimeTicks GetReleaseTime() const;
// Returns the time until a request can be sent (will be zero if the release
// time is in the past).
base::TimeDelta GetTimeUntilRelease() const;
// Converts |backoff_duration| to a release time, by adding it to
// GetTimeTicksNow(), limited by maximum_backoff_ms.
base::TimeTicks BackoffDurationToReleaseTime(
base::TimeDelta backoff_duration) const;
// Causes this object reject requests until the specified absolute time.
// This can be used to e.g. implement support for a Retry-After header.
void SetCustomReleaseTime(const base::TimeTicks& release_time);
// Returns true if this object has no significant state (i.e. you could
// just as well start with a fresh BackoffEntry object), and hasn't
// had for Policy::entry_lifetime_ms.
bool CanDiscard() const;
// Resets this entry to a fresh (as if just constructed) state.
void Reset();
// Returns the failure count for this entry.
int failure_count() const { return failure_count_; }
// Equivalent to TimeTicks::Now(), using clock_ if provided.
base::TimeTicks GetTimeTicksNow() const;
private:
// Calculates when requests should again be allowed through.
base::TimeTicks CalculateReleaseTime() const;
// Timestamp calculated by the exponential back-off algorithm at which we are
// allowed to start sending requests again.
base::TimeTicks exponential_backoff_release_time_;
// Counts request errors; decremented on success.
int failure_count_;
const raw_ptr<const Policy> policy_; // Not owned.
const raw_ptr<const base::TickClock> clock_; // Not owned.
THREAD_CHECKER(thread_checker_);
};
} // namespace net
#endif // NET_BASE_BACKOFF_ENTRY_H_

View File

@@ -0,0 +1,176 @@
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/backoff_entry_serializer.h"
#include <algorithm>
#include <ostream>
#include <utility>
#include "base/notreached.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/tick_clock.h"
#include "base/values.h"
#include "net/base/backoff_entry.h"
namespace {
// This max defines how many times we are willing to call
// |BackoffEntry::InformOfRequest| in |DeserializeFromList|.
//
// This value is meant to large enough that the computed backoff duration can
// still be saturated. Given that the duration is an int64 and assuming 1.01 as
// a conservative lower bound for BackoffEntry::Policy::multiply_factor,
// ceil(log(2**63-1, 1.01)) = 4389.
const int kMaxFailureCount = 4389;
// This function returns true iff |duration| is finite and can be serialized and
// deserialized without becoming infinite. This function is aligned with the
// latest version.
bool BackoffDurationSafeToSerialize(const base::TimeDelta& duration) {
return !duration.is_inf() &&
!base::Microseconds(duration.InMicroseconds()).is_inf();
}
} // namespace
namespace net {
base::Value::List BackoffEntrySerializer::SerializeToList(
const BackoffEntry& entry,
base::Time time_now) {
base::Value::List serialized;
serialized.Append(SerializationFormatVersion::kVersion2);
serialized.Append(entry.failure_count());
// Convert both |base::TimeTicks| values into |base::TimeDelta| values by
// subtracting |kZeroTicks. This way, the top-level subtraction uses
// |base::TimeDelta::operator-|, which has clamping semantics.
const base::TimeTicks kZeroTicks;
const base::TimeDelta kReleaseTime = entry.GetReleaseTime() - kZeroTicks;
const base::TimeDelta kTimeTicksNow = entry.GetTimeTicksNow() - kZeroTicks;
base::TimeDelta backoff_duration;
if (!kReleaseTime.is_inf() && !kTimeTicksNow.is_inf()) {
backoff_duration = kReleaseTime - kTimeTicksNow;
}
if (!BackoffDurationSafeToSerialize(backoff_duration)) {
backoff_duration = base::TimeDelta();
}
base::Time absolute_release_time = backoff_duration + time_now;
// If the computed release time is infinite, default to zero. The deserializer
// should pick up on this.
if (absolute_release_time.is_inf()) {
absolute_release_time = base::Time();
}
// Redundantly stores both the remaining time delta and the absolute time.
// The delta is used to work around some cases where wall clock time changes.
serialized.Append(base::NumberToString(backoff_duration.InMicroseconds()));
serialized.Append(
base::NumberToString(absolute_release_time.ToInternalValue()));
return serialized;
}
std::unique_ptr<BackoffEntry> BackoffEntrySerializer::DeserializeFromList(
const base::Value::List& serialized,
const BackoffEntry::Policy* policy,
const base::TickClock* tick_clock,
base::Time time_now) {
if (serialized.size() != 4)
return nullptr;
if (!serialized[0].is_int())
return nullptr;
int version_number = serialized[0].GetInt();
if (version_number != kVersion1 && version_number != kVersion2)
return nullptr;
if (!serialized[1].is_int())
return nullptr;
int failure_count = serialized[1].GetInt();
if (failure_count < 0) {
return nullptr;
}
failure_count = std::min(failure_count, kMaxFailureCount);
base::TimeDelta original_backoff_duration;
switch (version_number) {
case kVersion1: {
if (!serialized[2].is_double())
return nullptr;
double original_backoff_duration_double = serialized[2].GetDouble();
original_backoff_duration =
base::Seconds(original_backoff_duration_double);
break;
}
case kVersion2: {
if (!serialized[2].is_string())
return nullptr;
std::string original_backoff_duration_string = serialized[2].GetString();
int64_t original_backoff_duration_us;
if (!base::StringToInt64(original_backoff_duration_string,
&original_backoff_duration_us)) {
return nullptr;
}
original_backoff_duration =
base::Microseconds(original_backoff_duration_us);
break;
}
default:
NOTREACHED() << "Unexpected version_number: " << version_number;
}
if (!serialized[3].is_string())
return nullptr;
std::string absolute_release_time_string = serialized[3].GetString();
int64_t absolute_release_time_us;
if (!base::StringToInt64(absolute_release_time_string,
&absolute_release_time_us)) {
return nullptr;
}
auto entry = std::make_unique<BackoffEntry>(policy, tick_clock);
for (int n = 0; n < failure_count; n++)
entry->InformOfRequest(false);
base::Time absolute_release_time =
base::Time::FromInternalValue(absolute_release_time_us);
base::TimeDelta backoff_duration;
if (absolute_release_time == base::Time()) {
// When the serializer cannot compute a finite release time, it uses zero.
// When we see this, fall back to the redundant original_backoff_duration.
backoff_duration = original_backoff_duration;
} else {
// Before computing |backoff_duration|, throw out +/- infinity values for
// either operand. This way, we can use base::TimeDelta's saturated math.
if (absolute_release_time.is_inf() || time_now.is_inf())
return nullptr;
backoff_duration = absolute_release_time.ToDeltaSinceWindowsEpoch() -
time_now.ToDeltaSinceWindowsEpoch();
// In cases where the system wall clock is rewound, use the redundant
// original_backoff_duration to ensure the backoff duration isn't longer
// than it was before serializing (note that it's not possible to protect
// against the clock being wound forward).
if (backoff_duration > original_backoff_duration)
backoff_duration = original_backoff_duration;
}
if (!BackoffDurationSafeToSerialize(backoff_duration))
return nullptr;
const base::TimeTicks release_time =
entry->BackoffDurationToReleaseTime(backoff_duration);
if (release_time.is_inf())
return nullptr;
entry->SetCustomReleaseTime(release_time);
return entry;
}
} // namespace net

View File

@@ -0,0 +1,63 @@
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_BACKOFF_ENTRY_SERIALIZER_H_
#define NET_BASE_BACKOFF_ENTRY_SERIALIZER_H_
#include <memory>
#include "base/time/time.h"
#include "base/values.h"
#include "net/base/backoff_entry.h"
#include "net/base/net_export.h"
namespace base {
class TickClock;
}
namespace net {
enum SerializationFormatVersion {
kVersion1 = 1,
kVersion2,
};
// Serialize or deserialize a BackoffEntry, so it can persist beyond the
// lifetime of the browser.
class NET_EXPORT BackoffEntrySerializer {
public:
BackoffEntrySerializer() = delete;
BackoffEntrySerializer(const BackoffEntrySerializer&) = delete;
BackoffEntrySerializer& operator=(const BackoffEntrySerializer&) = delete;
// Serializes the release time and failure count into a List that can
// later be passed to Deserialize to re-create the given BackoffEntry. It
// always serializes using the latest format version. The Policy is not
// serialized, instead callers must pass an identical Policy* when
// deserializing. `time_now` should be `base::Time::Now()`, except for tests
// that want to simulate time changes. The release time TimeTicks will be
// converted to an absolute timestamp, thus the time will continue counting
// down even whilst the device is powered off, and will be partially
// vulnerable to changes in the system clock time.
static base::Value::List SerializeToList(const BackoffEntry& entry,
base::Time time_now);
// Deserializes a `list` back to a BackoffEntry. It supports all
// serialization format versions. `policy` MUST be the same Policy as the
// serialized entry had. `clock` may be NULL. Both `policy` and `clock` (if
// not NULL) must enclose lifetime of the returned BackoffEntry. `time_now`
// should be `base::Time::Now()`, except for tests that want to simulate time
// changes. The absolute timestamp that was serialized will be converted back
// to TimeTicks as best as possible. Returns NULL if deserialization was
// unsuccessful.
static std::unique_ptr<BackoffEntry> DeserializeFromList(
const base::Value::List& serialized,
const BackoffEntry::Policy* policy,
const base::TickClock* clock,
base::Time time_now);
};
} // namespace net
#endif // NET_BASE_BACKOFF_ENTRY_SERIALIZER_H_

38
src/net/base/cache_type.h Normal file
View File

@@ -0,0 +1,38 @@
// Copyright 2009 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_CACHE_TYPE_H_
#define NET_BASE_CACHE_TYPE_H_
namespace net {
// The types of caches that can be created.
enum CacheType {
DISK_CACHE, // Disk is used as the backing storage.
MEMORY_CACHE, // Data is stored only in memory.
REMOVED_MEDIA_CACHE, // No longer in use.
APP_CACHE, // Special case of DISK_CACHE. Optimizes for
// cases where auto-eviction is not desired:
// e.g. cache_storage, service worker script cache
SHADER_CACHE, // Backing store for the GL shader cache.
PNACL_CACHE, // Backing store the PNaCl translation cache
GENERATED_BYTE_CODE_CACHE, // Backing store for renderer generated data like
// bytecode for JavaScript.
GENERATED_NATIVE_CODE_CACHE, // Backing store for renderer generated data
// like native code for WebAssembly.
GENERATED_WEBUI_BYTE_CODE_CACHE, // Backing store for renderer generated data
// like bytecode for JavaScript from WebUI
// pages.
};
// The types of disk cache backend, only used at backend instantiation.
enum BackendType {
CACHE_BACKEND_DEFAULT,
CACHE_BACKEND_BLOCKFILE, // The |BackendImpl|.
CACHE_BACKEND_SIMPLE // The |SimpleBackendImpl|.
};
} // namespace net
#endif // NET_BASE_CACHE_TYPE_H_

View File

@@ -0,0 +1,118 @@
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/chunked_upload_data_stream.h"
#include "base/check_op.h"
#include "base/memory/ptr_util.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
namespace net {
ChunkedUploadDataStream::Writer::~Writer() = default;
bool ChunkedUploadDataStream::Writer::AppendData(const char* data,
int data_len,
bool is_done) {
if (!upload_data_stream_)
return false;
upload_data_stream_->AppendData(data, data_len, is_done);
return true;
}
ChunkedUploadDataStream::Writer::Writer(
base::WeakPtr<ChunkedUploadDataStream> upload_data_stream)
: upload_data_stream_(upload_data_stream) {}
ChunkedUploadDataStream::ChunkedUploadDataStream(int64_t identifier,
bool has_null_source)
: UploadDataStream(/*is_chunked=*/true, has_null_source, identifier) {}
ChunkedUploadDataStream::~ChunkedUploadDataStream() = default;
std::unique_ptr<ChunkedUploadDataStream::Writer>
ChunkedUploadDataStream::CreateWriter() {
return base::WrapUnique(new Writer(weak_factory_.GetWeakPtr()));
}
void ChunkedUploadDataStream::AppendData(
const char* data, int data_len, bool is_done) {
DCHECK(!all_data_appended_);
DCHECK(data_len > 0 || is_done);
if (data_len > 0) {
DCHECK(data);
upload_data_.push_back(
std::make_unique<std::vector<char>>(data, data + data_len));
}
all_data_appended_ = is_done;
if (!read_buffer_.get())
return;
int result = ReadChunk(read_buffer_.get(), read_buffer_len_);
// Shouldn't get an error or ERR_IO_PENDING.
DCHECK_GE(result, 0);
read_buffer_ = nullptr;
read_buffer_len_ = 0;
OnReadCompleted(result);
}
int ChunkedUploadDataStream::InitInternal(const NetLogWithSource& net_log) {
// ResetInternal should already have been called.
DCHECK(!read_buffer_.get());
DCHECK_EQ(0u, read_index_);
DCHECK_EQ(0u, read_offset_);
return OK;
}
int ChunkedUploadDataStream::ReadInternal(IOBuffer* buf, int buf_len) {
DCHECK_LT(0, buf_len);
DCHECK(!read_buffer_.get());
int result = ReadChunk(buf, buf_len);
if (result == ERR_IO_PENDING) {
read_buffer_ = buf;
read_buffer_len_ = buf_len;
}
return result;
}
void ChunkedUploadDataStream::ResetInternal() {
read_buffer_ = nullptr;
read_buffer_len_ = 0;
read_index_ = 0;
read_offset_ = 0;
}
int ChunkedUploadDataStream::ReadChunk(IOBuffer* buf, int buf_len) {
// Copy as much data as possible from |upload_data_| to |buf|.
int bytes_read = 0;
while (read_index_ < upload_data_.size() && bytes_read < buf_len) {
std::vector<char>* data = upload_data_[read_index_].get();
size_t bytes_to_read =
std::min(static_cast<size_t>(buf_len - bytes_read),
data->size() - read_offset_);
memcpy(buf->data() + bytes_read, data->data() + read_offset_,
bytes_to_read);
bytes_read += bytes_to_read;
read_offset_ += bytes_to_read;
if (read_offset_ == data->size()) {
read_index_++;
read_offset_ = 0;
}
}
DCHECK_LE(bytes_read, buf_len);
// If no data was written, and not all data has been appended, return
// ERR_IO_PENDING. The read will be completed in the next call to AppendData.
if (bytes_read == 0 && !all_data_appended_)
return ERR_IO_PENDING;
if (read_index_ == upload_data_.size() && all_data_appended_)
SetIsFinalChunk();
return bytes_read;
}
} // namespace net

View File

@@ -0,0 +1,108 @@
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_CHUNKED_UPLOAD_DATA_STREAM_H_
#define NET_BASE_CHUNKED_UPLOAD_DATA_STREAM_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <vector>
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "net/base/net_export.h"
#include "net/base/upload_data_stream.h"
namespace net {
class IOBuffer;
// Class with a push-based interface for uploading data. Buffers all data until
// the request is completed. Not recommended for uploading large amounts of
// seekable data, due to this buffering behavior.
class NET_EXPORT ChunkedUploadDataStream : public UploadDataStream {
public:
// Utility class that allows writing data to a particular
// ChunkedUploadDataStream. It can outlive the associated
// ChunkedUploadDataStream, and the URLRequest it is associated with, and
// still be safely used. This allows the consumer to not have to worry about
// the lifetime of the ChunkedUploadDataStream, which the owning URLRequest
// may delete without warning.
//
// The writer may only be used on the ChunkedUploadDataStream's thread.
class NET_EXPORT Writer {
public:
Writer(const Writer&) = delete;
Writer& operator=(const Writer&) = delete;
~Writer();
// Adds data to the stream. |is_done| should be true if this is the last
// data to be appended. |data_len| must not be 0 unless |is_done| is true.
// Once called with |is_done| being true, must never be called again.
// Returns true if write was passed successfully on to the next layer,
// though the data may not actually have been written to the underlying
// URLRequest. Returns false if unable to write the data failed because the
// underlying ChunkedUploadDataStream was destroyed.
bool AppendData(const char* data, int data_len, bool is_done);
private:
friend class ChunkedUploadDataStream;
explicit Writer(base::WeakPtr<ChunkedUploadDataStream> upload_data_stream);
const base::WeakPtr<ChunkedUploadDataStream> upload_data_stream_;
};
explicit ChunkedUploadDataStream(int64_t identifier,
bool has_null_source = false);
ChunkedUploadDataStream(const ChunkedUploadDataStream&) = delete;
ChunkedUploadDataStream& operator=(const ChunkedUploadDataStream&) = delete;
~ChunkedUploadDataStream() override;
// Creates a Writer for appending data to |this|. It's generally expected
// that only one writer is created per stream, though multiple writers are
// allowed. All writers write to the same stream, and once one of them
// appends data with |is_done| being true, no other writers may be used to
// append data.
std::unique_ptr<Writer> CreateWriter();
// Adds data to the stream. |is_done| should be true if this is the last
// data to be appended. |data_len| must not be 0 unless |is_done| is true.
// Once called with |is_done| being true, must never be called again.
// TODO(mmenke): Consider using IOBuffers instead, to reduce data copies.
// TODO(mmenke): Consider making private, and having all consumers use
// Writers.
void AppendData(const char* data, int data_len, bool is_done);
private:
// UploadDataStream implementation.
int InitInternal(const NetLogWithSource& net_log) override;
int ReadInternal(IOBuffer* buf, int buf_len) override;
void ResetInternal() override;
int ReadChunk(IOBuffer* buf, int buf_len);
// Index and offset of next element of |upload_data_| to be read.
size_t read_index_ = 0;
size_t read_offset_ = 0;
// True once all data has been appended to the stream.
bool all_data_appended_ = false;
std::vector<std::unique_ptr<std::vector<char>>> upload_data_;
// Buffer to write the next read's data to. Only set when a call to
// ReadInternal reads no data.
scoped_refptr<IOBuffer> read_buffer_;
int read_buffer_len_ = 0;
base::WeakPtrFactory<ChunkedUploadDataStream> weak_factory_{this};
};
} // namespace net
#endif // NET_BASE_CHUNKED_UPLOAD_DATA_STREAM_H_

View File

@@ -0,0 +1,29 @@
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_COMPLETION_ONCE_CALLBACK_H_
#define NET_BASE_COMPLETION_ONCE_CALLBACK_H_
#include <stdint.h>
#include "base/cancelable_callback.h"
#include "base/functional/callback.h"
namespace net {
// A OnceCallback specialization that takes a single int parameter. Usually this
// is used to report a byte count or network error code.
using CompletionOnceCallback = base::OnceCallback<void(int)>;
// 64bit version of the OnceCallback specialization that takes a single int64_t
// parameter. Usually this is used to report a file offset, size or network
// error code.
using Int64CompletionOnceCallback = base::OnceCallback<void(int64_t)>;
using CancelableCompletionOnceCallback =
base::CancelableOnceCallback<void(int)>;
} // namespace net
#endif // NET_BASE_COMPLETION_ONCE_CALLBACK_H_

View File

@@ -0,0 +1,29 @@
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_COMPLETION_REPEATING_CALLBACK_H_
#define NET_BASE_COMPLETION_REPEATING_CALLBACK_H_
#include <stdint.h>
#include "base/cancelable_callback.h"
#include "base/functional/callback.h"
namespace net {
// A RepeatingCallback specialization that takes a single int parameter. Usually
// this is used to report a byte count or network error code.
using CompletionRepeatingCallback = base::RepeatingCallback<void(int)>;
// 64bit version of the RepeatingCallback specialization that takes a single
// int64_t parameter. Usually this is used to report a file offset, size or
// network error code.
using Int64CompletionRepeatingCallback = base::RepeatingCallback<void(int64_t)>;
using CancelableCompletionRepeatingCallback =
base::CancelableRepeatingCallback<void(int)>;
} // namespace net
#endif // NET_BASE_COMPLETION_REPEATING_CALLBACK_H_

View File

@@ -0,0 +1,95 @@
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/connection_endpoint_metadata.h"
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "base/base64.h"
#include "base/values.h"
namespace net {
namespace {
const char kSupportedProtocolAlpnsKey[] = "supported_protocol_alpns";
const char kEchConfigListKey[] = "ech_config_list";
const char kTargetNameKey[] = "target_name";
} // namespace
ConnectionEndpointMetadata::ConnectionEndpointMetadata() = default;
ConnectionEndpointMetadata::ConnectionEndpointMetadata(
std::vector<std::string> supported_protocol_alpns,
EchConfigList ech_config_list,
std::string target_name)
: supported_protocol_alpns(std::move(supported_protocol_alpns)),
ech_config_list(std::move(ech_config_list)),
target_name(std::move(target_name)) {}
ConnectionEndpointMetadata::~ConnectionEndpointMetadata() = default;
ConnectionEndpointMetadata::ConnectionEndpointMetadata(
const ConnectionEndpointMetadata&) = default;
ConnectionEndpointMetadata::ConnectionEndpointMetadata(
ConnectionEndpointMetadata&&) = default;
base::Value ConnectionEndpointMetadata::ToValue() const {
base::Value::Dict dict;
base::Value::List alpns_list;
for (const std::string& alpn : supported_protocol_alpns) {
alpns_list.Append(alpn);
}
dict.Set(kSupportedProtocolAlpnsKey, std::move(alpns_list));
dict.Set(kEchConfigListKey, base::Base64Encode(ech_config_list));
if (!target_name.empty()) {
dict.Set(kTargetNameKey, target_name);
}
return base::Value(std::move(dict));
}
// static
std::optional<ConnectionEndpointMetadata> ConnectionEndpointMetadata::FromValue(
const base::Value& value) {
const base::Value::Dict* dict = value.GetIfDict();
if (!dict)
return std::nullopt;
const base::Value::List* alpns_list =
dict->FindList(kSupportedProtocolAlpnsKey);
const std::string* ech_config_list_value =
dict->FindString(kEchConfigListKey);
const std::string* target_name_value = dict->FindString(kTargetNameKey);
if (!alpns_list || !ech_config_list_value)
return std::nullopt;
ConnectionEndpointMetadata metadata;
std::vector<std::string> alpns;
for (const base::Value& alpn : *alpns_list) {
if (!alpn.is_string())
return std::nullopt;
metadata.supported_protocol_alpns.push_back(alpn.GetString());
}
std::optional<std::vector<uint8_t>> decoded =
base::Base64Decode(*ech_config_list_value);
if (!decoded)
return std::nullopt;
metadata.ech_config_list = std::move(*decoded);
if (target_name_value) {
metadata.target_name = *target_name_value;
}
return metadata;
}
} // namespace net

View File

@@ -0,0 +1,62 @@
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_CONNECTION_ENDPOINT_METADATA_H_
#define NET_BASE_CONNECTION_ENDPOINT_METADATA_H_
#include <stdint.h>
#include <optional>
#include <string>
#include <tuple>
#include <vector>
#include "base/values.h"
#include "net/base/net_export.h"
namespace net {
// Metadata used to create UDP/TCP/TLS/etc connections or information about such
// a connection.
struct NET_EXPORT_PRIVATE ConnectionEndpointMetadata {
// Expected to be parsed/consumed only by BoringSSL code and thus passed
// around here only as a raw byte array.
using EchConfigList = std::vector<uint8_t>;
ConnectionEndpointMetadata();
ConnectionEndpointMetadata(std::vector<std::string> supported_protocol_alpns,
EchConfigList ech_config_list,
std::string target_name);
~ConnectionEndpointMetadata();
ConnectionEndpointMetadata(const ConnectionEndpointMetadata&);
ConnectionEndpointMetadata& operator=(const ConnectionEndpointMetadata&) =
default;
ConnectionEndpointMetadata(ConnectionEndpointMetadata&&);
ConnectionEndpointMetadata& operator=(ConnectionEndpointMetadata&&) = default;
bool operator==(const ConnectionEndpointMetadata& other) const {
return std::tie(supported_protocol_alpns, ech_config_list, target_name) ==
std::tie(other.supported_protocol_alpns, other.ech_config_list,
target_name);
}
base::Value ToValue() const;
static std::optional<ConnectionEndpointMetadata> FromValue(
const base::Value& value);
// ALPN strings for protocols supported by the endpoint. Empty for default
// non-protocol endpoint.
std::vector<std::string> supported_protocol_alpns;
// If not empty, TLS Encrypted Client Hello config for the service.
EchConfigList ech_config_list;
// The target domain name of this metadata.
std::string target_name;
};
} // namespace net
#endif // NET_BASE_CONNECTION_ENDPOINT_METADATA_H_

View File

@@ -0,0 +1,114 @@
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/connection_endpoint_metadata_test_util.h"
#include <ostream>
#include <string>
#include <utility>
#include <vector>
#include "net/base/connection_endpoint_metadata.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
using EchConfigList = ConnectionEndpointMetadata::EchConfigList;
namespace {
class EndpointMetadataMatcher
: public testing::MatcherInterface<const ConnectionEndpointMetadata&> {
public:
EndpointMetadataMatcher(
testing::Matcher<std::vector<std::string>>
supported_protocol_alpns_matcher,
testing::Matcher<EchConfigList> ech_config_list_matcher,
testing::Matcher<std::string> target_name_matcher)
: supported_protocol_alpns_matcher_(
std::move(supported_protocol_alpns_matcher)),
ech_config_list_matcher_(std::move(ech_config_list_matcher)),
target_name_matcher_(std::move(target_name_matcher)) {}
~EndpointMetadataMatcher() override = default;
EndpointMetadataMatcher(const EndpointMetadataMatcher&) = default;
EndpointMetadataMatcher& operator=(const EndpointMetadataMatcher&) = default;
EndpointMetadataMatcher(EndpointMetadataMatcher&&) = default;
EndpointMetadataMatcher& operator=(EndpointMetadataMatcher&&) = default;
bool MatchAndExplain(
const ConnectionEndpointMetadata& metadata,
testing::MatchResultListener* result_listener) const override {
return ExplainMatchResult(
testing::Field(
"supported_protocol_alpns",
&ConnectionEndpointMetadata::supported_protocol_alpns,
supported_protocol_alpns_matcher_),
metadata, result_listener) &&
ExplainMatchResult(
testing::Field("ech_config_list",
&ConnectionEndpointMetadata::ech_config_list,
ech_config_list_matcher_),
metadata, result_listener) &&
ExplainMatchResult(
testing::Field("target_name",
&ConnectionEndpointMetadata::target_name,
target_name_matcher_),
metadata, result_listener);
}
void DescribeTo(std::ostream* os) const override {
*os << "matches ";
Describe(*os);
}
void DescribeNegationTo(std::ostream* os) const override {
*os << "does not match ";
Describe(*os);
}
private:
void Describe(std::ostream& os) const {
os << "ConnectionEndpoint {\nsupported_protocol_alpns: "
<< testing::PrintToString(supported_protocol_alpns_matcher_)
<< "\nech_config_list: "
<< testing::PrintToString(ech_config_list_matcher_)
<< "\ntarget_name: " << testing::PrintToString(target_name_matcher_)
<< "\n}";
}
testing::Matcher<std::vector<std::string>> supported_protocol_alpns_matcher_;
testing::Matcher<EchConfigList> ech_config_list_matcher_;
testing::Matcher<std::string> target_name_matcher_;
};
} // namespace
testing::Matcher<const ConnectionEndpointMetadata&>
ExpectConnectionEndpointMetadata(
testing::Matcher<std::vector<std::string>> supported_protocol_alpns_matcher,
testing::Matcher<EchConfigList> ech_config_list_matcher,
testing::Matcher<std::string> target_name_matcher) {
return testing::MakeMatcher(new EndpointMetadataMatcher(
std::move(supported_protocol_alpns_matcher),
std::move(ech_config_list_matcher), std::move(target_name_matcher)));
}
std::ostream& operator<<(
std::ostream& os,
const ConnectionEndpointMetadata& connection_endpoint_metadata) {
return os << "ConnectionEndpointMetadata {\nsupported_protocol_alpns: "
<< testing::PrintToString(
connection_endpoint_metadata.supported_protocol_alpns)
<< "\nech_config_list: "
<< testing::PrintToString(
connection_endpoint_metadata.ech_config_list)
<< "\ntarget_name: "
<< testing::PrintToString(connection_endpoint_metadata.target_name)
<< "\n}";
}
} // namespace net

View File

@@ -0,0 +1,32 @@
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_CONNECTION_ENDPOINT_METADATA_TEST_UTIL_H_
#define NET_BASE_CONNECTION_ENDPOINT_METADATA_TEST_UTIL_H_
#include <ostream>
#include <string>
#include <vector>
#include "net/base/connection_endpoint_metadata.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
testing::Matcher<const ConnectionEndpointMetadata&>
ExpectConnectionEndpointMetadata(
testing::Matcher<std::vector<std::string>>
supported_protocol_alpns_matcher = testing::IsEmpty(),
testing::Matcher<ConnectionEndpointMetadata::EchConfigList>
ech_config_list_matcher = testing::IsEmpty(),
testing::Matcher<std::string> target_name_matcher = testing::IsEmpty());
std::ostream& operator<<(
std::ostream& os,
const ConnectionEndpointMetadata& connection_endpoint_metadata);
} // namespace net
#endif // NET_BASE_CONNECTION_ENDPOINT_METADATA_TEST_UTIL_H_

209
src/net/base/data_url.cc Normal file
View File

@@ -0,0 +1,209 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// NOTE: based loosely on mozilla's nsDataChannel.cpp
#include "net/base/data_url.h"
#include <string>
#include <string_view>
#include "base/base64.h"
#include "base/feature_list.h"
#include "base/features.h"
#include "base/ranges/algorithm.h"
#include "base/strings/escape.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "net/base/mime_util.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_util.h"
#include "url/gurl.h"
namespace net {
namespace {
// https://infra.spec.whatwg.org/#ascii-whitespace, which is referenced by
// https://infra.spec.whatwg.org/#forgiving-base64, does not include \v in the
// set of ASCII whitespace characters the way Unicode does.
bool IsBase64Whitespace(char c) {
return c != '\v' && base::IsAsciiWhitespace(c);
}
// A data URL is ready for decode if it:
// - Doesn't need any extra padding.
// - Does not have any escaped characters.
// - Does not have any whitespace.
bool IsDataURLReadyForDecode(std::string_view body) {
return (body.length() % 4) == 0 && base::ranges::none_of(body, [](char c) {
return c == '%' || IsBase64Whitespace(c);
});
}
} // namespace
bool DataURL::Parse(const GURL& url,
std::string* mime_type,
std::string* charset,
std::string* data) {
if (!url.is_valid() || !url.has_scheme())
return false;
DCHECK(mime_type->empty());
DCHECK(charset->empty());
DCHECK(!data || data->empty());
std::string_view content;
std::string content_string;
if (base::FeatureList::IsEnabled(base::features::kOptimizeDataUrls)) {
// Avoid copying the URL content which can be expensive for large URLs.
content = url.GetContentPiece();
} else {
content_string = url.GetContent();
content = content_string;
}
std::string_view::const_iterator comma = base::ranges::find(content, ',');
if (comma == content.end())
return false;
std::vector<std::string_view> meta_data =
base::SplitStringPiece(base::MakeStringPiece(content.begin(), comma), ";",
base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
// These are moved to |mime_type| and |charset| on success.
std::string mime_type_value;
std::string charset_value;
auto iter = meta_data.cbegin();
if (iter != meta_data.cend()) {
mime_type_value = base::ToLowerASCII(*iter);
++iter;
}
static constexpr std::string_view kBase64Tag("base64");
static constexpr std::string_view kCharsetTag("charset=");
bool base64_encoded = false;
for (; iter != meta_data.cend(); ++iter) {
if (!base64_encoded &&
base::EqualsCaseInsensitiveASCII(*iter, kBase64Tag)) {
base64_encoded = true;
} else if (charset_value.empty() &&
base::StartsWith(*iter, kCharsetTag,
base::CompareCase::INSENSITIVE_ASCII)) {
charset_value = std::string(iter->substr(kCharsetTag.size()));
// The grammar for charset is not specially defined in RFC2045 and
// RFC2397. It just needs to be a token.
if (!HttpUtil::IsToken(charset_value))
return false;
}
}
if (mime_type_value.empty()) {
// Fallback to the default if nothing specified in the mediatype part as
// specified in RFC2045. As specified in RFC2397, we use |charset| even if
// |mime_type| is empty.
mime_type_value = "text/plain";
if (charset_value.empty())
charset_value = "US-ASCII";
} else if (!ParseMimeTypeWithoutParameter(mime_type_value, nullptr,
nullptr)) {
// Fallback to the default as recommended in RFC2045 when the mediatype
// value is invalid. For this case, we don't respect |charset| but force it
// set to "US-ASCII".
mime_type_value = "text/plain";
charset_value = "US-ASCII";
}
// The caller may not be interested in receiving the data.
if (data) {
// Preserve spaces if dealing with text or xml input, same as mozilla:
// https://bugzilla.mozilla.org/show_bug.cgi?id=138052
// but strip them otherwise:
// https://bugzilla.mozilla.org/show_bug.cgi?id=37200
// (Spaces in a data URL should be escaped, which is handled below, so any
// spaces now are wrong. People expect to be able to enter them in the URL
// bar for text, and it can't hurt, so we allow it.)
//
// TODO(mmenke): Is removing all spaces reasonable? GURL removes trailing
// spaces itself, anyways. Should we just trim leading spaces instead?
// Allowing random intermediary spaces seems unnecessary.
auto raw_body = base::MakeStringPiece(comma + 1, content.end());
// For base64, we may have url-escaped whitespace which is not part
// of the data, and should be stripped. Otherwise, the escaped whitespace
// could be part of the payload, so don't strip it.
if (base64_encoded) {
// If the data URL is well formed, we can decode it immediately.
if (base::FeatureList::IsEnabled(base::features::kOptimizeDataUrls) &&
IsDataURLReadyForDecode(raw_body)) {
if (!base::Base64Decode(raw_body, data))
return false;
} else {
std::string unescaped_body = base::UnescapeBinaryURLComponent(raw_body);
if (!base::Base64Decode(unescaped_body, data,
base::Base64DecodePolicy::kForgiving))
return false;
}
} else {
// Strip whitespace for non-text MIME types.
std::string temp;
if (!(mime_type_value.compare(0, 5, "text/") == 0 ||
mime_type_value.find("xml") != std::string::npos)) {
temp = std::string(raw_body);
std::erase_if(temp, base::IsAsciiWhitespace<char>);
raw_body = temp;
}
*data = base::UnescapeBinaryURLComponent(raw_body);
}
}
*mime_type = std::move(mime_type_value);
*charset = std::move(charset_value);
return true;
}
Error DataURL::BuildResponse(const GURL& url,
std::string_view method,
std::string* mime_type,
std::string* charset,
std::string* data,
scoped_refptr<HttpResponseHeaders>* headers) {
DCHECK(data);
DCHECK(!*headers);
if (!DataURL::Parse(url, mime_type, charset, data))
return ERR_INVALID_URL;
// |mime_type| set by DataURL::Parse() is guaranteed to be in
// token "/" token
// form. |charset| can be an empty string.
DCHECK(!mime_type->empty());
// "charset" in the Content-Type header is specified explicitly to follow
// the "token" ABNF in the HTTP spec. When the DataURL::Parse() call is
// successful, it's guaranteed that the string in |charset| follows the
// "token" ABNF.
std::string content_type = *mime_type;
if (!charset->empty())
content_type.append(";charset=" + *charset);
// The terminal double CRLF isn't needed by TryToCreate().
*headers = HttpResponseHeaders::TryToCreate(
"HTTP/1.1 200 OK\r\n"
"Content-Type:" +
content_type);
// Above line should always succeed - TryToCreate() only fails when there are
// nulls in the string, and DataURL::Parse() can't return nulls in anything
// but the |data| argument.
DCHECK(*headers);
if (base::EqualsCaseInsensitiveASCII(method, "HEAD"))
data->clear();
return OK;
}
} // namespace net

86
src/net/base/data_url.h Normal file
View File

@@ -0,0 +1,86 @@
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_DATA_URL_H_
#define NET_BASE_DATA_URL_H_
#include <string>
#include <string_view>
#include "base/memory/scoped_refptr.h"
#include "net/base/net_errors.h"
#include "net/base/net_export.h"
class GURL;
namespace net {
class HttpResponseHeaders;
// See RFC 2397 for a complete description of the 'data' URL scheme.
//
// Briefly, a 'data' URL has the form:
//
// data:[<mediatype>][;base64],<data>
//
// The <mediatype> is an Internet media type specification (with optional
// parameters.) The appearance of ";base64" means that the data is encoded as
// base64. Without ";base64", the data (as a sequence of octets) is represented
// using ASCII encoding for octets inside the range of safe URL characters and
// using the standard %xx hex encoding of URLs for octets outside that range.
// If <mediatype> is omitted, it defaults to text/plain;charset=US-ASCII. As a
// shorthand, "text/plain" can be omitted but the charset parameter supplied.
//
class NET_EXPORT DataURL {
public:
// This method can be used to parse a 'data' URL into its component pieces.
//
// |mime_type| and |charset| must be non-null and point to empty strings.
//
// If |data| is null, then the <data> section will not be parsed or validated.
// If non-null, it must point to an empty string.
//
// The resulting mime_type is normalized to lowercase. The data is the
// decoded data (e.g.., if the data URL specifies base64 encoding, then the
// returned data is base64 decoded, and any %-escaped bytes are unescaped).
//
// If the media type value doesn't match the media-type production defined in
// RFC 7231, mime_type will be set to the default value "text/plain". We
// don't simply fail for this grammar violation since Chromium had been
// accepting such invalid values. For example, <img> element with the src
// attribute set to a data URL with an invalid media type "image" (without a
// slash and subtype) had been displayed. However, the value this method will
// store in mime_type argument can be used for generating other headers, etc.
// This could lead to security vulnerability. We don't want to accept
// arbitrary value and ask each caller to validate the return value.
//
// If the charset parameter is specified but its value doesn't match the
// token production defined in RFC 7230, this method simply fails and returns
// false.
//
// If there's any other grammar violation in the URL, then this method will
// return false, and all passed in pointers will be unmodified. On success,
// true is returned.
[[nodiscard]] static bool Parse(const GURL& url,
std::string* mime_type,
std::string* charset,
std::string* data);
// Similar to parse, except that it also generates a bogus set of response
// headers, with Content-Type populated, and takes a method. Only the "HEAD"
// method modifies the response, resulting in a 0-length body. All arguments
// except must be non-null. All std::string pointers must point to empty
// strings, and |*headers| must be nullptr. Returns net::OK on success.
[[nodiscard]] static Error BuildResponse(
const GURL& url,
std::string_view method,
std::string* mime_type,
std::string* charset,
std::string* data,
scoped_refptr<HttpResponseHeaders>* headers);
};
} // namespace net
#endif // NET_BASE_DATA_URL_H_

View File

@@ -0,0 +1,58 @@
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/datagram_buffer.h"
#include "base/memory/ptr_util.h"
#include <cstring>
namespace net {
DatagramBufferPool::DatagramBufferPool(size_t max_buffer_size)
: max_buffer_size_(max_buffer_size) {}
DatagramBufferPool::~DatagramBufferPool() = default;
void DatagramBufferPool::Enqueue(const char* buffer,
size_t buf_len,
DatagramBuffers* buffers) {
DCHECK_LE(buf_len, max_buffer_size_);
std::unique_ptr<DatagramBuffer> datagram_buffer;
if (free_list_.empty()) {
datagram_buffer = base::WrapUnique(new DatagramBuffer(max_buffer_size_));
} else {
datagram_buffer = std::move(free_list_.front());
free_list_.pop_front();
}
datagram_buffer->Set(buffer, buf_len);
buffers->emplace_back(std::move(datagram_buffer));
}
void DatagramBufferPool::Dequeue(DatagramBuffers* buffers) {
if (buffers->size() == 0)
return;
free_list_.splice(free_list_.cend(), *buffers);
}
DatagramBuffer::DatagramBuffer(size_t max_buffer_size)
: data_(std::make_unique<char[]>(max_buffer_size)) {}
DatagramBuffer::~DatagramBuffer() = default;
void DatagramBuffer::Set(const char* buffer, size_t buf_len) {
length_ = buf_len;
std::memcpy(data_.get(), buffer, buf_len);
}
char* DatagramBuffer::data() const {
return data_.get();
}
size_t DatagramBuffer::length() const {
return length_;
}
} // namespace net

View File

@@ -0,0 +1,102 @@
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_DATAGRAM_BUFFER_H_
#define NET_BASE_DATAGRAM_BUFFER_H_
#include <list>
#include "base/memory/weak_ptr.h"
#include "net/base/net_export.h"
namespace net {
// An IO buffer, (at least initially) specifically for use with the
// new DatagramClientSocket::WriteAsync method, with the following key
// features:
//
// 1) Meant to be easily batched when that improves efficiency. The
// primary goal of WriteAsync is to enable enlisting an
// additional cpu core for the kernel part of socket write.
// 2) Uses unique_ptr (with std::move) rather than reference
// counting as in IOBuffers. The benefit is safer cancellation
// semantics, IOBuffer used reference count to enforce unique
// ownership in an idiomatic fashion. unique_ptr is ligher weight
// as it doesn't use thread safe primitives as
// RefCountedThreadSafe does.
// 3) Provides a pooling allocator, which for datagram buffers is
// much cheaper than using fully general allocator (e.g. malloc
// etc.). The implementation takes advantage of
// std::list::splice so that costs associated with allocations
// and copies of pool metadata quickly amortize to zero, and all
// common operations are O(1).
class DatagramBuffer;
// Batches of DatagramBuffers are treated as a FIFO queue, implemented
// by |std::list|. Note that |std::list::splice()| is attractive for
// this use case because it keeps most operations to O(1) and
// minimizes allocations/frees and copies.
typedef std::list<std::unique_ptr<DatagramBuffer>> DatagramBuffers;
class NET_EXPORT_PRIVATE DatagramBufferPool {
public:
// |max_buffer_size| must be >= largest |buf_len| provided to
// ||New()|.
explicit DatagramBufferPool(size_t max_buffer_size);
DatagramBufferPool(const DatagramBufferPool&) = delete;
DatagramBufferPool& operator=(const DatagramBufferPool&) = delete;
virtual ~DatagramBufferPool();
// Insert a new element (drawn from the pool) containing a copy of
// |buffer| to |buffers|. Caller retains owenership of |buffers| and |buffer|.
void Enqueue(const char* buffer, size_t buf_len, DatagramBuffers* buffers);
// Return all elements of |buffers| to the pool. Caller retains
// ownership of |buffers|.
void Dequeue(DatagramBuffers* buffers);
size_t max_buffer_size() { return max_buffer_size_; }
private:
const size_t max_buffer_size_;
DatagramBuffers free_list_;
};
// |DatagramBuffer|s can only be created via
// |DatagramBufferPool::Enqueue()|.
//
// |DatagramBuffer|s should be recycled via
// |DatagramBufferPool::Dequeue|. Care must be taken when a
// |DatagramBuffer| is moved to another thread via
// |PostTask|. |Dequeue| is not expected to be thread-safe, so it
// is preferred to move the |DatagramBuffer|s back to the thread where
// the pool lives (e.g. using |PostTaskAndReturnWithResult|) and
// dequeuing them from there. In the exception of pathalogical
// cancellation (e.g. due to thread tear-down), the destructor will
// release its memory permanently rather than returning to the pool.
class NET_EXPORT_PRIVATE DatagramBuffer {
public:
DatagramBuffer() = delete;
DatagramBuffer(const DatagramBuffer&) = delete;
DatagramBuffer& operator=(const DatagramBuffer&) = delete;
~DatagramBuffer();
char* data() const;
size_t length() const;
protected:
explicit DatagramBuffer(size_t max_packet_size);
private:
friend class DatagramBufferPool;
void Set(const char* buffer, size_t buf_len);
std::unique_ptr<char[]> data_;
size_t length_ = 0;
};
} // namespace net
#endif // NET_BASE_DATAGRAM_BUFFER_H_

View File

@@ -0,0 +1,228 @@
<!DOCTYPE html>
<html dir="$i18n{textdirection}" lang="$i18n{language}">
<head>
<meta charset="utf-8">
<meta name="color-scheme" content="light dark">
<meta name="google" value="notranslate">
<script>
function addRow(name, url, isdir,
size, size_string, date_modified, date_modified_string) {
if (name == "." || name == "..")
return;
var root = document.location.pathname;
if (root.substr(-1) !== "/")
root += "/";
var tbody = document.getElementById("tbody");
var row = document.createElement("tr");
var file_cell = document.createElement("td");
var link = document.createElement("a");
link.className = isdir ? "icon dir" : "icon file";
if (isdir) {
name = name + "/";
url = url + "/";
size = 0;
size_string = "";
} else {
link.draggable = "true";
link.addEventListener("dragstart", onDragStart, false);
}
link.innerText = name;
link.href = root + url;
file_cell.dataset.value = name;
file_cell.appendChild(link);
row.appendChild(file_cell);
row.appendChild(createCell(size, size_string));
row.appendChild(createCell(date_modified, date_modified_string));
tbody.appendChild(row);
}
function onDragStart(e) {
var el = e.srcElement;
var name = el.innerText.replace(":", "");
var download_url_data = "application/octet-stream:" + name + ":" + el.href;
e.dataTransfer.setData("DownloadURL", download_url_data);
e.dataTransfer.effectAllowed = "copy";
}
function createCell(value, text) {
var cell = document.createElement("td");
cell.setAttribute("class", "detailsColumn");
cell.dataset.value = value;
cell.innerText = text;
return cell;
}
function start(location) {
var header = document.getElementById("header");
header.innerText = header.innerText.replace("LOCATION", location);
document.getElementById("title").innerText = header.innerText;
}
function onHasParentDirectory() {
var box = document.getElementById("parentDirLinkBox");
box.style.display = "block";
var root = document.location.pathname;
if (!root.endsWith("/"))
root += "/";
var link = document.getElementById("parentDirLink");
link.href = root + "..";
}
function sortTable(column) {
var theader = document.getElementById("theader");
var oldOrder = theader.cells[column].dataset.order || '1';
oldOrder = parseInt(oldOrder, 10)
var newOrder = 0 - oldOrder;
theader.cells[column].dataset.order = newOrder;
var tbody = document.getElementById("tbody");
var rows = tbody.rows;
var list = [], i;
for (i = 0; i < rows.length; i++) {
list.push(rows[i]);
}
list.sort(function(row1, row2) {
var a = row1.cells[column].dataset.value;
var b = row2.cells[column].dataset.value;
if (column) {
a = parseInt(a, 10);
b = parseInt(b, 10);
return a > b ? newOrder : a < b ? oldOrder : 0;
}
// Column 0 is text.
if (a > b)
return newOrder;
if (a < b)
return oldOrder;
return 0;
});
// Appending an existing child again just moves it.
for (i = 0; i < list.length; i++) {
tbody.appendChild(list[i]);
}
}
// Add event handlers to column headers.
function addHandlers(element, column) {
element.onclick = (e) => sortTable(column);
element.onkeydown = (e) => {
if (e.key == 'Enter' || e.key == ' ') {
sortTable(column);
e.preventDefault();
}
};
}
function onLoad() {
addHandlers(document.getElementById('nameColumnHeader'), 0);
addHandlers(document.getElementById('sizeColumnHeader'), 1);
addHandlers(document.getElementById('dateColumnHeader'), 2);
}
window.addEventListener('DOMContentLoaded', onLoad);
</script>
<style>
h1 {
border-bottom: 1px solid #c0c0c0;
margin-bottom: 10px;
padding-bottom: 10px;
white-space: nowrap;
}
table {
border-collapse: collapse;
}
th {
cursor: pointer;
}
td.detailsColumn {
padding-inline-start: 2em;
text-align: end;
white-space: nowrap;
}
a.icon {
padding-inline-start: 1.5em;
text-decoration: none;
user-select: auto;
}
a.icon:hover {
text-decoration: underline;
}
a.file {
background : url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMAAAAAAABupgeRAAABEElEQVR42nRRx3HDMBC846AHZ7sP54BmWAyrsP588qnwlhqw/k4v5ZwWxM1hzmGRgV1cYqrRarXoH2w2m6qqiqKIR6cPtzc3xMSML2Te7XZZlnW7Pe/91/dX47WRBHuA9oyGmRknzGDjab1ePzw8bLfb6WRalmW4ip9FDVpYSWZgOp12Oh3nXJ7nxoJSGEciteP9y+fH52q1euv38WosqA6T2gGOT44vry7BEQtJkMAMMpa6JagAMcUfWYa4hkkzAc7fFlSjwqCoOUYAF5RjHZPVCFBOtSBGfgUDji3c3jpibeEMQhIMh8NwshqyRsBJgvF4jMs/YlVR5KhgNpuBLzk0OcUiR3CMhcPaOzsZiAAA/AjmaB3WZIkAAAAASUVORK5CYII=") left top no-repeat;
}
a.dir {
background : url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABt0lEQVR42oxStZoWQRCs2cXdHTLcHZ6EjAwnQWIkJyQlRt4Cd3d3d1n5d7q7ju1zv/q+mh6taQsk8fn29kPDRo87SDMQcNAUJgIQkBjdAoRKdXjm2mOH0AqS+PlkP8sfp0h93iu/PDji9s2FzSSJVg5ykZqWgfGRr9rAAAQiDFoB1OfyESZEB7iAI0lHwLREQBcQQKqo8p+gNUCguwCNAAUQAcFOb0NNGjT+BbUC2YsHZpWLhC6/m0chqIoM1LKbQIIBwlTQE1xAo9QDGDPYf6rkTpPc92gCUYVJAZjhyZltJ95f3zuvLYRGWWCUNkDL2333McBh4kaLlxg+aTmyL7c2xTjkN4Bt7oE3DBP/3SRz65R/bkmBRPGzcRNHYuzMjaj+fdnaFoJUEdTSXfaHbe7XNnMPyqryPcmfY+zURaAB7SHk9cXSH4fQ5rojgCAVIuqCNWgRhLYLhJB4k3iZfIPtnQiCpjAzeBIRXMA6emAqoEbQSoDdGxFUrxS1AYcpaNbBgyQBGJEOnYOeENKR/iAd1npusI4C75/c3539+nbUjOgZV5CkAU27df40lH+agUdIuA/EAgDmZnwZlhDc0wAAAABJRU5ErkJggg==") left top no-repeat;
}
a.up {
background : url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACM0lEQVR42myTA+w1RxRHz+zftmrbdlTbtq04qRGrCmvbDWp9tq3a7tPcub8mj9XZ3eHOGQdJAHw77/LbZuvnWy+c/CIAd+91CMf3bo+bgcBiBAGIZKXb19/zodsAkFT+3px+ssYfyHTQW5tr05dCOf3xN49KaVX9+2zy1dX4XMk+5JflN5MBPL30oVsvnvEyp+18Nt3ZAErQMSFOfelCFvw0HcUloDayljZkX+MmamTAMTe+d+ltZ+1wEaRAX/MAnkJdcujzZyErIiVSzCEvIiq4O83AG7LAkwsfIgAnbncag82jfPPdd9RQyhPkpNJvKJWQBKlYFmQA315n4YPNjwMAZYy0TgAweedLmLzTJSTLIxkWDaVCVfAbbiKjytgmm+EGpMBYW0WwwbZ7lL8anox/UxekaOW544HO0ANAshxuORT/RG5YSrjlwZ3lM955tlQqbtVMlWIhjwzkAVFB8Q9EAAA3AFJ+DR3DO/Pnd3NPi7H117rAzWjpEs8vfIqsGZpaweOfEAAFJKuM0v6kf2iC5pZ9+fmLSZfWBVaKfLLNOXj6lYY0V2lfyVCIsVzmcRV9Y0fx02eTaEwhl2PDrXcjFdYRAohQmS8QEFLCLKGYA0AeEakhCCFDXqxsE0AQACgAQp5w96o0lAXuNASeDKWIvADiHwigfBINpWKtAXJvCEKWgSJNbRvxf4SmrnKDpvZavePu1K/zu/due1X/6Nj90MBd/J2Cic7WjBp/jUdIuA8AUtd65M+PzXIAAAAASUVORK5CYII=") left top no-repeat;
}
html[dir=rtl] a {
background-position-x: right;
}
#parentDirLinkBox {
margin-bottom: 10px;
padding-bottom: 10px;
}
</style>
<title id="title"></title>
</head>
<body>
<h1 id="header">$i18n{header}</h1>
<div id="parentDirLinkBox" style="display:none">
<a id="parentDirLink" class="icon up">
<span id="parentDirText">$i18n{parentDirText}</span>
</a>
</div>
<table>
<thead>
<tr class="header" id="theader">
<th id="nameColumnHeader" tabindex=0 role="button">$i18n{headerName}</th>
<th id="sizeColumnHeader" class="detailsColumn" tabindex=0 role="button">
$i18n{headerSize}
</th>
<th id="dateColumnHeader" class="detailsColumn" tabindex=0 role="button">
$i18n{headerDateModified}
</th>
</tr>
</thead>
<tbody id="tbody">
</tbody>
</table>
</body>
</html>

View File

@@ -0,0 +1,206 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/directory_lister.h"
#include <algorithm>
#include <utility>
#include "base/check.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/i18n/file_util_icu.h"
#include "base/location.h"
#include "base/notreached.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_runner.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_restrictions.h"
#include "net/base/net_errors.h"
namespace net {
namespace {
bool IsDotDot(const base::FilePath& path) {
return FILE_PATH_LITERAL("..") == path.BaseName().value();
}
// Comparator for sorting lister results. This uses the locale aware filename
// comparison function on the filenames for sorting in the user's locale.
// Static.
bool CompareAlphaDirsFirst(const DirectoryLister::DirectoryListerData& a,
const DirectoryLister::DirectoryListerData& b) {
// Parent directory before all else.
if (IsDotDot(b.info.GetName())) {
return false;
}
if (IsDotDot(a.info.GetName())) {
return true;
}
// Directories before regular files.
bool a_is_directory = a.info.IsDirectory();
bool b_is_directory = b.info.IsDirectory();
if (a_is_directory != b_is_directory)
return a_is_directory;
return base::i18n::LocaleAwareCompareFilenames(a.info.GetName(),
b.info.GetName());
}
void SortData(std::vector<DirectoryLister::DirectoryListerData>* data,
DirectoryLister::ListingType listing_type) {
// Sort the results. See the TODO below (this sort should be removed and we
// should do it from JS).
if (listing_type == DirectoryLister::ALPHA_DIRS_FIRST) {
std::sort(data->begin(), data->end(), CompareAlphaDirsFirst);
} else if (listing_type != DirectoryLister::NO_SORT &&
listing_type != DirectoryLister::NO_SORT_RECURSIVE) {
NOTREACHED();
}
}
} // namespace
DirectoryLister::DirectoryLister(const base::FilePath& dir,
DirectoryListerDelegate* delegate)
: DirectoryLister(dir, ALPHA_DIRS_FIRST, delegate) {}
DirectoryLister::DirectoryLister(const base::FilePath& dir,
ListingType type,
DirectoryListerDelegate* delegate)
: delegate_(delegate) {
core_ = base::MakeRefCounted<Core>(dir, type, this);
DCHECK(delegate_);
DCHECK(!dir.value().empty());
}
DirectoryLister::~DirectoryLister() {
Cancel();
}
void DirectoryLister::Start() {
base::ThreadPool::PostTask(
FROM_HERE,
{base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(&Core::Start, core_));
}
void DirectoryLister::Cancel() {
core_->CancelOnOriginSequence();
}
DirectoryLister::Core::Core(const base::FilePath& dir,
ListingType type,
DirectoryLister* lister)
: dir_(dir),
type_(type),
origin_task_runner_(base::SequencedTaskRunner::GetCurrentDefault().get()),
lister_(lister) {
DCHECK(lister_);
}
DirectoryLister::Core::~Core() = default;
void DirectoryLister::Core::CancelOnOriginSequence() {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
base::subtle::NoBarrier_Store(&cancelled_, 1);
// Core must not call into |lister_| after cancellation, as the |lister_| may
// have been destroyed. Setting |lister_| to NULL ensures any such access will
// cause a crash.
lister_ = nullptr;
}
void DirectoryLister::Core::Start() {
auto directory_list = std::make_unique<DirectoryList>();
if (!base::DirectoryExists(dir_)) {
origin_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&Core::DoneOnOriginSequence, this,
std::move(directory_list), ERR_FILE_NOT_FOUND));
return;
}
int types = base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES;
bool recursive;
if (NO_SORT_RECURSIVE != type_) {
types |= base::FileEnumerator::INCLUDE_DOT_DOT;
recursive = false;
} else {
recursive = true;
}
base::FileEnumerator file_enum(dir_, recursive, types);
base::FilePath path;
while (!(path = file_enum.Next()).empty()) {
// Abort on cancellation. This is purely for performance reasons.
// Correctness guarantees are made by checks in DoneOnOriginSequence.
if (IsCancelled())
return;
DirectoryListerData data;
data.info = file_enum.GetInfo();
data.path = path;
data.absolute_path = base::MakeAbsoluteFilePath(path);
directory_list->push_back(data);
/* TODO(brettw) bug 24107: It would be nice to send incremental updates.
We gather them all so they can be sorted, but eventually the sorting
should be done from JS to give more flexibility in the page. When we do
that, we can uncomment this to send incremental updates to the page.
const int kFilesPerEvent = 8;
if (file_data.size() < kFilesPerEvent)
continue;
origin_loop_->PostTask(
FROM_HERE,
base::BindOnce(&DirectoryLister::Core::SendData, file_data));
file_data.clear();
*/
}
SortData(directory_list.get(), type_);
origin_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&Core::DoneOnOriginSequence, this,
std::move(directory_list), OK));
}
bool DirectoryLister::Core::IsCancelled() const {
return !!base::subtle::NoBarrier_Load(&cancelled_);
}
void DirectoryLister::Core::DoneOnOriginSequence(
std::unique_ptr<DirectoryList> directory_list,
int error) const {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
// Need to check if the operation was before first callback.
if (IsCancelled())
return;
for (const auto& lister_data : *directory_list) {
lister_->OnListFile(lister_data);
// Need to check if the operation was cancelled during the callback.
if (IsCancelled())
return;
}
lister_->OnListDone(error);
}
void DirectoryLister::OnListFile(const DirectoryListerData& data) {
delegate_->OnListFile(data);
}
void DirectoryLister::OnListDone(int error) {
delegate_->OnListDone(error);
}
} // namespace net

View File

@@ -0,0 +1,141 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_DIRECTORY_LISTER_H_
#define NET_BASE_DIRECTORY_LISTER_H_
#include <memory>
#include <vector>
#include "base/atomicops.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "net/base/net_export.h"
namespace base {
class SequencedTaskRunner;
}
namespace net {
// This class provides an API for asynchronously listing the contents of a
// directory on the filesystem. It runs a task on a background thread, and
// enumerates all files in the specified directory on that thread. Destroying
// the lister cancels the list operation. The DirectoryLister must only be
// used on a thread with a MessageLoop.
class NET_EXPORT DirectoryLister {
public:
// Represents one file found.
struct DirectoryListerData {
base::FileEnumerator::FileInfo info;
base::FilePath path;
base::FilePath absolute_path;
};
// Implement this class to receive directory entries.
class DirectoryListerDelegate {
public:
// Called for each file found by the lister.
virtual void OnListFile(const DirectoryListerData& data) = 0;
// Called when the listing is complete.
virtual void OnListDone(int error) = 0;
protected:
virtual ~DirectoryListerDelegate() = default;
};
// Listing options
// ALPHA_DIRS_FIRST is the default listing type:
// directories first in name order, then files by name order
// Listing is recursive only if listing type is NO_SORT_RECURSIVE.
// TODO(mmenke): Improve testing.
enum ListingType {
NO_SORT,
NO_SORT_RECURSIVE,
ALPHA_DIRS_FIRST,
};
DirectoryLister(const base::FilePath& dir,
DirectoryListerDelegate* delegate);
DirectoryLister(const base::FilePath& dir,
ListingType type,
DirectoryListerDelegate* delegate);
DirectoryLister(const DirectoryLister&) = delete;
DirectoryLister& operator=(const DirectoryLister&) = delete;
// Will invoke Cancel().
~DirectoryLister();
// Call this method to start the asynchronous directory enumeration.
void Start();
// Call this method to asynchronously stop directory enumeration. The
// delegate will not be called back.
void Cancel();
private:
typedef std::vector<DirectoryListerData> DirectoryList;
// Class responsible for retrieving and sorting the actual directory list on
// a worker pool thread. Created on the DirectoryLister's thread. As it's
// refcounted, it's destroyed when the final reference is released, which may
// happen on either thread.
//
// It's kept alive during the calls to Start() and DoneOnOriginSequence() by
// the reference owned by the callback itself.
class Core : public base::RefCountedThreadSafe<Core> {
public:
Core(const base::FilePath& dir, ListingType type, DirectoryLister* lister);
Core(const Core&) = delete;
Core& operator=(const Core&) = delete;
// May only be called on a worker pool thread.
void Start();
// Must be called on the origin thread.
void CancelOnOriginSequence();
private:
friend class base::RefCountedThreadSafe<Core>;
class DataEvent;
~Core();
// Called on both threads.
bool IsCancelled() const;
// Called on origin thread.
void DoneOnOriginSequence(std::unique_ptr<DirectoryList> directory_list,
int error) const;
const base::FilePath dir_;
const ListingType type_;
const scoped_refptr<base::SequencedTaskRunner> origin_task_runner_;
// Only used on the origin thread.
raw_ptr<DirectoryLister> lister_;
// Set to 1 on cancellation. Used both to abort listing files early on the
// worker pool thread for performance reasons and to ensure |lister_| isn't
// called after cancellation on the origin thread.
base::subtle::Atomic32 cancelled_ = 0;
};
// Call into the corresponding DirectoryListerDelegate. Must not be called
// after cancellation.
void OnListFile(const DirectoryListerData& data);
void OnListDone(int error);
scoped_refptr<Core> core_;
const raw_ptr<DirectoryListerDelegate> delegate_;
};
} // namespace net
#endif // NET_BASE_DIRECTORY_LISTER_H_

View File

@@ -0,0 +1,97 @@
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/directory_listing.h"
#include "base/i18n/time_formatting.h"
#include "base/json/string_escape.h"
#include "base/logging.h"
#include "base/memory/ref_counted_memory.h"
#include "base/strings/escape.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "net/base/net_module.h"
#include "net/grit/net_resources.h"
namespace net {
std::string GetDirectoryListingHeader(const std::u16string& title) {
scoped_refptr<base::RefCountedMemory> header(
NetModule::GetResource(IDR_DIR_HEADER_HTML));
// This can be null in unit tests.
DLOG_IF(WARNING, !header) << "Missing resource: directory listing header";
std::string result;
if (header)
result.assign(header->front_as<char>(), header->size());
result.append("<script>start(");
base::EscapeJSONString(title, true, &result);
result.append(");</script>\n");
return result;
}
std::string GetDirectoryListingEntry(const std::u16string& name,
const std::string& raw_bytes,
bool is_dir,
int64_t size,
base::Time modified) {
std::string result;
result.append("<script>addRow(");
base::EscapeJSONString(name, true, &result);
result.append(",");
if (raw_bytes.empty()) {
base::EscapeJSONString(base::EscapePath(base::UTF16ToUTF8(name)), true,
&result);
} else {
base::EscapeJSONString(base::EscapePath(raw_bytes), true, &result);
}
if (is_dir) {
result.append(",1,");
} else {
result.append(",0,");
}
// Negative size means unknown or not applicable (e.g. directory).
std::stringstream raw_size_string_stream;
raw_size_string_stream << size << ",";
result.append(raw_size_string_stream.str());
std::u16string size_string;
if (size >= 0)
size_string = base::FormatBytesUnlocalized(size);
base::EscapeJSONString(size_string, true, &result);
result.append(",");
// |modified| can be NULL in FTP listings.
std::u16string modified_str;
if (modified.is_null()) {
result.append("0,");
} else {
std::stringstream raw_time_string_stream;
// Certain access paths can only get up to seconds resolution, so here we
// output the raw time value in seconds for consistency.
raw_time_string_stream << modified.InMillisecondsSinceUnixEpoch() /
base::Time::kMillisecondsPerSecond
<< ",";
result.append(raw_time_string_stream.str());
modified_str = base::TimeFormatShortDateAndTime(modified);
}
base::EscapeJSONString(modified_str, true, &result);
result.append(");</script>\n");
return result;
}
std::string GetParentDirectoryLink() {
return std::string("<script>onHasParentDirectory();</script>\n");
}
} // namespace net

View File

@@ -0,0 +1,45 @@
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_DIRECTORY_LISTING_H_
#define NET_BASE_DIRECTORY_LISTING_H_
#include <stdint.h>
#include <string>
#include "net/base/net_export.h"
namespace base {
class Time;
}
namespace net {
// Call these functions to get the html snippet for a directory listing.
// The return values of these functions are in UTF-8.
NET_EXPORT std::string GetDirectoryListingHeader(const std::u16string& title);
// Given the name of a file in a directory (ftp or local) and
// other information (is_dir, size, modification time), it returns
// the html snippet to add the entry for the file to the directory listing.
// Currently, it's a script tag containing a call to a Javascript function
// |addRow|.
//
// |name| is the file name to be displayed. |raw_bytes| will be used
// as the actual target of the link (so for example, ftp links should use
// server's encoding). If |raw_bytes| is an empty string, UTF-8 encoded |name|
// will be used.
//
// Both |name| and |raw_bytes| are escaped internally.
NET_EXPORT std::string GetDirectoryListingEntry(const std::u16string& name,
const std::string& raw_bytes,
bool is_dir,
int64_t size,
base::Time modified);
NET_EXPORT std::string GetParentDirectoryLink();
} // namespace net
#endif // NET_BASE_DIRECTORY_LISTING_H_

View File

@@ -0,0 +1,151 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/elements_upload_data_stream.h"
#include <utility>
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/upload_bytes_element_reader.h"
#include "net/base/upload_element_reader.h"
namespace net {
ElementsUploadDataStream::ElementsUploadDataStream(
std::vector<std::unique_ptr<UploadElementReader>> element_readers,
int64_t identifier)
: UploadDataStream(false, identifier),
element_readers_(std::move(element_readers)) {}
ElementsUploadDataStream::~ElementsUploadDataStream() = default;
std::unique_ptr<UploadDataStream> ElementsUploadDataStream::CreateWithReader(
std::unique_ptr<UploadElementReader> reader,
int64_t identifier) {
std::vector<std::unique_ptr<UploadElementReader>> readers;
readers.push_back(std::move(reader));
return std::make_unique<ElementsUploadDataStream>(std::move(readers),
identifier);
}
int ElementsUploadDataStream::InitInternal(const NetLogWithSource& net_log) {
return InitElements(0);
}
int ElementsUploadDataStream::ReadInternal(
IOBuffer* buf,
int buf_len) {
DCHECK_GT(buf_len, 0);
return ReadElements(base::MakeRefCounted<DrainableIOBuffer>(buf, buf_len));
}
bool ElementsUploadDataStream::IsInMemory() const {
for (const std::unique_ptr<UploadElementReader>& it : element_readers_) {
if (!it->IsInMemory())
return false;
}
return true;
}
const std::vector<std::unique_ptr<UploadElementReader>>*
ElementsUploadDataStream::GetElementReaders() const {
return &element_readers_;
}
void ElementsUploadDataStream::ResetInternal() {
weak_ptr_factory_.InvalidateWeakPtrs();
read_error_ = OK;
element_index_ = 0;
}
int ElementsUploadDataStream::InitElements(size_t start_index) {
// Call Init() for all elements.
for (size_t i = start_index; i < element_readers_.size(); ++i) {
UploadElementReader* reader = element_readers_[i].get();
// When new_result is ERR_IO_PENDING, InitInternal() will be called
// with start_index == i + 1 when reader->Init() finishes.
int result = reader->Init(
base::BindOnce(&ElementsUploadDataStream::OnInitElementCompleted,
weak_ptr_factory_.GetWeakPtr(), i));
DCHECK(result != ERR_IO_PENDING || !reader->IsInMemory());
DCHECK_LE(result, OK);
if (result != OK)
return result;
}
uint64_t total_size = 0;
for (const std::unique_ptr<UploadElementReader>& it : element_readers_) {
total_size += it->GetContentLength();
}
SetSize(total_size);
return OK;
}
void ElementsUploadDataStream::OnInitElementCompleted(size_t index,
int result) {
DCHECK_NE(ERR_IO_PENDING, result);
// Check the last result.
if (result == OK)
result = InitElements(index + 1);
if (result != ERR_IO_PENDING)
OnInitCompleted(result);
}
int ElementsUploadDataStream::ReadElements(
const scoped_refptr<DrainableIOBuffer>& buf) {
while (read_error_ == OK && element_index_ < element_readers_.size()) {
UploadElementReader* reader = element_readers_[element_index_].get();
if (reader->BytesRemaining() == 0) {
++element_index_;
continue;
}
if (buf->BytesRemaining() == 0)
break;
int result = reader->Read(
buf.get(), buf->BytesRemaining(),
base::BindOnce(&ElementsUploadDataStream::OnReadElementCompleted,
weak_ptr_factory_.GetWeakPtr(), buf));
if (result == ERR_IO_PENDING)
return ERR_IO_PENDING;
ProcessReadResult(buf, result);
}
if (buf->BytesConsumed() > 0)
return buf->BytesConsumed();
return read_error_;
}
void ElementsUploadDataStream::OnReadElementCompleted(
const scoped_refptr<DrainableIOBuffer>& buf,
int result) {
ProcessReadResult(buf, result);
result = ReadElements(buf);
if (result != ERR_IO_PENDING)
OnReadCompleted(result);
}
void ElementsUploadDataStream::ProcessReadResult(
const scoped_refptr<DrainableIOBuffer>& buf,
int result) {
DCHECK_NE(ERR_IO_PENDING, result);
DCHECK(!read_error_);
if (result >= 0) {
buf->DidConsume(result);
} else {
read_error_ = result;
}
}
} // namespace net

View File

@@ -0,0 +1,91 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_ELEMENTS_UPLOAD_DATA_STREAM_H_
#define NET_BASE_ELEMENTS_UPLOAD_DATA_STREAM_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <vector>
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "net/base/net_errors.h"
#include "net/base/net_export.h"
#include "net/base/upload_data_stream.h"
namespace net {
class DrainableIOBuffer;
class IOBuffer;
class UploadElementReader;
// A non-chunked UploadDataStream consisting of one or more UploadElements.
class NET_EXPORT ElementsUploadDataStream : public UploadDataStream {
public:
ElementsUploadDataStream(
std::vector<std::unique_ptr<UploadElementReader>> element_readers,
int64_t identifier);
ElementsUploadDataStream(const ElementsUploadDataStream&) = delete;
ElementsUploadDataStream& operator=(const ElementsUploadDataStream&) = delete;
~ElementsUploadDataStream() override;
// Creates an ElementsUploadDataStream with a single reader. Returns a
// std::unique_ptr<UploadDataStream> for ease of use.
static std::unique_ptr<UploadDataStream> CreateWithReader(
std::unique_ptr<UploadElementReader> reader,
int64_t identifier);
private:
// UploadDataStream implementation.
bool IsInMemory() const override;
const std::vector<std::unique_ptr<UploadElementReader>>* GetElementReaders()
const override;
int InitInternal(const NetLogWithSource& net_log) override;
int ReadInternal(IOBuffer* buf, int buf_len) override;
void ResetInternal() override;
// Runs Init() for all element readers.
// This method is used to implement InitInternal().
int InitElements(size_t start_index);
// Called when the |index| element finishes initialization. If it succeeded,
// continues with the |index + 1| element. Calls OnInitCompleted on error or
// when all elements have been initialized.
void OnInitElementCompleted(size_t index, int result);
// Reads data from the element readers.
// This method is used to implement Read().
int ReadElements(const scoped_refptr<DrainableIOBuffer>& buf);
// Resumes pending read and calls OnReadCompleted with a result when
// necessary.
void OnReadElementCompleted(const scoped_refptr<DrainableIOBuffer>& buf,
int result);
// Processes result of UploadElementReader::Read(). If |result| indicates
// success, updates |buf|'s offset. Otherwise, sets |read_failed_| to true.
void ProcessReadResult(const scoped_refptr<DrainableIOBuffer>& buf,
int result);
std::vector<std::unique_ptr<UploadElementReader>> element_readers_;
// Index of the current upload element (i.e. the element currently being
// read). The index is used as a cursor to iterate over elements in
// |upload_data_|.
size_t element_index_ = 0;
// Set to actual error if read fails, otherwise set to net::OK.
int read_error_ = OK;
base::WeakPtrFactory<ElementsUploadDataStream> weak_ptr_factory_{this};
};
} // namespace net
#endif // NET_BASE_ELEMENTS_UPLOAD_DATA_STREAM_H_

View File

@@ -0,0 +1,220 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_EXPIRING_CACHE_H_
#define NET_BASE_EXPIRING_CACHE_H_
#include <stddef.h>
#include <map>
#include <utility>
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ref.h"
#include "base/time/time.h"
namespace net {
template <typename KeyType,
typename ValueType,
typename ExpirationType>
class NoopEvictionHandler {
public:
void Handle(const KeyType& key,
const ValueType& value,
const ExpirationType& expiration,
const ExpirationType& now,
bool onGet) const {
}
};
// Cache implementation where all entries have an explicit expiration policy. As
// new items are added, expired items will be removed first.
// The template types have the following requirements:
// KeyType must be LessThanComparable, Assignable, and CopyConstructible.
// ValueType must be CopyConstructible and Assignable.
// ExpirationType must be CopyConstructible and Assignable.
// ExpirationCompare is a function class that takes two arguments of the
// type ExpirationType and returns a bool. If |comp| is an instance of
// ExpirationCompare, then the expression |comp(current, expiration)| shall
// return true iff |current| is still valid within |expiration|.
//
// A simple use of this class may use base::TimeTicks, which provides a
// monotonically increasing clock, for the expiration type. Because it's always
// increasing, std::less<> can be used, which will simply ensure that |now| is
// sorted before |expiration|:
//
// ExpiringCache<std::string, std::string, base::TimeTicks,
// std::less<base::TimeTicks> > cache(0);
// // Add a value that expires in 5 minutes
// cache.Put("key1", "value1", base::TimeTicks::Now(),
// base::TimeTicks::Now() + base::Minutes(5));
// // Add another value that expires in 10 minutes.
// cache.Put("key2", "value2", base::TimeTicks::Now(),
// base::TimeTicks::Now() + base::Minutes(10));
//
// Alternatively, there may be some more complex expiration criteria, at which
// point a custom functor may be used:
//
// struct ComplexExpirationFunctor {
// bool operator()(const ComplexExpiration& now,
// const ComplexExpiration& expiration) const;
// };
// ExpiringCache<std::string, std::string, ComplexExpiration,
// ComplexExpirationFunctor> cache(15);
// // Add a value that expires once the 'sprocket' has 'cog'-ified.
// cache.Put("key1", "value1", ComplexExpiration("sprocket"),
// ComplexExpiration("cog"));
template <typename KeyType,
typename ValueType,
typename ExpirationType,
typename ExpirationCompare,
typename EvictionHandler = NoopEvictionHandler<KeyType,
ValueType,
ExpirationType> >
class ExpiringCache {
public:
ExpiringCache(const ExpiringCache&) = delete;
ExpiringCache& operator=(const ExpiringCache&) = delete;
private:
// Intentionally violate the C++ Style Guide so that EntryMap is known to be
// a dependent type. Without this, Clang's two-phase lookup complains when
// using EntryMap::const_iterator, while GCC and MSVC happily resolve the
// typename.
// Tuple to represent the value and when it expires.
typedef std::pair<ValueType, ExpirationType> Entry;
typedef std::map<KeyType, Entry> EntryMap;
public:
typedef KeyType key_type;
typedef ValueType value_type;
typedef ExpirationType expiration_type;
// This class provides a read-only iterator over items in the ExpiringCache
class Iterator {
public:
explicit Iterator(const ExpiringCache& cache)
: cache_(cache), it_(cache_->entries_.begin()) {}
~Iterator() = default;
bool HasNext() const { return it_ != cache_->entries_.end(); }
void Advance() { ++it_; }
const KeyType& key() const { return it_->first; }
const ValueType& value() const { return it_->second.first; }
const ExpirationType& expiration() const { return it_->second.second; }
private:
const raw_ref<const ExpiringCache> cache_;
// Use a second layer of type indirection, as both EntryMap and
// EntryMap::const_iterator are dependent types.
typedef typename ExpiringCache::EntryMap EntryMap;
typename EntryMap::const_iterator it_;
};
// Constructs an ExpiringCache that stores up to |max_entries|.
explicit ExpiringCache(size_t max_entries) : max_entries_(max_entries) {}
~ExpiringCache() = default;
// Returns the value matching |key|, which must be valid at the time |now|.
// Returns NULL if the item is not found or has expired. If the item has
// expired, it is immediately removed from the cache.
// Note: The returned pointer remains owned by the ExpiringCache and is
// invalidated by a call to a non-const method.
const ValueType* Get(const KeyType& key, const ExpirationType& now) {
typename EntryMap::iterator it = entries_.find(key);
if (it == entries_.end())
return nullptr;
// Immediately remove expired entries.
if (!expiration_comp_(now, it->second.second)) {
Evict(it, now, true);
return nullptr;
}
return &it->second.first;
}
// Updates or replaces the value associated with |key|.
void Put(const KeyType& key,
const ValueType& value,
const ExpirationType& now,
const ExpirationType& expiration) {
typename EntryMap::iterator it = entries_.find(key);
if (it == entries_.end()) {
// Compact the cache if it grew beyond the limit.
if (entries_.size() == max_entries_ )
Compact(now);
// No existing entry. Creating a new one.
entries_.insert(std::pair(key, Entry(value, expiration)));
} else {
// Update an existing cache entry.
it->second.first = value;
it->second.second = expiration;
}
}
// Empties the cache.
void Clear() {
entries_.clear();
}
// Returns the number of entries in the cache.
size_t size() const { return entries_.size(); }
// Returns the maximum number of entries in the cache.
size_t max_entries() const { return max_entries_; }
bool empty() const { return entries_.empty(); }
private:
FRIEND_TEST_ALL_PREFIXES(ExpiringCacheTest, Compact);
FRIEND_TEST_ALL_PREFIXES(ExpiringCacheTest, CustomFunctor);
// Prunes entries from the cache to bring it below |max_entries()|.
void Compact(const ExpirationType& now) {
// Clear out expired entries.
typename EntryMap::iterator it;
for (it = entries_.begin(); it != entries_.end(); ) {
if (!expiration_comp_(now, it->second.second)) {
Evict(it++, now, false);
} else {
++it;
}
}
if (entries_.size() < max_entries_)
return;
// If the cache is still too full, start deleting items 'randomly'.
for (it = entries_.begin();
it != entries_.end() && entries_.size() >= max_entries_;) {
Evict(it++, now, false);
}
}
void Evict(typename EntryMap::iterator it,
const ExpirationType& now,
bool on_get) {
eviction_handler_.Handle(it->first, it->second.first, it->second.second,
now, on_get);
entries_.erase(it);
}
// Bound on total size of the cache.
size_t max_entries_;
EntryMap entries_;
ExpirationCompare expiration_comp_;
EvictionHandler eviction_handler_;
};
} // namespace net
#endif // NET_BASE_EXPIRING_CACHE_H_

537
src/net/base/features.cc Normal file
View File

@@ -0,0 +1,537 @@
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/features.h"
#include <vector>
#include "base/feature_list.h"
#include "build/build_config.h"
#include "net/base/cronet_buildflags.h"
#include "net/net_buildflags.h"
namespace net::features {
BASE_FEATURE(kAlpsForHttp2, "AlpsForHttp2", base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kAvoidH2Reprioritization,
"AvoidH2Reprioritization",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kCapReferrerToOriginOnCrossOrigin,
"CapReferrerToOriginOnCrossOrigin",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kAsyncDns,
"AsyncDns",
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID) || \
BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
base::FEATURE_ENABLED_BY_DEFAULT
#else
base::FEATURE_DISABLED_BY_DEFAULT
#endif
);
BASE_FEATURE(kDnsTransactionDynamicTimeouts,
"DnsTransactionDynamicTimeouts",
base::FEATURE_DISABLED_BY_DEFAULT);
const base::FeatureParam<double> kDnsTransactionTimeoutMultiplier{
&kDnsTransactionDynamicTimeouts, "DnsTransactionTimeoutMultiplier", 7.5};
const base::FeatureParam<base::TimeDelta> kDnsMinTransactionTimeout{
&kDnsTransactionDynamicTimeouts, "DnsMinTransactionTimeout",
base::Seconds(12)};
BASE_FEATURE(kUseDnsHttpsSvcb,
"UseDnsHttpsSvcb",
base::FEATURE_ENABLED_BY_DEFAULT);
const base::FeatureParam<bool> kUseDnsHttpsSvcbEnforceSecureResponse{
&kUseDnsHttpsSvcb, "UseDnsHttpsSvcbEnforceSecureResponse", false};
const base::FeatureParam<base::TimeDelta> kUseDnsHttpsSvcbInsecureExtraTimeMax{
&kUseDnsHttpsSvcb, "UseDnsHttpsSvcbInsecureExtraTimeMax",
base::Milliseconds(50)};
const base::FeatureParam<int> kUseDnsHttpsSvcbInsecureExtraTimePercent{
&kUseDnsHttpsSvcb, "UseDnsHttpsSvcbInsecureExtraTimePercent", 20};
const base::FeatureParam<base::TimeDelta> kUseDnsHttpsSvcbInsecureExtraTimeMin{
&kUseDnsHttpsSvcb, "UseDnsHttpsSvcbInsecureExtraTimeMin",
base::Milliseconds(5)};
const base::FeatureParam<base::TimeDelta> kUseDnsHttpsSvcbSecureExtraTimeMax{
&kUseDnsHttpsSvcb, "UseDnsHttpsSvcbSecureExtraTimeMax",
base::Milliseconds(50)};
const base::FeatureParam<int> kUseDnsHttpsSvcbSecureExtraTimePercent{
&kUseDnsHttpsSvcb, "UseDnsHttpsSvcbSecureExtraTimePercent", 20};
const base::FeatureParam<base::TimeDelta> kUseDnsHttpsSvcbSecureExtraTimeMin{
&kUseDnsHttpsSvcb, "UseDnsHttpsSvcbSecureExtraTimeMin",
base::Milliseconds(5)};
BASE_FEATURE(kUseDnsHttpsSvcbAlpn,
"UseDnsHttpsSvcbAlpn",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kUseHostResolverCache,
"UseHostResolverCache",
base::FEATURE_DISABLED_BY_DEFAULT);
const base::FeatureParam<int> kAlternativePortForGloballyReachableCheck{
&kUseAlternativePortForGloballyReachableCheck,
"AlternativePortForGloballyReachableCheck", 443};
BASE_FEATURE(kUseAlternativePortForGloballyReachableCheck,
"UseAlternativePortForGloballyReachableCheck",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kEnableIPv6ReachabilityOverride,
"EnableIPv6ReachabilityOverride",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kEnableTLS13EarlyData,
"EnableTLS13EarlyData",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kRSAKeyUsageForLocalAnchors,
"RSAKeyUsageForLocalAnchors",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kNetworkQualityEstimator,
"NetworkQualityEstimator",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kSplitCacheByIncludeCredentials,
"SplitCacheByIncludeCredentials",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kSplitCacheByNetworkIsolationKey,
"SplitCacheByNetworkIsolationKey",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kSplitCodeCacheByNetworkIsolationKey,
"SplitCodeCacheByNetworkIsolationKey",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kSplitHostCacheByNetworkIsolationKey,
"SplitHostCacheByNetworkIsolationKey",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kPartitionConnectionsByNetworkIsolationKey,
"PartitionConnectionsByNetworkIsolationKey",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kPartitionHttpServerPropertiesByNetworkIsolationKey,
"PartitionHttpServerPropertiesByNetworkIsolationKey",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kPartitionSSLSessionsByNetworkIsolationKey,
"PartitionSSLSessionsByNetworkIsolationKey",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kPartitionNelAndReportingByNetworkIsolationKey,
"PartitionNelAndReportingByNetworkIsolationKey",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kEnableCrossSiteFlagNetworkIsolationKey,
"EnableCrossSiteFlagNetworkIsolationKey",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kEnableFrameSiteSharedOpaqueNetworkIsolationKey,
"EnableFrameSiteSharedOpaqueNetworkIsolationKey",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kHttpCacheKeyingExperimentControlGroup,
"HttpCacheKeyingExperimentControlGroup",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kTLS13KeyUpdate,
"TLS13KeyUpdate",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kPermuteTLSExtensions,
"PermuteTLSExtensions",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kPostQuantumKyber,
"PostQuantumKyber",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kNetUnusedIdleSocketTimeout,
"NetUnusedIdleSocketTimeout",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kShortLaxAllowUnsafeThreshold,
"ShortLaxAllowUnsafeThreshold",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kSameSiteDefaultChecksMethodRigorously,
"SameSiteDefaultChecksMethodRigorously",
base::FEATURE_DISABLED_BY_DEFAULT);
#if BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL)
BASE_FEATURE(kChromeRootStoreUsed,
"ChromeRootStoreUsed",
#if BUILDFLAG(IS_ANDROID)
base::FEATURE_ENABLED_BY_DEFAULT
#else
base::FEATURE_DISABLED_BY_DEFAULT
#endif
);
#endif // BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL)
#if BUILDFLAG(IS_MAC) || BUILDFLAG(USE_NSS_CERTS) || BUILDFLAG(IS_WIN)
BASE_FEATURE(kTrustStoreTrustedLeafSupport,
"TrustStoreTrustedLeafSupport",
base::FEATURE_ENABLED_BY_DEFAULT);
#endif
BASE_FEATURE(kTurnOffStreamingMediaCachingOnBattery,
"TurnOffStreamingMediaCachingOnBattery",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kTurnOffStreamingMediaCachingAlways,
"TurnOffStreamingMediaCachingAlways",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kSchemefulSameSite,
"SchemefulSameSite",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kLimitOpenUDPSockets,
"LimitOpenUDPSockets",
base::FEATURE_ENABLED_BY_DEFAULT);
extern const base::FeatureParam<int> kLimitOpenUDPSocketsMax(
&kLimitOpenUDPSockets,
"LimitOpenUDPSocketsMax",
6000);
BASE_FEATURE(kTimeoutTcpConnectAttempt,
"TimeoutTcpConnectAttempt",
base::FEATURE_DISABLED_BY_DEFAULT);
extern const base::FeatureParam<double> kTimeoutTcpConnectAttemptRTTMultiplier(
&kTimeoutTcpConnectAttempt,
"TimeoutTcpConnectAttemptRTTMultiplier",
5.0);
extern const base::FeatureParam<base::TimeDelta> kTimeoutTcpConnectAttemptMin(
&kTimeoutTcpConnectAttempt,
"TimeoutTcpConnectAttemptMin",
base::Seconds(8));
extern const base::FeatureParam<base::TimeDelta> kTimeoutTcpConnectAttemptMax(
&kTimeoutTcpConnectAttempt,
"TimeoutTcpConnectAttemptMax",
base::Seconds(30));
#if BUILDFLAG(ENABLE_REPORTING)
BASE_FEATURE(kDocumentReporting,
"DocumentReporting",
base::FEATURE_ENABLED_BY_DEFAULT);
#endif // BUILDFLAG(ENABLE_REPORTING)
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
BASE_FEATURE(kUdpSocketPosixAlwaysUpdateBytesReceived,
"UdpSocketPosixAlwaysUpdateBytesReceived",
base::FEATURE_ENABLED_BY_DEFAULT);
#endif // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
BASE_FEATURE(kCookieSameSiteConsidersRedirectChain,
"CookieSameSiteConsidersRedirectChain",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kWaitForFirstPartySetsInit,
"WaitForFirstPartySetsInit",
base::FEATURE_DISABLED_BY_DEFAULT);
// Controls the maximum time duration an outermost frame navigation should be
// deferred by RWS initialization.
extern const base::FeatureParam<base::TimeDelta>
kWaitForFirstPartySetsInitNavigationThrottleTimeout{
&kWaitForFirstPartySetsInit,
"kWaitForFirstPartySetsInitNavigationThrottleTimeout",
base::Seconds(0)};
BASE_FEATURE(kPartitionedCookies,
"PartitionedCookies",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kBlockTruncatedCookies,
"BlockTruncatedCookies",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kStaticKeyPinningEnforcement,
"StaticKeyPinningEnforcement",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kCookieDomainRejectNonASCII,
"CookieDomainRejectNonASCII",
base::FEATURE_DISABLED_BY_DEFAULT);
// Enables partitioning of third party storage (IndexedDB, CacheStorage, etc.)
// by the top level site to reduce fingerprinting.
BASE_FEATURE(kThirdPartyStoragePartitioning,
"ThirdPartyStoragePartitioning",
base::FEATURE_ENABLED_BY_DEFAULT);
// Whether to use the new code paths needed to support partitioning Blob URLs.
// This exists as a kill-switch in case an issue is identified with the Blob
// URL implementation that causes breakage.
BASE_FEATURE(kSupportPartitionedBlobUrl,
"SupportPartitionedBlobUrl",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kTpcdTrialSettings,
"TpcdSupportSettings",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kTopLevelTpcdTrialSettings,
"TopLevelTpcdSupportSettings",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kTpcdMetadataGrants,
"TpcdMetadataGrants",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kAlpsParsing, "AlpsParsing", base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kAlpsClientHintParsing,
"AlpsClientHintParsing",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kShouldKillSessionOnAcceptChMalformed,
"ShouldKillSessionOnAcceptChMalformed",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kCaseInsensitiveCookiePrefix,
"CaseInsensitiveCookiePrefix",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kEnableWebsocketsOverHttp3,
"EnableWebsocketsOverHttp3",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kUseNAT64ForIPv4Literal,
"UseNAT64ForIPv4Literal",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kBlockNewForbiddenHeaders,
"BlockNewForbiddenHeaders",
base::FEATURE_ENABLED_BY_DEFAULT);
#if BUILDFLAG(IS_WIN)
BASE_FEATURE(kPlatformKeyProbeSHA256,
"PlatformKeyProbeSHA256",
base::FEATURE_ENABLED_BY_DEFAULT);
// Disabled because of https://crbug.com/1489696.
BASE_FEATURE(kEnableGetNetworkConnectivityHintAPI,
"EnableGetNetworkConnectivityHintAPI",
base::FEATURE_DISABLED_BY_DEFAULT);
#endif
// Prefetch to follow normal semantics instead of 5-minute rule
// https://crbug.com/1345207
BASE_FEATURE(kPrefetchFollowsNormalCacheSemantics,
"PrefetchFollowsNormalCacheSemantics",
base::FEATURE_DISABLED_BY_DEFAULT);
// A flag for new Kerberos feature, that suggests new UI
// when Kerberos authentication in browser fails on ChromeOS.
// b/260522530
#if BUILDFLAG(IS_CHROMEOS)
BASE_FEATURE(kKerberosInBrowserRedirect,
"KerberosInBrowserRedirect",
base::FEATURE_ENABLED_BY_DEFAULT);
#endif
// A flag to use asynchronous session creation for new QUIC sessions.
BASE_FEATURE(kAsyncQuicSession,
"AsyncQuicSession",
#if BUILDFLAG(IS_WIN)
base::FEATURE_ENABLED_BY_DEFAULT);
#else
base::FEATURE_DISABLED_BY_DEFAULT);
#endif
// A flag to make multiport context creation asynchronous.
BASE_FEATURE(kAsyncMultiPortPath,
"AsyncMultiPortPath",
#if !BUILDFLAG(CRONET_BUILD) && (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID))
base::FEATURE_ENABLED_BY_DEFAULT);
#else
base::FEATURE_DISABLED_BY_DEFAULT);
#endif
// IP protection experiment configuration settings
BASE_FEATURE(kEnableIpProtectionProxy,
"EnableIpPrivacyProxy",
base::FEATURE_DISABLED_BY_DEFAULT);
const base::FeatureParam<std::string> kIpPrivacyTokenServer{
&kEnableIpProtectionProxy, /*name=*/"IpPrivacyTokenServer",
/*default_value=*/"https://phosphor-pa.googleapis.com"};
const base::FeatureParam<std::string> kIpPrivacyTokenServerGetInitialDataPath{
&kEnableIpProtectionProxy,
/*name=*/"IpPrivacyTokenServerGetInitialDataPath",
/*default_value=*/"/v1/ipblinding/getInitialData"};
const base::FeatureParam<std::string> kIpPrivacyTokenServerGetTokensPath{
&kEnableIpProtectionProxy, /*name=*/"IpPrivacyTokenServerGetTokensPath",
/*default_value=*/"/v1/ipblinding/auth"};
const base::FeatureParam<std::string> kIpPrivacyTokenServerGetProxyConfigPath{
&kEnableIpProtectionProxy,
/*name=*/"IpPrivacyTokenServerGetProxyConfigPath",
/*default_value=*/"/v1/ipblinding/getProxyConfig"};
const base::FeatureParam<int> kIpPrivacyAuthTokenCacheBatchSize{
&kEnableIpProtectionProxy, /*name=*/"IpPrivacyAuthTokenCacheBatchSize",
/*default_value=*/64};
const base::FeatureParam<int> kIpPrivacyAuthTokenCacheLowWaterMark{
&kEnableIpProtectionProxy, /*name=*/"IpPrivacyAuthTokenCacheLowWaterMark",
/*default_value=*/16};
const base::FeatureParam<base::TimeDelta> kIpPrivacyProxyListFetchInterval{
&kEnableIpProtectionProxy, /*name=*/"IpPrivacyProxyListFetchInterval",
/*default_value=*/base::Hours(1)};
const base::FeatureParam<base::TimeDelta> kIpPrivacyProxyListMinFetchInterval{
&kEnableIpProtectionProxy, /*name=*/"IpPrivacyProxyMinListFetchInterval",
/*default_value=*/base::Minutes(1)};
const base::FeatureParam<bool> kIpPrivacyDirectOnly{
&kEnableIpProtectionProxy, /*name=*/"IpPrivacyDirectOnly",
/*default_value=*/false};
const base::FeatureParam<std::string> kIpPrivacyProxyBPsk{
&kEnableIpProtectionProxy, /*name=*/"IpPrivacyProxyBPsk",
/*default_value=*/""};
const base::FeatureParam<bool> kIpPrivacyIncludeOAuthTokenInGetProxyConfig{
&kEnableIpProtectionProxy,
/*name=*/"IpPrivacyIncludeOAuthTokenInGetProxyConfig",
/*default_value=*/false};
const base::FeatureParam<std::string> kIpPrivacyProxyAHostnameOverride{
&kEnableIpProtectionProxy, /*name=*/"IpPrivacyProxyAHostnameOverride",
/*default_value=*/""};
const base::FeatureParam<std::string> kIpPrivacyProxyBHostnameOverride{
&kEnableIpProtectionProxy, /*name=*/"IpPrivacyProxyBHostnameOverride",
/*default_value=*/""};
const base::FeatureParam<bool> kIpPrivacyAddHeaderToProxiedRequests{
&kEnableIpProtectionProxy, /*name=*/"IpPrivacyAddHeaderToProxiedRequests",
/*default_value=*/false};
const base::FeatureParam<base::TimeDelta> kIpPrivacyExpirationFuzz{
&kEnableIpProtectionProxy, /*name=*/"IpPrivacyExpirationFuzz",
/*default_value=*/base::Minutes(15)};
// Network-change migration requires NetworkHandle support, which are currently
// only supported on Android (see
// NetworkChangeNotifier::AreNetworkHandlesSupported).
#if BUILDFLAG(IS_ANDROID)
inline constexpr auto kMigrateSessionsOnNetworkChangeV2Default =
base::FEATURE_ENABLED_BY_DEFAULT;
#else // !BUILDFLAG(IS_ANDROID)
inline constexpr auto kMigrateSessionsOnNetworkChangeV2Default =
base::FEATURE_DISABLED_BY_DEFAULT;
#endif // BUILDFLAG(IS_ANDROID)
BASE_FEATURE(kMigrateSessionsOnNetworkChangeV2,
"MigrateSessionsOnNetworkChangeV2",
kMigrateSessionsOnNetworkChangeV2Default);
BASE_FEATURE(kDisableBlackholeOnNoNewNetwork,
"DisableBlackHoleOnNoNewNetwork",
base::FEATURE_DISABLED_BY_DEFAULT);
#if BUILDFLAG(IS_LINUX)
BASE_FEATURE(kAddressTrackerLinuxIsProxied,
"AddressTrackerLinuxIsProxied",
base::FEATURE_ENABLED_BY_DEFAULT);
#endif // BUILDFLAG(IS_LINUX)
// Enables binding of cookies to the port that originally set them by default.
BASE_FEATURE(kEnablePortBoundCookies,
"EnablePortBoundCookies",
base::FEATURE_DISABLED_BY_DEFAULT);
// Enables binding of cookies to the scheme that originally set them.
NET_EXPORT BASE_DECLARE_FEATURE(kEnableSchemeBoundCookies);
BASE_FEATURE(kEnableSchemeBoundCookies,
"EnableSchemeBoundCookies",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kTimeLimitedInsecureCookies,
"TimeLimitedInsecureCookies",
base::FEATURE_DISABLED_BY_DEFAULT);
// Enable third-party cookie blocking from the command line.
BASE_FEATURE(kForceThirdPartyCookieBlocking,
"ForceThirdPartyCookieBlockingEnabled",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kThirdPartyCookieTopLevelSiteCorsException,
"ThirdPartyCookieTopLevelSiteCorsException",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kEnableEarlyHintsOnHttp11,
"EnableEarlyHintsOnHttp11",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kEnableWebTransportDraft07,
"EnableWebTransportDraft07",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kZstdContentEncoding,
"ZstdContentEncoding",
base::FEATURE_ENABLED_BY_DEFAULT);
// When enabled, partitioned storage will be allowed even if third-party cookies
// are disabled by default. Partitioned storage will not be allowed if
// third-party cookies are disabled due to a specific rule.
BASE_FEATURE(kThirdPartyPartitionedStorageAllowedByDefault,
"ThirdPartyPartitionedStorageAllowedByDefault",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kPriorityHeader,
"PriorityHeader",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kSpdyHeadersToHttpResponseUseBuilder,
"SpdyHeadersToHttpResponseUseBuilder",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kReceiveEcn, "ReceiveEcn", base::FEATURE_DISABLED_BY_DEFAULT);
// TODO(crbug.com/634470): Remove this feature flag in January 2024 if the new
// limit sticks.
BASE_FEATURE(kNewCertPathBuilderIterationLimit,
"NewCertPathBuilderIterationLimit",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kUseNewAlpsCodepointHttp2,
"UseNewAlpsCodepointHttp2",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kUseNewAlpsCodepointQUIC,
"UseNewAlpsCodepointQUIC",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kTreatHTTPExpiresHeaderValueZeroAsExpired,
"TreatHTTPExpiresHeaderValueZeroAsExpired",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kTruncateBodyToContentLength,
"TruncateBodyToContentLength",
base::FEATURE_ENABLED_BY_DEFAULT);
} // namespace net::features

528
src/net/base/features.h Normal file
View File

@@ -0,0 +1,528 @@
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_FEATURES_H_
#define NET_BASE_FEATURES_H_
#include <string>
#include <string_view>
#include "base/feature_list.h"
#include "base/metrics/field_trial_params.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "crypto/crypto_buildflags.h"
#include "net/base/net_export.h"
#include "net/net_buildflags.h"
namespace net::features {
// Enables ALPS extension of TLS 1.3 for HTTP/2, see
// https://vasilvv.github.io/tls-alps/draft-vvv-tls-alps.html and
// https://vasilvv.github.io/httpbis-alps/draft-vvv-httpbis-alps.html.
NET_EXPORT BASE_DECLARE_FEATURE(kAlpsForHttp2);
// Disable H2 reprioritization, in order to measure its impact.
NET_EXPORT BASE_DECLARE_FEATURE(kAvoidH2Reprioritization);
// When kCapReferrerToOriginOnCrossOrigin is enabled, HTTP referrers on cross-
// origin requests are restricted to contain at most the source origin.
NET_EXPORT BASE_DECLARE_FEATURE(kCapReferrerToOriginOnCrossOrigin);
// Enables the built-in DNS resolver.
NET_EXPORT BASE_DECLARE_FEATURE(kAsyncDns);
// Support for altering the parameters used for DNS transaction timeout. See
// ResolveContext::SecureTransactionTimeout().
NET_EXPORT BASE_DECLARE_FEATURE(kDnsTransactionDynamicTimeouts);
// Multiplier applied to current fallback periods in determining a transaction
// timeout.
NET_EXPORT extern const base::FeatureParam<double>
kDnsTransactionTimeoutMultiplier;
NET_EXPORT extern const base::FeatureParam<base::TimeDelta>
kDnsMinTransactionTimeout;
// Enables querying HTTPS DNS records that will affect results from HostResolver
// and may be used to affect connection behavior. Whether or not those results
// are used (e.g. to connect via ECH) may be controlled by separate features.
NET_EXPORT BASE_DECLARE_FEATURE(kUseDnsHttpsSvcb);
// Param to control whether or not HostResolver, when using Secure DNS, will
// fail the entire connection attempt when receiving an inconclusive response to
// an HTTPS query (anything except transport error, timeout, or SERVFAIL). Used
// to prevent certain downgrade attacks against ECH behavior.
NET_EXPORT extern const base::FeatureParam<bool>
kUseDnsHttpsSvcbEnforceSecureResponse;
// If we are still waiting for an HTTPS transaction after all the
// other transactions in an insecure DnsTask have completed, we will compute a
// timeout for the remaining transaction. The timeout will be
// `kUseDnsHttpsSvcbInsecureExtraTimePercent.Get() / 100 * t`, where `t` is the
// time delta since the first query began. And the timeout will additionally be
// clamped by:
// (a) `kUseDnsHttpsSvcbInsecureExtraTimeMin.Get()`
// (b) `kUseDnsHttpsSvcbInsecureExtraTimeMax.Get()`
//
// Any param is ignored if zero, and if one of min/max is non-zero with a zero
// percent param it will be used as an absolute timeout. If all are zero, there
// is no timeout specific to HTTPS transactions, only the regular DNS query
// timeout and server fallback.
NET_EXPORT extern const base::FeatureParam<base::TimeDelta>
kUseDnsHttpsSvcbInsecureExtraTimeMax;
NET_EXPORT extern const base::FeatureParam<int>
kUseDnsHttpsSvcbInsecureExtraTimePercent;
NET_EXPORT extern const base::FeatureParam<base::TimeDelta>
kUseDnsHttpsSvcbInsecureExtraTimeMin;
// Same as `kUseDnsHttpsSvcbInsecureExtraTime...` except for secure DnsTasks.
//
// If `kUseDnsHttpsSvcbEnforceSecureResponse` is enabled, the timeouts will not
// be used because there is no sense killing a transaction early if that will
// just kill the entire request.
NET_EXPORT extern const base::FeatureParam<base::TimeDelta>
kUseDnsHttpsSvcbSecureExtraTimeMax;
NET_EXPORT extern const base::FeatureParam<int>
kUseDnsHttpsSvcbSecureExtraTimePercent;
NET_EXPORT extern const base::FeatureParam<base::TimeDelta>
kUseDnsHttpsSvcbSecureExtraTimeMin;
// Update protocol using ALPN information in HTTPS DNS records.
NET_EXPORT BASE_DECLARE_FEATURE(kUseDnsHttpsSvcbAlpn);
// If enabled, HostResolver will use the new HostResolverCache that separately
// caches by DNS type, unlike the old HostCache that always cached by merged
// request results. May enable related behavior such as separately sorting DNS
// results after each transaction rather than sorting collectively after all
// transactions complete.
NET_EXPORT BASE_DECLARE_FEATURE(kUseHostResolverCache);
// If the `kUseAlternativePortForGloballyReachableCheck` flag is enabled, the
// globally reachable check will use the port number specified by
// `kAlternativePortForGloballyReachableCheck` flag. Otherwise, the globally
// reachable check will use 443 port.
NET_EXPORT extern const base::FeatureParam<int>
kAlternativePortForGloballyReachableCheck;
NET_EXPORT BASE_DECLARE_FEATURE(kUseAlternativePortForGloballyReachableCheck);
// If enabled, overrides IPv6 reachability probe results based on the system's
// IP addresses.
NET_EXPORT BASE_DECLARE_FEATURE(kEnableIPv6ReachabilityOverride);
// Enables TLS 1.3 early data.
NET_EXPORT BASE_DECLARE_FEATURE(kEnableTLS13EarlyData);
// Enables checking the X.509 keyUsage extension in TLS 1.2 for RSA server
// certificates that chain to a local trust anchor.
//
// Independent of the setting of this feature, keyUsage is always checked at TLS
// 1.3, for ECDSA certificates, and for all certificates that chain to a known
// root.
NET_EXPORT BASE_DECLARE_FEATURE(kRSAKeyUsageForLocalAnchors);
// Enables optimizing the network quality estimation algorithms in network
// quality estimator (NQE).
NET_EXPORT BASE_DECLARE_FEATURE(kNetworkQualityEstimator);
// Splits cache entries by the request's includeCredentials.
NET_EXPORT BASE_DECLARE_FEATURE(kSplitCacheByIncludeCredentials);
// Splits cache entries by the request's NetworkIsolationKey if one is
// available.
NET_EXPORT BASE_DECLARE_FEATURE(kSplitCacheByNetworkIsolationKey);
// Splits the generated code cache by the request's NetworkIsolationKey if one
// is available. Note that this feature is also gated behind
// `net::HttpCache::IsSplitCacheEnabled()`.
NET_EXPORT BASE_DECLARE_FEATURE(kSplitCodeCacheByNetworkIsolationKey);
// Splits host cache entries by the DNS request's NetworkAnonymizationKey if one
// is available. Also prevents merging live DNS lookups when there is a NAK
// mismatch.
NET_EXPORT BASE_DECLARE_FEATURE(kSplitHostCacheByNetworkIsolationKey);
// Partitions connections based on the NetworkAnonymizationKey associated with a
// request.
NET_EXPORT BASE_DECLARE_FEATURE(kPartitionConnectionsByNetworkIsolationKey);
// Partitions HttpServerProperties based on the NetworkAnonymizationKey
// associated with a request.
NET_EXPORT BASE_DECLARE_FEATURE(
kPartitionHttpServerPropertiesByNetworkIsolationKey);
// Partitions TLS sessions and QUIC server configs based on the
// NetworkAnonymizationKey associated with a request.
//
// This feature requires kPartitionConnectionsByNetworkIsolationKey to be
// enabled to work.
NET_EXPORT BASE_DECLARE_FEATURE(kPartitionSSLSessionsByNetworkIsolationKey);
// Partitions Network Error Logging and Reporting API data by
// NetworkAnonymizationKey. Also partitions all reports generated by other
// consumers of the reporting API. Applies the NetworkAnonymizationKey to
// reports uploads as well.
//
// When disabled, the main entry points of the reporting and NEL services ignore
// NetworkAnonymizationKey parameters, and they're cleared while loading from
// the cache, but internal objects can be created with them (e.g., endpoints),
// for testing.
NET_EXPORT BASE_DECLARE_FEATURE(kPartitionNelAndReportingByNetworkIsolationKey);
// Creates a <double key + is_cross_site> NetworkIsolationKey which is used
// to partition the HTTP cache. This key will have the following properties:
// `top_frame_site_` -> the schemeful site of the top level page.
// `frame_site_` -> std::nullopt.
// `is_cross_site_` -> a boolean indicating whether the frame site is
// schemefully cross-site from the top-level site.
NET_EXPORT BASE_DECLARE_FEATURE(kEnableCrossSiteFlagNetworkIsolationKey);
NET_EXPORT BASE_DECLARE_FEATURE(
kEnableFrameSiteSharedOpaqueNetworkIsolationKey);
NET_EXPORT BASE_DECLARE_FEATURE(kHttpCacheKeyingExperimentControlGroup);
// Enables sending TLS 1.3 Key Update messages on TLS 1.3 connections in order
// to ensure that this corner of the spec is exercised. This is currently
// disabled by default because we discovered incompatibilities with some
// servers.
NET_EXPORT BASE_DECLARE_FEATURE(kTLS13KeyUpdate);
// Enables permuting TLS extensions in the ClientHello, to reduce the risk of
// non-compliant servers ossifying parts of the ClientHello and interfering with
// deployment of future security improvements.
NET_EXPORT BASE_DECLARE_FEATURE(kPermuteTLSExtensions);
// Enables Kyber-based post-quantum key-agreements in TLS 1.3 connections.
NET_EXPORT BASE_DECLARE_FEATURE(kPostQuantumKyber);
// Changes the timeout after which unused sockets idle sockets are cleaned up.
NET_EXPORT BASE_DECLARE_FEATURE(kNetUnusedIdleSocketTimeout);
// When enabled, the time threshold for Lax-allow-unsafe cookies will be lowered
// from 2 minutes to 10 seconds. This time threshold refers to the age cutoff
// for which cookies that default into SameSite=Lax, which are newer than the
// threshold, will be sent with any top-level cross-site navigation regardless
// of HTTP method (i.e. allowing unsafe methods). This is a convenience for
// integration tests which may want to test behavior of cookies older than the
// threshold, but which would not be practical to run for 2 minutes.
NET_EXPORT BASE_DECLARE_FEATURE(kShortLaxAllowUnsafeThreshold);
// When enabled, the SameSite by default feature does not add the
// "Lax-allow-unsafe" behavior. Any cookies that do not specify a SameSite
// attribute will be treated as Lax only, i.e. POST and other unsafe HTTP
// methods will not be allowed at all for top-level cross-site navigations.
// This only has an effect if the cookie defaults to SameSite=Lax.
NET_EXPORT BASE_DECLARE_FEATURE(kSameSiteDefaultChecksMethodRigorously);
#if BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL)
// When enabled, use the Chrome Root Store instead of the system root store
NET_EXPORT BASE_DECLARE_FEATURE(kChromeRootStoreUsed);
#endif // BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL)
// When enabled, bssl::TrustStore implementations will use TRUSTED_LEAF,
// TRUSTED_ANCHOR_OR_LEAF, and TRUSTED_ANCHOR as appropriate. When disabled,
// bssl::TrustStore implementation will only use TRUSTED_ANCHOR.
// TODO(https://crbug.com/1403034): remove this a few milestones after the
// trusted leaf support has been launched on all relevant platforms.
#if BUILDFLAG(IS_MAC) || BUILDFLAG(USE_NSS_CERTS) || BUILDFLAG(IS_WIN)
NET_EXPORT BASE_DECLARE_FEATURE(kTrustStoreTrustedLeafSupport);
#endif
// Turns off streaming media caching to disk when on battery power.
NET_EXPORT BASE_DECLARE_FEATURE(kTurnOffStreamingMediaCachingOnBattery);
// Turns off streaming media caching to disk always.
NET_EXPORT BASE_DECLARE_FEATURE(kTurnOffStreamingMediaCachingAlways);
// When enabled this feature will cause same-site calculations to take into
// account the scheme of the site-for-cookies and the request/response url.
NET_EXPORT BASE_DECLARE_FEATURE(kSchemefulSameSite);
// Enables a process-wide limit on "open" UDP sockets. See
// udp_socket_global_limits.h for details on what constitutes an "open" socket.
NET_EXPORT BASE_DECLARE_FEATURE(kLimitOpenUDPSockets);
// FeatureParams associated with kLimitOpenUDPSockets.
// Sets the maximum allowed open UDP sockets. Provisioning more sockets than
// this will result in a failure (ERR_INSUFFICIENT_RESOURCES).
NET_EXPORT extern const base::FeatureParam<int> kLimitOpenUDPSocketsMax;
// Enables a timeout on individual TCP connect attempts, based on
// the parameter values.
NET_EXPORT BASE_DECLARE_FEATURE(kTimeoutTcpConnectAttempt);
// FeatureParams associated with kTimeoutTcpConnectAttempt.
// When there is an estimated RTT available, the experimental TCP connect
// attempt timeout is calculated as:
//
// clamp(kTimeoutTcpConnectAttemptMin,
// kTimeoutTcpConnectAttemptMax,
// <Estimated RTT> * kTimeoutTcpConnectAttemptRTTMultiplier);
//
// Otherwise the TCP connect attempt timeout is set to
// kTimeoutTcpConnectAttemptMax.
NET_EXPORT extern const base::FeatureParam<double>
kTimeoutTcpConnectAttemptRTTMultiplier;
NET_EXPORT extern const base::FeatureParam<base::TimeDelta>
kTimeoutTcpConnectAttemptMin;
NET_EXPORT extern const base::FeatureParam<base::TimeDelta>
kTimeoutTcpConnectAttemptMax;
#if BUILDFLAG(ENABLE_REPORTING)
// When enabled this feature will allow a new Reporting-Endpoints header to
// configure reporting endpoints for report delivery. This is used to support
// the new Document Reporting spec.
NET_EXPORT BASE_DECLARE_FEATURE(kDocumentReporting);
#endif // BUILDFLAG(ENABLE_REPORTING)
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
// When enabled, UDPSocketPosix increments the global counter of bytes received
// every time bytes are received, instead of using a timer to batch updates.
// This should reduce the number of wake ups and improve battery consumption.
// TODO(https://crbug.com/1189805): Cleanup the feature after verifying that it
// doesn't negatively affect performance.
NET_EXPORT BASE_DECLARE_FEATURE(kUdpSocketPosixAlwaysUpdateBytesReceived);
#endif // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
// When this feature is enabled, redirected requests will be considered
// cross-site for the purpose of SameSite cookies if any redirect hop was
// cross-site to the target URL, even if the original initiator of the
// redirected request was same-site with the target URL (and the
// site-for-cookies).
// See spec changes in https://github.com/httpwg/http-extensions/pull/1348
NET_EXPORT BASE_DECLARE_FEATURE(kCookieSameSiteConsidersRedirectChain);
// When this feature is enabled, the network service will wait until First-Party
// Sets are initialized before issuing requests that use the HTTP cache or
// cookies.
NET_EXPORT BASE_DECLARE_FEATURE(kWaitForFirstPartySetsInit);
// Controls the maximum time duration an outermost frame navigation should be
// deferred by RWS initialization.
NET_EXPORT extern const base::FeatureParam<base::TimeDelta>
kWaitForFirstPartySetsInitNavigationThrottleTimeout;
// When enabled, sites can opt-in to having their cookies partitioned by
// top-level site with the Partitioned attribute. Partitioned cookies will only
// be sent when the browser is on the same top-level site that it was on when
// the cookie was set.
NET_EXPORT BASE_DECLARE_FEATURE(kPartitionedCookies);
// When enabled, cookie-related code will treat cookies containing '\0', '\r',
// and '\n' as invalid and reject the cookie.
NET_EXPORT BASE_DECLARE_FEATURE(kBlockTruncatedCookies);
// Controls whether static key pinning is enforced.
NET_EXPORT BASE_DECLARE_FEATURE(kStaticKeyPinningEnforcement);
// When enabled, cookies with a non-ASCII domain attribute will be rejected.
NET_EXPORT BASE_DECLARE_FEATURE(kCookieDomainRejectNonASCII);
NET_EXPORT BASE_DECLARE_FEATURE(kThirdPartyStoragePartitioning);
NET_EXPORT BASE_DECLARE_FEATURE(kSupportPartitionedBlobUrl);
// Feature to enable consideration of 3PC deprecation trial settings.
NET_EXPORT BASE_DECLARE_FEATURE(kTpcdTrialSettings);
// Feature to enable consideration of top-level 3PC deprecation trial settings.
NET_EXPORT BASE_DECLARE_FEATURE(kTopLevelTpcdTrialSettings);
// Whether to enable the use of 3PC based on 3PCD metadata grants delivered via
// component updater.
NET_EXPORT BASE_DECLARE_FEATURE(kTpcdMetadataGrants);
// Whether ALPS parsing is on for any type of frame.
NET_EXPORT BASE_DECLARE_FEATURE(kAlpsParsing);
// Whether ALPS parsing is on for client hint parsing specifically.
NET_EXPORT BASE_DECLARE_FEATURE(kAlpsClientHintParsing);
// Whether to kill the session on Error::kAcceptChMalformed.
NET_EXPORT BASE_DECLARE_FEATURE(kShouldKillSessionOnAcceptChMalformed);
NET_EXPORT BASE_DECLARE_FEATURE(kCaseInsensitiveCookiePrefix);
NET_EXPORT BASE_DECLARE_FEATURE(kEnableWebsocketsOverHttp3);
// Whether to do IPv4 to IPv6 address translation for IPv4 literals.
NET_EXPORT BASE_DECLARE_FEATURE(kUseNAT64ForIPv4Literal);
// Whether to block newly added forbidden headers (https://crbug.com/1362331).
NET_EXPORT BASE_DECLARE_FEATURE(kBlockNewForbiddenHeaders);
#if BUILDFLAG(IS_WIN)
// Whether to probe for SHA-256 on some legacy platform keys, before assuming
// the key requires SHA-1. See SSLPlatformKeyWin for details.
NET_EXPORT BASE_DECLARE_FEATURE(kPlatformKeyProbeSHA256);
// Whether or not to use the GetNetworkConnectivityHint API on modern Windows
// versions for the Network Change Notifier.
NET_EXPORT BASE_DECLARE_FEATURE(kEnableGetNetworkConnectivityHintAPI);
#endif
// Prefetch to follow normal semantics instead of 5-minute rule
// https://crbug.com/1345207
NET_EXPORT BASE_DECLARE_FEATURE(kPrefetchFollowsNormalCacheSemantics);
// A flag for new Kerberos feature, that suggests new UI
// when Kerberos authentication in browser fails on ChromeOS.
// b/260522530
#if BUILDFLAG(IS_CHROMEOS)
NET_EXPORT BASE_DECLARE_FEATURE(kKerberosInBrowserRedirect);
#endif
// A flag to use asynchronous session creation for new QUIC sessions.
NET_EXPORT BASE_DECLARE_FEATURE(kAsyncQuicSession);
// A flag to make multiport context creation asynchronous.
NET_EXPORT BASE_DECLARE_FEATURE(kAsyncMultiPortPath);
// Enables custom proxy configuration for the IP Protection experimental proxy.
NET_EXPORT BASE_DECLARE_FEATURE(kEnableIpProtectionProxy);
// Sets the name of the IP protection auth token server.
NET_EXPORT extern const base::FeatureParam<std::string> kIpPrivacyTokenServer;
// Sets the path component of the IP protection auth token server URL used for
// getting initial token signing data.
NET_EXPORT extern const base::FeatureParam<std::string>
kIpPrivacyTokenServerGetInitialDataPath;
// Sets the path component of the IP protection auth token server URL used for
// getting blind-signed tokens.
NET_EXPORT extern const base::FeatureParam<std::string>
kIpPrivacyTokenServerGetTokensPath;
// Sets the path component of the IP protection auth token server URL used for
// getting proxy configuration.
NET_EXPORT extern const base::FeatureParam<std::string>
kIpPrivacyTokenServerGetProxyConfigPath;
// Sets the batch size to fetch new auth tokens for IP protection.
NET_EXPORT extern const base::FeatureParam<int>
kIpPrivacyAuthTokenCacheBatchSize;
// Sets the cache low-water-mark for auth tokens for IP protection.
NET_EXPORT extern const base::FeatureParam<int>
kIpPrivacyAuthTokenCacheLowWaterMark;
// Sets the normal time between fetches of the IP protection proxy list.
NET_EXPORT extern const base::FeatureParam<base::TimeDelta>
kIpPrivacyProxyListFetchInterval;
// Sets the minimum time between fetches of the IP protection proxy list, such
// as when a re-fetch is forced due to an error.
NET_EXPORT extern const base::FeatureParam<base::TimeDelta>
kIpPrivacyProxyListMinFetchInterval;
// Overrides the ProxyA hostname normally set by the proxylist fetch.
NET_EXPORT extern const base::FeatureParam<std::string>
kIpPrivacyProxyAHostnameOverride;
// Overrides the ProxyB hostname normally set by the proxylist fetch.
NET_EXPORT extern const base::FeatureParam<std::string>
kIpPrivacyProxyBHostnameOverride;
// Controls whether IP Protection _proxying_ is bypassed by not including any
// of the proxies in the proxy list. This supports experimental comparison of
// connections that _would_ have been proxied, but were not.
NET_EXPORT extern const base::FeatureParam<bool> kIpPrivacyDirectOnly;
// The PSK added to connections to proxyB with `Proxy-Authorization: Preshared
// $PSK`.
NET_EXPORT extern const base::FeatureParam<std::string> kIpPrivacyProxyBPsk;
// If true, pass OAuth token to Phosphor in GetProxyConfig API for IP
// Protection.
NET_EXPORT extern const base::FeatureParam<bool>
kIpPrivacyIncludeOAuthTokenInGetProxyConfig;
// Controls whether a header ("IP-Protection: 1") should be added to proxied
// network requests.
NET_EXPORT extern const base::FeatureParam<bool>
kIpPrivacyAddHeaderToProxiedRequests;
// Token expirations will have a random time between 5 seconds and this delta
// subtracted from their expiration, in order to even out the load on the token
// servers.
NET_EXPORT extern const base::FeatureParam<base::TimeDelta>
kIpPrivacyExpirationFuzz;
// Whether QuicParams::migrate_sessions_on_network_change_v2 defaults to true or
// false. This is needed as a workaround to set this value to true on Android
// but not on WebView (until crbug.com/1430082 has been fixed).
NET_EXPORT BASE_DECLARE_FEATURE(kMigrateSessionsOnNetworkChangeV2);
// Enables whether blackhole detector should be disabled during connection
// migration and there is no available network.
NET_EXPORT BASE_DECLARE_FEATURE(kDisableBlackholeOnNoNewNetwork);
#if BUILDFLAG(IS_LINUX)
// AddressTrackerLinux will not run inside the network service in this
// configuration, which will improve the Linux network service sandbox.
// TODO(crbug.com/1312226): remove this.
NET_EXPORT BASE_DECLARE_FEATURE(kAddressTrackerLinuxIsProxied);
#endif // BUILDFLAG(IS_LINUX)
// Enables binding of cookies to the port that originally set them by default.
NET_EXPORT BASE_DECLARE_FEATURE(kEnablePortBoundCookies);
// Enables binding of cookies to the scheme that originally set them. Also
// enables domain cookie shadowing protection.
NET_EXPORT BASE_DECLARE_FEATURE(kEnableSchemeBoundCookies);
// Enables expiration duration limit (3 hours) for cookies on insecure websites.
// This feature is a no-op unless kEnableSchemeBoundCookies is enabled.
NET_EXPORT BASE_DECLARE_FEATURE(kTimeLimitedInsecureCookies);
// Enables enabling third-party cookie blocking from the command line.
NET_EXPORT BASE_DECLARE_FEATURE(kForceThirdPartyCookieBlocking);
// Enables an exception for third-party cookie blocking when the request is
// same-site with the top-level document, opted into CORS, but embedded in a
// cross-site context.
NET_EXPORT BASE_DECLARE_FEATURE(kThirdPartyCookieTopLevelSiteCorsException);
// Enables Early Hints on HTTP/1.1.
NET_EXPORT BASE_DECLARE_FEATURE(kEnableEarlyHintsOnHttp11);
// Enables draft-07 version of WebTransport over HTTP/3.
NET_EXPORT BASE_DECLARE_FEATURE(kEnableWebTransportDraft07);
// Enables Zstandard Content-Encoding support.
NET_EXPORT BASE_DECLARE_FEATURE(kZstdContentEncoding);
NET_EXPORT BASE_DECLARE_FEATURE(kThirdPartyPartitionedStorageAllowedByDefault);
// Enables the HTTP extensible priorities "priority" header.
// RFC 9218
NET_EXPORT BASE_DECLARE_FEATURE(kPriorityHeader);
// Enables a more efficient implementation of SpdyHeadersToHttpResponse().
NET_EXPORT BASE_DECLARE_FEATURE(kSpdyHeadersToHttpResponseUseBuilder);
// Enables receiving ECN bit by sockets in Chrome.
NET_EXPORT BASE_DECLARE_FEATURE(kReceiveEcn);
NET_EXPORT BASE_DECLARE_FEATURE(kNewCertPathBuilderIterationLimit);
// Enables using the new ALPS codepoint to negotiate application settings for
// HTTP2.
NET_EXPORT BASE_DECLARE_FEATURE(kUseNewAlpsCodepointHttp2);
// Enables using the new ALPS codepoint to negotiate application settings for
// QUIC.
NET_EXPORT BASE_DECLARE_FEATURE(kUseNewAlpsCodepointQUIC);
// Treat HTTP header `Expires: "0"` as expired value according section 5.3 on
// RFC 9111.
// TODO(https://crbug.com/853508): Remove after the bug fix will go well for a
// while on stable channels.
NET_EXPORT BASE_DECLARE_FEATURE(kTreatHTTPExpiresHeaderValueZeroAsExpired);
// Enables truncating the response body to the content length.
NET_EXPORT BASE_DECLARE_FEATURE(kTruncateBodyToContentLength);
} // namespace net::features
#endif // NET_BASE_FEATURES_H_

View File

@@ -0,0 +1,94 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/file_stream.h"
#include <utility>
#include "net/base/file_stream_context.h"
#include "net/base/net_errors.h"
namespace net {
FileStream::FileStream(const scoped_refptr<base::TaskRunner>& task_runner)
: context_(std::make_unique<Context>(task_runner)) {}
FileStream::FileStream(base::File file,
const scoped_refptr<base::TaskRunner>& task_runner)
: context_(std::make_unique<Context>(std::move(file), task_runner)) {}
FileStream::~FileStream() {
context_.release()->Orphan();
}
int FileStream::Open(const base::FilePath& path,
int open_flags,
CompletionOnceCallback callback) {
if (IsOpen()) {
DLOG(FATAL) << "File is already open!";
return ERR_UNEXPECTED;
}
DCHECK(open_flags & base::File::FLAG_ASYNC);
context_->Open(path, open_flags, std::move(callback));
return ERR_IO_PENDING;
}
int FileStream::Close(CompletionOnceCallback callback) {
context_->Close(std::move(callback));
return ERR_IO_PENDING;
}
bool FileStream::IsOpen() const {
return context_->IsOpen();
}
int FileStream::Seek(int64_t offset, Int64CompletionOnceCallback callback) {
if (!IsOpen())
return ERR_UNEXPECTED;
context_->Seek(offset, std::move(callback));
return ERR_IO_PENDING;
}
int FileStream::Read(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) {
if (!IsOpen())
return ERR_UNEXPECTED;
// read(..., 0) will return 0, which indicates end-of-file.
DCHECK_GT(buf_len, 0);
return context_->Read(buf, buf_len, std::move(callback));
}
int FileStream::Write(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) {
if (!IsOpen())
return ERR_UNEXPECTED;
DCHECK_GE(buf_len, 0);
return context_->Write(buf, buf_len, std::move(callback));
}
int FileStream::GetFileInfo(base::File::Info* file_info,
CompletionOnceCallback callback) {
if (!IsOpen())
return ERR_UNEXPECTED;
context_->GetFileInfo(file_info, std::move(callback));
return ERR_IO_PENDING;
}
int FileStream::Flush(CompletionOnceCallback callback) {
if (!IsOpen())
return ERR_UNEXPECTED;
context_->Flush(std::move(callback));
return ERR_IO_PENDING;
}
} // namespace net

173
src/net/base/file_stream.h Normal file
View File

@@ -0,0 +1,173 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file defines FileStream, a basic interface for reading and writing files
// synchronously or asynchronously with support for seeking to an offset.
// Note that even when used asynchronously, only one operation is supported at
// a time.
#ifndef NET_BASE_FILE_STREAM_H_
#define NET_BASE_FILE_STREAM_H_
#include <stdint.h>
#include <memory>
#include "base/files/file.h"
#include "net/base/completion_once_callback.h"
#include "net/base/net_export.h"
namespace base {
class FilePath;
class TaskRunner;
}
namespace net {
class IOBuffer;
class NET_EXPORT FileStream {
public:
// Uses |task_runner| for asynchronous operations.
explicit FileStream(const scoped_refptr<base::TaskRunner>& task_runner);
// Construct a FileStream with an already opened file. |file| must be opened
// for async reading on Windows, and sync reading everywehere else.
//
// Uses |task_runner| for asynchronous operations.
FileStream(base::File file,
const scoped_refptr<base::TaskRunner>& task_runner);
FileStream(const FileStream&) = delete;
FileStream& operator=(const FileStream&) = delete;
// The underlying file is closed automatically.
virtual ~FileStream();
// Call this method to open the FileStream asynchronously. The remaining
// methods cannot be used unless the file is opened successfully. Returns
// ERR_IO_PENDING if the operation is started. If the operation cannot be
// started then an error code is returned.
//
// Once the operation is done, |callback| will be run on the thread where
// Open() was called, with the result code. open_flags is a bitfield of
// base::File::Flags.
//
// If the file stream is not closed manually, the underlying file will be
// automatically closed when FileStream is destructed in an asynchronous
// manner (i.e. the file stream is closed in the background but you don't
// know when).
virtual int Open(const base::FilePath& path,
int open_flags,
CompletionOnceCallback callback);
// Returns ERR_IO_PENDING and closes the file asynchronously, calling
// |callback| when done.
// It is invalid to request any asynchronous operations while there is an
// in-flight asynchronous operation.
virtual int Close(CompletionOnceCallback callback);
// Returns true if Open succeeded and Close has not been called.
virtual bool IsOpen() const;
// Adjust the position from the start of the file where data is read
// asynchronously. Upon success, ERR_IO_PENDING is returned and |callback|
// will be run on the thread where Seek() was called with the the stream
// position relative to the start of the file. Otherwise, an error code is
// returned. It is invalid to request any asynchronous operations while there
// is an in-flight asynchronous operation.
virtual int Seek(int64_t offset, Int64CompletionOnceCallback callback);
// Call this method to read data from the current stream position
// asynchronously. Up to buf_len bytes will be copied into buf. (In
// other words, partial reads are allowed.) Returns the number of bytes
// copied, 0 if at end-of-file, or an error code if the operation could
// not be performed.
//
// The file must be opened with FLAG_ASYNC, and a non-null
// callback must be passed to this method. If the read could not
// complete synchronously, then ERR_IO_PENDING is returned, and the
// callback will be run on the thread where Read() was called, when the
// read has completed.
//
// It is valid to destroy or close the file stream while there is an
// asynchronous read in progress. That will cancel the read and allow
// the buffer to be freed.
//
// It is invalid to request any asynchronous operations while there is an
// in-flight asynchronous operation.
//
// This method must not be called if the stream was opened WRITE_ONLY.
virtual int Read(IOBuffer* buf, int buf_len, CompletionOnceCallback callback);
// Call this method to write data at the current stream position
// asynchronously. Up to buf_len bytes will be written from buf. (In
// other words, partial writes are allowed.) Returns the number of
// bytes written, or an error code if the operation could not be
// performed.
//
// The file must be opened with FLAG_ASYNC, and a non-null
// callback must be passed to this method. If the write could not
// complete synchronously, then ERR_IO_PENDING is returned, and the
// callback will be run on the thread where Write() was called when
// the write has completed.
//
// It is valid to destroy or close the file stream while there is an
// asynchronous write in progress. That will cancel the write and allow
// the buffer to be freed.
//
// It is invalid to request any asynchronous operations while there is an
// in-flight asynchronous operation.
//
// This method must not be called if the stream was opened READ_ONLY.
//
// Zero byte writes are not allowed.
virtual int Write(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback);
// Gets status information about File. May fail synchronously, but never
// succeeds synchronously.
//
// It is invalid to request any asynchronous operations while there is an
// in-flight asynchronous operation.
//
// |file_info| must remain valid until |callback| is invoked.
virtual int GetFileInfo(base::File::Info* file_info,
CompletionOnceCallback callback);
// Forces out a filesystem sync on this file to make sure that the file was
// written out to disk and is not currently sitting in the buffer. This does
// not have to be called, it just forces one to happen at the time of
// calling.
//
// The file must be opened with FLAG_ASYNC, and a non-null
// callback must be passed to this method. If the write could not
// complete synchronously, then ERR_IO_PENDING is returned, and the
// callback will be run on the thread where Flush() was called when
// the write has completed.
//
// It is valid to destroy or close the file stream while there is an
// asynchronous flush in progress. That will cancel the flush and allow
// the buffer to be freed.
//
// It is invalid to request any asynchronous operations while there is an
// in-flight asynchronous operation.
//
// This method should not be called if the stream was opened READ_ONLY.
virtual int Flush(CompletionOnceCallback callback);
private:
class Context;
// Context performing I/O operations. It was extracted into a separate class
// to perform asynchronous operations because FileStream can be destroyed
// before completion of an async operation. Also if a FileStream is destroyed
// without explicitly calling Close, the file should be closed asynchronously
// without delaying FileStream's destructor.
std::unique_ptr<Context> context_;
};
} // namespace net
#endif // NET_BASE_FILE_STREAM_H_

View File

@@ -0,0 +1,254 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/file_stream_context.h"
#include <utility>
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/task/task_runner.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "net/base/net_errors.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/content_uri_utils.h"
#endif
namespace net {
namespace {
void CallInt64ToInt(CompletionOnceCallback callback, int64_t result) {
std::move(callback).Run(static_cast<int>(result));
}
} // namespace
FileStream::Context::IOResult::IOResult()
: result(OK),
os_error(0) {
}
FileStream::Context::IOResult::IOResult(int64_t result,
logging::SystemErrorCode os_error)
: result(result), os_error(os_error) {
}
// static
FileStream::Context::IOResult FileStream::Context::IOResult::FromOSError(
logging::SystemErrorCode os_error) {
return IOResult(MapSystemError(os_error), os_error);
}
// ---------------------------------------------------------------------
FileStream::Context::OpenResult::OpenResult() = default;
FileStream::Context::OpenResult::OpenResult(base::File file,
IOResult error_code)
: file(std::move(file)), error_code(error_code) {}
FileStream::Context::OpenResult::OpenResult(OpenResult&& other)
: file(std::move(other.file)), error_code(other.error_code) {}
FileStream::Context::OpenResult& FileStream::Context::OpenResult::operator=(
OpenResult&& other) {
file = std::move(other.file);
error_code = other.error_code;
return *this;
}
// ---------------------------------------------------------------------
void FileStream::Context::Orphan() {
DCHECK(!orphaned_);
orphaned_ = true;
if (!async_in_progress_) {
CloseAndDelete();
} else if (file_.IsValid()) {
#if BUILDFLAG(IS_WIN)
CancelIo(file_.GetPlatformFile());
#endif
}
}
void FileStream::Context::Open(const base::FilePath& path,
int open_flags,
CompletionOnceCallback callback) {
DCHECK(!async_in_progress_);
bool posted = task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&Context::OpenFileImpl, base::Unretained(this), path,
open_flags),
base::BindOnce(&Context::OnOpenCompleted, base::Unretained(this),
std::move(callback)));
DCHECK(posted);
async_in_progress_ = true;
}
void FileStream::Context::Close(CompletionOnceCallback callback) {
DCHECK(!async_in_progress_);
bool posted = task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&Context::CloseFileImpl, base::Unretained(this)),
base::BindOnce(&Context::OnAsyncCompleted, base::Unretained(this),
IntToInt64(std::move(callback))));
DCHECK(posted);
async_in_progress_ = true;
}
void FileStream::Context::Seek(int64_t offset,
Int64CompletionOnceCallback callback) {
DCHECK(!async_in_progress_);
if (offset < 0) {
std::move(callback).Run(net::ERR_INVALID_ARGUMENT);
return;
}
bool posted = task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&Context::SeekFileImpl, base::Unretained(this), offset),
base::BindOnce(&Context::OnAsyncCompleted, base::Unretained(this),
std::move(callback)));
DCHECK(posted);
async_in_progress_ = true;
}
void FileStream::Context::GetFileInfo(base::File::Info* file_info,
CompletionOnceCallback callback) {
task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&Context::GetFileInfoImpl, base::Unretained(this),
base::Unretained(file_info)),
base::BindOnce(&Context::OnAsyncCompleted, base::Unretained(this),
IntToInt64(std::move(callback))));
async_in_progress_ = true;
}
void FileStream::Context::Flush(CompletionOnceCallback callback) {
DCHECK(!async_in_progress_);
bool posted = task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&Context::FlushFileImpl, base::Unretained(this)),
base::BindOnce(&Context::OnAsyncCompleted, base::Unretained(this),
IntToInt64(std::move(callback))));
DCHECK(posted);
async_in_progress_ = true;
}
bool FileStream::Context::IsOpen() const {
return file_.IsValid();
}
FileStream::Context::OpenResult FileStream::Context::OpenFileImpl(
const base::FilePath& path, int open_flags) {
#if BUILDFLAG(IS_POSIX)
// Always use blocking IO.
open_flags &= ~base::File::FLAG_ASYNC;
#endif
base::File file;
#if BUILDFLAG(IS_ANDROID)
if (path.IsContentUri()) {
// Check that only Read flags are set.
DCHECK_EQ(open_flags & ~base::File::FLAG_ASYNC,
base::File::FLAG_OPEN | base::File::FLAG_READ);
file = base::OpenContentUriForRead(path);
} else {
#endif // BUILDFLAG(IS_ANDROID)
// FileStream::Context actually closes the file asynchronously,
// independently from FileStream's destructor. It can cause problems for
// users wanting to delete the file right after FileStream deletion. Thus
// we are always adding SHARE_DELETE flag to accommodate such use case.
// TODO(rvargas): This sounds like a bug, as deleting the file would
// presumably happen on the wrong thread. There should be an async delete.
open_flags |= base::File::FLAG_WIN_SHARE_DELETE;
file.Initialize(path, open_flags);
#if BUILDFLAG(IS_ANDROID)
}
#endif // BUILDFLAG(IS_ANDROID)
if (!file.IsValid()) {
return OpenResult(base::File(),
IOResult::FromOSError(logging::GetLastSystemErrorCode()));
}
return OpenResult(std::move(file), IOResult(OK, 0));
}
FileStream::Context::IOResult FileStream::Context::GetFileInfoImpl(
base::File::Info* file_info) {
bool result = file_.GetInfo(file_info);
if (!result)
return IOResult::FromOSError(logging::GetLastSystemErrorCode());
return IOResult(OK, 0);
}
FileStream::Context::IOResult FileStream::Context::CloseFileImpl() {
file_.Close();
return IOResult(OK, 0);
}
FileStream::Context::IOResult FileStream::Context::FlushFileImpl() {
if (file_.Flush())
return IOResult(OK, 0);
return IOResult::FromOSError(logging::GetLastSystemErrorCode());
}
void FileStream::Context::OnOpenCompleted(CompletionOnceCallback callback,
OpenResult open_result) {
file_ = std::move(open_result.file);
if (file_.IsValid() && !orphaned_)
OnFileOpened();
OnAsyncCompleted(IntToInt64(std::move(callback)), open_result.error_code);
}
void FileStream::Context::CloseAndDelete() {
DCHECK(!async_in_progress_);
if (file_.IsValid()) {
bool posted = task_runner_.get()->PostTask(
FROM_HERE, base::BindOnce(base::IgnoreResult(&Context::CloseFileImpl),
base::Owned(this)));
DCHECK(posted);
} else {
delete this;
}
}
Int64CompletionOnceCallback FileStream::Context::IntToInt64(
CompletionOnceCallback callback) {
return base::BindOnce(&CallInt64ToInt, std::move(callback));
}
void FileStream::Context::OnAsyncCompleted(Int64CompletionOnceCallback callback,
const IOResult& result) {
// Reset this before Run() as Run() may issue a new async operation. Also it
// should be reset before Close() because it shouldn't run if any async
// operation is in progress.
async_in_progress_ = false;
if (orphaned_) {
CloseAndDelete();
} else {
std::move(callback).Run(result.result);
}
}
} // namespace net

View File

@@ -0,0 +1,248 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file defines FileStream::Context class.
// The general design of FileStream is as follows: file_stream.h defines
// FileStream class which basically is just an "wrapper" not containing any
// specific implementation details. It re-routes all its method calls to
// the instance of FileStream::Context (FileStream holds a scoped_ptr to
// FileStream::Context instance). Context was extracted into a different class
// to be able to do and finish all async operations even when FileStream
// instance is deleted. So FileStream's destructor can schedule file
// closing to be done by Context in WorkerPool (or the TaskRunner passed to
// constructor) and then just return (releasing Context pointer from
// scoped_ptr) without waiting for actual closing to complete.
// Implementation of FileStream::Context is divided in two parts: some methods
// and members are platform-independent and some depend on the platform. This
// header file contains the complete definition of Context class including all
// platform-dependent parts (because of that it has a lot of #if-#else
// branching). Implementations of all platform-independent methods are
// located in file_stream_context.cc, and all platform-dependent methods are
// in file_stream_context_{win,posix}.cc. This separation provides better
// readability of Context's code. And we tried to make as much Context code
// platform-independent as possible. So file_stream_context_{win,posix}.cc are
// much smaller than file_stream_context.cc now.
#ifndef NET_BASE_FILE_STREAM_CONTEXT_H_
#define NET_BASE_FILE_STREAM_CONTEXT_H_
#include <stdint.h>
#include "base/files/file.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_pump_for_io.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/task_runner.h"
#include "build/build_config.h"
#include "net/base/completion_once_callback.h"
#include "net/base/file_stream.h"
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
#include <errno.h>
#endif
namespace base {
class FilePath;
}
namespace net {
class IOBuffer;
#if BUILDFLAG(IS_WIN)
class FileStream::Context : public base::MessagePumpForIO::IOHandler {
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
class FileStream::Context {
#endif
public:
////////////////////////////////////////////////////////////////////////////
// Platform-dependent methods implemented in
// file_stream_context_{win,posix}.cc.
////////////////////////////////////////////////////////////////////////////
explicit Context(scoped_refptr<base::TaskRunner> task_runner);
Context(base::File file, scoped_refptr<base::TaskRunner> task_runner);
Context(const Context&) = delete;
Context& operator=(const Context&) = delete;
#if BUILDFLAG(IS_WIN)
~Context() override;
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
~Context();
#endif
int Read(IOBuffer* buf, int buf_len, CompletionOnceCallback callback);
int Write(IOBuffer* buf, int buf_len, CompletionOnceCallback callback);
bool async_in_progress() const { return async_in_progress_; }
////////////////////////////////////////////////////////////////////////////
// Platform-independent methods implemented in file_stream_context.cc.
////////////////////////////////////////////////////////////////////////////
// Destroys the context. It can be deleted in the method or deletion can be
// deferred if some asynchronous operation is now in progress or if file is
// not closed yet.
void Orphan();
void Open(const base::FilePath& path,
int open_flags,
CompletionOnceCallback callback);
void Close(CompletionOnceCallback callback);
// Seeks |offset| bytes from the start of the file.
void Seek(int64_t offset, Int64CompletionOnceCallback callback);
void GetFileInfo(base::File::Info* file_info,
CompletionOnceCallback callback);
void Flush(CompletionOnceCallback callback);
bool IsOpen() const;
private:
struct IOResult {
IOResult();
IOResult(int64_t result, logging::SystemErrorCode os_error);
static IOResult FromOSError(logging::SystemErrorCode os_error);
int64_t result;
logging::SystemErrorCode os_error; // Set only when result < 0.
};
struct OpenResult {
public:
OpenResult();
OpenResult(base::File file, IOResult error_code);
OpenResult(OpenResult&& other);
OpenResult& operator=(OpenResult&& other);
OpenResult(const OpenResult&) = delete;
OpenResult& operator=(const OpenResult&) = delete;
base::File file;
IOResult error_code;
};
////////////////////////////////////////////////////////////////////////////
// Platform-independent methods implemented in file_stream_context.cc.
////////////////////////////////////////////////////////////////////////////
OpenResult OpenFileImpl(const base::FilePath& path, int open_flags);
IOResult GetFileInfoImpl(base::File::Info* file_info);
IOResult CloseFileImpl();
IOResult FlushFileImpl();
void OnOpenCompleted(CompletionOnceCallback callback, OpenResult open_result);
void CloseAndDelete();
Int64CompletionOnceCallback IntToInt64(CompletionOnceCallback callback);
// Called when Open() or Seek() completes. |result| contains the result or a
// network error code.
void OnAsyncCompleted(Int64CompletionOnceCallback callback,
const IOResult& result);
////////////////////////////////////////////////////////////////////////////
// Platform-dependent methods implemented in
// file_stream_context_{win,posix}.cc.
////////////////////////////////////////////////////////////////////////////
// Adjusts the position from where the data is read.
IOResult SeekFileImpl(int64_t offset);
void OnFileOpened();
#if BUILDFLAG(IS_WIN)
void IOCompletionIsPending(CompletionOnceCallback callback, IOBuffer* buf);
// Implementation of MessagePumpForIO::IOHandler.
void OnIOCompleted(base::MessagePumpForIO::IOContext* context,
DWORD bytes_read,
DWORD error) override;
// Invokes the user callback.
void InvokeUserCallback();
// Deletes an orphaned context.
void DeleteOrphanedContext();
// The ReadFile call on Windows can execute synchronously at times.
// http://support.microsoft.com/kb/156932. This ends up blocking the calling
// thread which is undesirable. To avoid this we execute the ReadFile call
// on a worker thread.
// The |context| parameter is a pointer to the current Context instance. It
// is safe to pass this as is to the pool as the Context instance should
// remain valid until the pending Read operation completes.
// The |file| parameter is the handle to the file being read.
// The |buf| parameter is the buffer where we want the ReadFile to read the
// data into.
// The |buf_len| parameter contains the number of bytes to be read.
// The |overlapped| parameter is a pointer to the OVERLAPPED structure being
// used.
// The |origin_thread_task_runner| is a task runner instance used to post
// tasks back to the originating thread.
static void ReadAsync(
FileStream::Context* context,
HANDLE file,
scoped_refptr<IOBuffer> buf,
int buf_len,
OVERLAPPED* overlapped,
scoped_refptr<base::SingleThreadTaskRunner> origin_thread_task_runner);
// This callback executes on the main calling thread. It informs the caller
// about the result of the ReadFile call.
// The |read_file_ret| parameter contains the return value of the ReadFile
// call.
// The |bytes_read| contains the number of bytes read from the file, if
// ReadFile succeeds.
// The |os_error| parameter contains the value of the last error returned by
// the ReadFile API.
void ReadAsyncResult(BOOL read_file_ret, DWORD bytes_read, DWORD os_error);
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
// ReadFileImpl() is a simple wrapper around read() that handles EINTR
// signals and calls RecordAndMapError() to map errno to net error codes.
IOResult ReadFileImpl(scoped_refptr<IOBuffer> buf, int buf_len);
// WriteFileImpl() is a simple wrapper around write() that handles EINTR
// signals and calls MapSystemError() to map errno to net error codes.
// It tries to write to completion.
IOResult WriteFileImpl(scoped_refptr<IOBuffer> buf, int buf_len);
#endif // BUILDFLAG(IS_WIN)
base::File file_;
bool async_in_progress_ = false;
bool orphaned_ = false;
const scoped_refptr<base::TaskRunner> task_runner_;
#if BUILDFLAG(IS_WIN)
base::MessagePumpForIO::IOContext io_context_;
CompletionOnceCallback callback_;
scoped_refptr<IOBuffer> in_flight_buf_;
// This flag is set to true when we receive a Read request which is queued to
// the thread pool.
bool async_read_initiated_ = false;
// This flag is set to true when we receive a notification ReadAsyncResult()
// on the calling thread which indicates that the asynchronous Read
// operation is complete.
bool async_read_completed_ = false;
// This flag is set to true when we receive an IO completion notification for
// an asynchronously initiated Read operation. OnIOComplete().
bool io_complete_for_read_received_ = false;
// Tracks the result of the IO completion operation. Set in OnIOComplete.
int result_ = 0;
#endif
};
} // namespace net
#endif // NET_BASE_FILE_STREAM_CONTEXT_H_

View File

@@ -0,0 +1,100 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/file_stream_context.h"
#include <errno.h>
#include <utility>
#include "base/check.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/posix/eintr_wrapper.h"
#include "base/task/task_runner.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
namespace net {
FileStream::Context::Context(scoped_refptr<base::TaskRunner> task_runner)
: Context(base::File(), std::move(task_runner)) {}
FileStream::Context::Context(base::File file,
scoped_refptr<base::TaskRunner> task_runner)
: file_(std::move(file)), task_runner_(std::move(task_runner)) {}
FileStream::Context::~Context() = default;
int FileStream::Context::Read(IOBuffer* in_buf,
int buf_len,
CompletionOnceCallback callback) {
DCHECK(!async_in_progress_);
scoped_refptr<IOBuffer> buf = in_buf;
const bool posted = task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&Context::ReadFileImpl, base::Unretained(this), buf,
buf_len),
base::BindOnce(&Context::OnAsyncCompleted, base::Unretained(this),
IntToInt64(std::move(callback))));
DCHECK(posted);
async_in_progress_ = true;
return ERR_IO_PENDING;
}
int FileStream::Context::Write(IOBuffer* in_buf,
int buf_len,
CompletionOnceCallback callback) {
DCHECK(!async_in_progress_);
scoped_refptr<IOBuffer> buf = in_buf;
const bool posted = task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&Context::WriteFileImpl, base::Unretained(this), buf,
buf_len),
base::BindOnce(&Context::OnAsyncCompleted, base::Unretained(this),
IntToInt64(std::move(callback))));
DCHECK(posted);
async_in_progress_ = true;
return ERR_IO_PENDING;
}
FileStream::Context::IOResult FileStream::Context::SeekFileImpl(
int64_t offset) {
int64_t res = file_.Seek(base::File::FROM_BEGIN, offset);
if (res == -1)
return IOResult::FromOSError(errno);
return IOResult(res, 0);
}
void FileStream::Context::OnFileOpened() {
}
FileStream::Context::IOResult FileStream::Context::ReadFileImpl(
scoped_refptr<IOBuffer> buf,
int buf_len) {
int res = file_.ReadAtCurrentPosNoBestEffort(buf->data(), buf_len);
if (res == -1)
return IOResult::FromOSError(errno);
return IOResult(res, 0);
}
FileStream::Context::IOResult FileStream::Context::WriteFileImpl(
scoped_refptr<IOBuffer> buf,
int buf_len) {
int res = file_.WriteAtCurrentPosNoBestEffort(buf->data(), buf_len);
if (res == -1)
return IOResult::FromOSError(errno);
return IOResult(res, 0);
}
} // namespace net

View File

@@ -0,0 +1,229 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/file_stream_context.h"
#include <windows.h>
#include <utility>
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_pump_for_io.h"
#include "base/task/current_thread.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/task_runner.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
namespace net {
namespace {
void SetOffset(OVERLAPPED* overlapped, const LARGE_INTEGER& offset) {
overlapped->Offset = offset.LowPart;
overlapped->OffsetHigh = offset.HighPart;
}
void IncrementOffset(OVERLAPPED* overlapped, DWORD count) {
LARGE_INTEGER offset;
offset.LowPart = overlapped->Offset;
offset.HighPart = overlapped->OffsetHigh;
offset.QuadPart += static_cast<LONGLONG>(count);
SetOffset(overlapped, offset);
}
} // namespace
FileStream::Context::Context(scoped_refptr<base::TaskRunner> task_runner)
: Context(base::File(), std::move(task_runner)) {}
FileStream::Context::Context(base::File file,
scoped_refptr<base::TaskRunner> task_runner)
: base::MessagePumpForIO::IOHandler(FROM_HERE),
file_(std::move(file)),
task_runner_(std::move(task_runner)) {
if (file_.IsValid()) {
DCHECK(file_.async());
OnFileOpened();
}
}
FileStream::Context::~Context() = default;
int FileStream::Context::Read(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) {
DCHECK(!async_in_progress_);
DCHECK(!async_read_initiated_);
DCHECK(!async_read_completed_);
DCHECK(!io_complete_for_read_received_);
IOCompletionIsPending(std::move(callback), buf);
async_read_initiated_ = true;
result_ = 0;
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&FileStream::Context::ReadAsync, base::Unretained(this),
file_.GetPlatformFile(), base::WrapRefCounted(buf),
buf_len, &io_context_.overlapped,
base::SingleThreadTaskRunner::GetCurrentDefault()));
return ERR_IO_PENDING;
}
int FileStream::Context::Write(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) {
DCHECK(!async_in_progress_);
result_ = 0;
DWORD bytes_written = 0;
if (!WriteFile(file_.GetPlatformFile(), buf->data(), buf_len,
&bytes_written, &io_context_.overlapped)) {
IOResult error = IOResult::FromOSError(GetLastError());
if (error.os_error == ERROR_IO_PENDING) {
IOCompletionIsPending(std::move(callback), buf);
} else {
LOG(WARNING) << "WriteFile failed: " << error.os_error;
}
return static_cast<int>(error.result);
}
IOCompletionIsPending(std::move(callback), buf);
return ERR_IO_PENDING;
}
FileStream::Context::IOResult FileStream::Context::SeekFileImpl(
int64_t offset) {
LARGE_INTEGER result;
result.QuadPart = offset;
SetOffset(&io_context_.overlapped, result);
return IOResult(result.QuadPart, 0);
}
void FileStream::Context::OnFileOpened() {
HRESULT hr = base::CurrentIOThread::Get()->RegisterIOHandler(
file_.GetPlatformFile(), this);
if (!SUCCEEDED(hr))
file_.Close();
}
void FileStream::Context::IOCompletionIsPending(CompletionOnceCallback callback,
IOBuffer* buf) {
DCHECK(callback_.is_null());
callback_ = std::move(callback);
in_flight_buf_ = buf; // Hold until the async operation ends.
async_in_progress_ = true;
}
void FileStream::Context::OnIOCompleted(
base::MessagePumpForIO::IOContext* context,
DWORD bytes_read,
DWORD error) {
DCHECK_EQ(&io_context_, context);
DCHECK(!callback_.is_null());
DCHECK(async_in_progress_);
if (!async_read_initiated_)
async_in_progress_ = false;
if (orphaned_) {
io_complete_for_read_received_ = true;
// If we are called due to a pending read and the asynchronous read task
// has not completed we have to keep the context around until it completes.
if (async_read_initiated_ && !async_read_completed_)
return;
DeleteOrphanedContext();
return;
}
if (error == ERROR_HANDLE_EOF) {
result_ = 0;
} else if (error) {
IOResult error_result = IOResult::FromOSError(error);
result_ = static_cast<int>(error_result.result);
} else {
if (result_)
DCHECK_EQ(result_, static_cast<int>(bytes_read));
result_ = bytes_read;
IncrementOffset(&io_context_.overlapped, bytes_read);
}
if (async_read_initiated_)
io_complete_for_read_received_ = true;
InvokeUserCallback();
}
void FileStream::Context::InvokeUserCallback() {
// For an asynchonous Read operation don't invoke the user callback until
// we receive the IO completion notification and the asynchronous Read
// completion notification.
if (async_read_initiated_) {
if (!io_complete_for_read_received_ || !async_read_completed_)
return;
async_read_initiated_ = false;
io_complete_for_read_received_ = false;
async_read_completed_ = false;
async_in_progress_ = false;
}
scoped_refptr<IOBuffer> temp_buf = in_flight_buf_;
in_flight_buf_ = nullptr;
std::move(callback_).Run(result_);
}
void FileStream::Context::DeleteOrphanedContext() {
async_in_progress_ = false;
callback_.Reset();
in_flight_buf_ = nullptr;
CloseAndDelete();
}
// static
void FileStream::Context::ReadAsync(
FileStream::Context* context,
HANDLE file,
scoped_refptr<IOBuffer> buf,
int buf_len,
OVERLAPPED* overlapped,
scoped_refptr<base::SingleThreadTaskRunner> origin_thread_task_runner) {
DWORD bytes_read = 0;
BOOL ret = ::ReadFile(file, buf->data(), buf_len, &bytes_read, overlapped);
origin_thread_task_runner->PostTask(
FROM_HERE, base::BindOnce(&FileStream::Context::ReadAsyncResult,
base::Unretained(context), ret, bytes_read,
::GetLastError()));
}
void FileStream::Context::ReadAsyncResult(BOOL read_file_ret,
DWORD bytes_read,
DWORD os_error) {
// If the context is orphaned and we already received the io completion
// notification then we should delete the context and get out.
if (orphaned_ && io_complete_for_read_received_) {
DeleteOrphanedContext();
return;
}
async_read_completed_ = true;
if (read_file_ret) {
result_ = bytes_read;
InvokeUserCallback();
return;
}
IOResult error = IOResult::FromOSError(os_error);
if (error.os_error == ERROR_IO_PENDING) {
InvokeUserCallback();
} else {
OnIOCompleted(&io_context_, 0, error.os_error);
}
}
} // namespace net

View File

@@ -0,0 +1,223 @@
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/filename_util.h"
#include <set>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/strings/escape.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "net/base/filename_util_internal.h"
#include "net/base/net_string_util.h"
#include "net/base/url_util.h"
#include "net/http/http_content_disposition.h"
#include "url/gurl.h"
namespace net {
// Prefix to prepend to get a file URL.
static const char kFileURLPrefix[] = "file:///";
GURL FilePathToFileURL(const base::FilePath& path) {
// Produce a URL like "file:///C:/foo" for a regular file, or
// "file://///server/path" for UNC. The URL canonicalizer will fix up the
// latter case to be the canonical UNC form: "file://server/path"
std::string url_string(kFileURLPrefix);
// GURL() strips some whitespace and trailing control chars which are valid
// in file paths. It also interprets chars such as `%;#?` and maybe `\`, so we
// must percent encode these first. Reserve max possible length up front.
std::string utf8_path = path.AsUTF8Unsafe();
url_string.reserve(url_string.size() + (3 * utf8_path.size()));
for (auto c : utf8_path) {
if (c == '%' || c == ';' || c == '#' || c == '?' ||
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
c == '\\' ||
#endif
c <= ' ') {
url_string += '%';
base::AppendHexEncodedByte(static_cast<uint8_t>(c), url_string);
} else {
url_string += c;
}
}
return GURL(url_string);
}
bool FileURLToFilePath(const GURL& url, base::FilePath* file_path) {
*file_path = base::FilePath();
base::FilePath::StringType& file_path_str =
const_cast<base::FilePath::StringType&>(file_path->value());
file_path_str.clear();
if (!url.is_valid())
return false;
// We may want to change this to a CHECK in the future.
if (!url.SchemeIsFile())
return false;
#if BUILDFLAG(IS_WIN)
std::string path;
std::string host = url.host();
if (host.empty()) {
// URL contains no host, the path is the filename. In this case, the path
// will probably be preceded with a slash, as in "/C:/foo.txt", so we
// trim out that here.
path = url.path();
size_t first_non_slash = path.find_first_not_of("/\\");
if (first_non_slash != std::string::npos && first_non_slash > 0)
path.erase(0, first_non_slash);
} else {
// URL contains a host: this means it's UNC. We keep the preceding slash
// on the path.
path = "\\\\";
path.append(host);
path.append(url.path());
}
std::replace(path.begin(), path.end(), '/', '\\');
#else // BUILDFLAG(IS_WIN)
// On POSIX, there's no obvious interpretation of file:// URLs with a host.
// Usually, remote mounts are still mounted onto the local filesystem.
// Therefore, we discard all URLs that are not obviously local to prevent
// spoofing attacks using file:// URLs. See crbug.com/881675.
if (!url.host().empty() && !net::IsLocalhost(url)) {
return false;
}
std::string path = url.path();
#endif // !BUILDFLAG(IS_WIN)
if (path.empty())
return false;
// "%2F" ('/') results in failure, because it represents a literal '/'
// character in a path segment (not a path separator). If this were decoded,
// it would be interpreted as a path separator on both POSIX and Windows (note
// that Firefox *does* decode this, but it was decided on
// https://crbug.com/585422 that this represents a potential security risk).
// It isn't correct to keep it as "%2F", so this just fails. This is fine,
// because '/' is not a valid filename character on either POSIX or Windows.
//
// A valid URL may include "%00" (NULL) in its path (see
// https://crbug.com/1400251), which is considered an illegal filename and
// results in failure.
std::set<unsigned char> illegal_encoded_bytes{'/', '\0'};
#if BUILDFLAG(IS_WIN)
// "%5C" ('\\') on Windows results in failure, for the same reason as '/'
// above. On POSIX, "%5C" simply decodes as '\\', a valid filename character.
illegal_encoded_bytes.insert('\\');
#endif
if (base::ContainsEncodedBytes(path, illegal_encoded_bytes))
return false;
// Unescape all percent-encoded sequences, including blocked-for-display
// characters, control characters and invalid UTF-8 byte sequences.
// Percent-encoded bytes are not meaningful in a file system.
path = base::UnescapeBinaryURLComponent(path);
#if BUILDFLAG(IS_WIN)
if (base::IsStringUTF8(path)) {
file_path_str.assign(base::UTF8ToWide(path));
// We used to try too hard and see if |path| made up entirely of
// the 1st 256 characters in the Unicode was a zero-extended UTF-16.
// If so, we converted it to 'Latin-1' and checked if the result was UTF-8.
// If the check passed, we converted the result to UTF-8.
// Otherwise, we treated the result as the native OS encoding.
// However, that led to http://crbug.com/4619 and http://crbug.com/14153
} else {
// Not UTF-8, assume encoding is native codepage and we're done. We know we
// are giving the conversion function a nonempty string, and it may fail if
// the given string is not in the current encoding and give us an empty
// string back. We detect this and report failure.
file_path_str = base::SysNativeMBToWide(path);
}
#else // BUILDFLAG(IS_WIN)
// Collapse multiple path slashes into a single path slash.
std::string new_path;
do {
new_path = path;
base::ReplaceSubstringsAfterOffset(&new_path, 0, "//", "/");
path.swap(new_path);
} while (new_path != path);
file_path_str.assign(path);
#endif // !BUILDFLAG(IS_WIN)
return !file_path_str.empty();
}
void GenerateSafeFileName(const std::string& mime_type,
bool ignore_extension,
base::FilePath* file_path) {
// Make sure we get the right file extension
EnsureSafeExtension(mime_type, ignore_extension, file_path);
#if BUILDFLAG(IS_WIN)
// Prepend "_" to the file name if it's a reserved name
base::FilePath::StringType leaf_name = file_path->BaseName().value();
DCHECK(!leaf_name.empty());
if (IsReservedNameOnWindows(leaf_name)) {
leaf_name = base::FilePath::StringType(FILE_PATH_LITERAL("_")) + leaf_name;
*file_path = file_path->DirName();
if (file_path->value() == base::FilePath::kCurrentDirectory) {
*file_path = base::FilePath(leaf_name);
} else {
*file_path = file_path->Append(leaf_name);
}
}
#endif
}
bool IsReservedNameOnWindows(const base::FilePath::StringType& filename) {
// This list is taken from the MSDN article "Naming a file"
// http://msdn2.microsoft.com/en-us/library/aa365247(VS.85).aspx
// I also added clock$ because GetSaveFileName seems to consider it as a
// reserved name too.
static const char* const known_devices[] = {
"con", "prn", "aux", "nul", "com1", "com2", "com3", "com4",
"com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3",
"lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "clock$"};
#if BUILDFLAG(IS_WIN)
std::string filename_lower = base::ToLowerASCII(base::WideToUTF8(filename));
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
std::string filename_lower = base::ToLowerASCII(filename);
#endif
for (const char* const device : known_devices) {
// Check for an exact match, or a "DEVICE." prefix.
size_t len = strlen(device);
if (filename_lower.starts_with(device) &&
(filename_lower.size() == len || filename_lower[len] == '.')) {
return true;
}
}
static const char* const magic_names[] = {
// These file names are used by the "Customize folder" feature of the
// shell.
"desktop.ini",
"thumbs.db",
};
for (const char* const magic_name : magic_names) {
if (filename_lower == magic_name)
return true;
}
return false;
}
} // namespace net

View File

@@ -0,0 +1,145 @@
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_FILENAME_UTIL_H_
#define NET_BASE_FILENAME_UTIL_H_
#include <string>
#include "base/files/file_path.h"
#include "net/base/net_export.h"
class GURL;
namespace base {
class FilePath;
}
namespace net {
// Given the full path to a file name, creates a file: URL. The returned URL
// may not be valid if the input is malformed.
NET_EXPORT GURL FilePathToFileURL(const base::FilePath& path);
// Converts a file: URL back to a filename that can be passed to the OS. The
// file URL must be well-formed (GURL::is_valid() must return true); we don't
// handle degenerate cases here. Returns true on success, false if |url| is
// invalid or the file path cannot be extracted from |url|.
// On failure, *file_path will be empty.
//
// Do not call this with a |url| that doesn't have a file:// scheme.
// The implementation is specific to the platform filesystem, and not
// applicable to other schemes.
NET_EXPORT bool FileURLToFilePath(const GURL& url, base::FilePath* file_path);
// Generates a filename using the first successful method from the following (in
// order):
//
// 1) The raw Content-Disposition header in |content_disposition| as read from
// the network. |referrer_charset| is used to decode non-ASCII strings.
// 2) |suggested_name| if specified. |suggested_name| is assumed to be in
// UTF-8.
// 3) The filename extracted from the |url|. |referrer_charset| will be used to
// interpret the URL if there are non-ascii characters.The file extension for
// filenames extracted from the URL are considered unreliable if the URL
// contains a query string. If a MIME type is available (i.e. |mime_type| is
// not empty) and that MIME type has a preferred extension, then the
// resulting filename will have that preferred extension.
// 4) |default_name|. If non-empty, |default_name| is assumed to be a filename
// and shouldn't contain a path. |default_name| is not subject to validation
// or sanitization, and therefore shouldn't be a user supplied string.
// 5) The hostname portion from the |url|
//
// Then, leading and trailing '.'s will be removed. On Windows, trailing spaces
// are also removed. The string "download" is the final fallback if no filename
// is found or the filename is empty.
//
// Any illegal characters in the filename will be replaced by '-'. If the
// filename doesn't contain an extension, and a |mime_type| is specified, the
// preferred extension for the |mime_type| will be appended to the filename.
// The resulting filename is then checked against a list of reserved names on
// Windows. If the name is reserved, an underscore will be prepended to the
// filename.
//
// Note: |mime_type| should only be specified if this function is called from a
// thread that allows IO.
NET_EXPORT std::u16string GetSuggestedFilename(
const GURL& url,
const std::string& content_disposition,
const std::string& referrer_charset,
const std::string& suggested_name,
const std::string& mime_type,
const std::string& default_name);
// Similar to GetSuggestedFilename(), but returns a FilePath.
NET_EXPORT base::FilePath GenerateFileName(
const GURL& url,
const std::string& content_disposition,
const std::string& referrer_charset,
const std::string& suggested_name,
const std::string& mime_type,
const std::string& default_name);
// Similar to GetSuggestedFilename(). If |should_replace_extension| is true, the
// file extension extracted from a URL will always be considered unreliable and
// the file extension will be determined by |mime_type|.
NET_EXPORT base::FilePath GenerateFileName(
const GURL& url,
const std::string& content_disposition,
const std::string& referrer_charset,
const std::string& suggested_name,
const std::string& mime_type,
const std::string& default_name,
bool should_replace_extension);
// Valid components:
// * are not empty
// * are not Windows reserved names (CON, NUL.zip, etc.)
// * do not have trailing separators
// * do not equal kCurrentDirectory
// * do not reference the parent directory
// * do not contain illegal characters
// * do not end with Windows shell-integrated extensions (even on posix)
// * do not begin with '.' (which would hide them in most file managers)
// * do not end with ' ' or '.'
NET_EXPORT bool IsSafePortablePathComponent(const base::FilePath& component);
// Basenames of valid relative paths are IsSafePortableBasename(), and internal
// path components of valid relative paths are valid path components as
// described above IsSafePortableBasename(). Valid relative paths are not
// absolute paths.
NET_EXPORT bool IsSafePortableRelativePath(const base::FilePath& path);
// Ensures that the filename and extension is safe to use in the filesystem.
//
// Assumes that |file_path| already contains a valid path or file name. On
// Windows if the extension causes the file to have an unsafe interaction with
// the shell (see net_util::IsShellIntegratedExtension()), then it will be
// replaced by the string 'download'. If |file_path| doesn't contain an
// extension or |ignore_extension| is true then the preferred extension, if one
// exists, for |mime_type| will be used as the extension.
//
// On Windows, the filename will be checked against a set of reserved names, and
// if so, an underscore will be prepended to the name.
//
// |file_name| can either be just the file name or it can be a full path to a
// file.
//
// Note: |mime_type| should only be non-empty if this function is called from a
// thread that allows IO.
NET_EXPORT void GenerateSafeFileName(const std::string& mime_type,
bool ignore_extension,
base::FilePath* file_path);
// Returns whether the specified file name is a reserved name on Windows.
// This includes names like "com2.zip" (which correspond to devices) and
// desktop.ini and thumbs.db which have special meaning to the Windows shell.
// Even on other platforms, this will return whether or not a file name is
// reserved on Windows.
NET_EXPORT bool IsReservedNameOnWindows(
const base::FilePath::StringType& filename);
} // namespace net
#endif // NET_BASE_FILENAME_UTIL_H_

View File

@@ -0,0 +1,96 @@
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/filename_util.h"
#include <string>
#include "base/check.h"
#include "base/files/file_path.h"
#include "base/i18n/file_util_icu.h"
#include "build/chromeos_buildflags.h"
#include "net/base/filename_util_internal.h"
class GURL;
namespace net {
bool IsSafePortablePathComponent(const base::FilePath& component) {
std::u16string component16;
base::FilePath::StringType sanitized = component.value();
SanitizeGeneratedFileName(&sanitized, true);
base::FilePath::StringType extension = component.Extension();
if (!extension.empty())
extension.erase(extension.begin()); // Erase preceding '.'.
return !component.empty() && (component == component.BaseName()) &&
(component == component.StripTrailingSeparators()) &&
FilePathToString16(component, &component16) &&
base::i18n::IsFilenameLegal(component16) &&
!IsShellIntegratedExtension(extension) &&
(sanitized == component.value()) &&
!IsReservedNameOnWindows(component.value());
}
bool IsSafePortableRelativePath(const base::FilePath& path) {
if (path.empty() || path.IsAbsolute() || path.EndsWithSeparator())
return false;
std::vector<base::FilePath::StringType> components = path.GetComponents();
if (components.empty())
return false;
for (size_t i = 0; i < components.size() - 1; ++i) {
if (!IsSafePortablePathComponent(base::FilePath(components[i])))
return false;
}
return IsSafePortablePathComponent(path.BaseName());
}
std::u16string GetSuggestedFilename(const GURL& url,
const std::string& content_disposition,
const std::string& referrer_charset,
const std::string& suggested_name,
const std::string& mime_type,
const std::string& default_name) {
return GetSuggestedFilenameImpl(url, content_disposition, referrer_charset,
suggested_name, mime_type, default_name,
false, /* should_replace_extension */
&base::i18n::ReplaceIllegalCharactersInPath);
}
base::FilePath GenerateFileName(const GURL& url,
const std::string& content_disposition,
const std::string& referrer_charset,
const std::string& suggested_name,
const std::string& mime_type,
const std::string& default_file_name) {
return GenerateFileName(url, content_disposition, referrer_charset,
suggested_name, mime_type, default_file_name,
false /* should_replace_extension */);
}
base::FilePath GenerateFileName(const GURL& url,
const std::string& content_disposition,
const std::string& referrer_charset,
const std::string& suggested_name,
const std::string& mime_type,
const std::string& default_file_name,
bool should_replace_extension) {
base::FilePath generated_name(GenerateFileNameImpl(
url, content_disposition, referrer_charset, suggested_name, mime_type,
default_file_name, should_replace_extension,
&base::i18n::ReplaceIllegalCharactersInPath));
#if BUILDFLAG(IS_CHROMEOS_ASH)
// When doing file manager operations on ChromeOS, the file paths get
// normalized in WebKit layer, so let's ensure downloaded files have
// normalized names. Otherwise, we won't be able to handle files with NFD
// utf8 encoded characters in name.
base::i18n::NormalizeFileNameEncoding(&generated_name);
#endif
DCHECK(!generated_name.empty());
return generated_name;
}
} // namespace net

View File

@@ -0,0 +1,333 @@
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/filename_util_internal.h"
#include "base/containers/contains.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/strings/escape.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "net/base/filename_util.h"
#include "net/base/mime_util.h"
#include "net/base/net_string_util.h"
#include "net/http/http_content_disposition.h"
#include "url/gurl.h"
namespace net {
namespace {
// Examines the current extension in |file_name| and tries to return the correct
// extension the file should actually be using. Used by EnsureSafeExtension.
// All other code should use EnsureSafeExtension, as it includes additional
// safety checks.
base::FilePath::StringType GetCorrectedExtensionUnsafe(
const std::string& mime_type,
bool ignore_extension,
const base::FilePath& file_name) {
// See if the file name already contains an extension.
base::FilePath::StringType extension = file_name.Extension();
if (!extension.empty())
extension.erase(extension.begin()); // Erase preceding '.'.
// Nothing to do if there's no mime type.
if (mime_type.empty())
return extension;
// Nothing to do there's an extension, unless |ignore_extension| is true.
if (!extension.empty() && !ignore_extension)
return extension;
// Don't do anything if there's not a preferred extension for the mime
// type.
base::FilePath::StringType preferred_mime_extension;
if (!GetPreferredExtensionForMimeType(mime_type, &preferred_mime_extension))
return extension;
// If the existing extension is in the list of valid extensions for the
// given type, use it. This avoids doing things like pointlessly renaming
// "foo.jpg" to "foo.jpeg".
std::vector<base::FilePath::StringType> all_mime_extensions;
GetExtensionsForMimeType(mime_type, &all_mime_extensions);
if (base::Contains(all_mime_extensions, extension))
return extension;
// Get the "final" extension. In most cases, this is the same as the
// |extension|, but in cases like "foo.tar.gz", it's "gz" while
// |extension| is "tar.gz".
base::FilePath::StringType final_extension = file_name.FinalExtension();
// Erase preceding '.'.
if (!final_extension.empty())
final_extension.erase(final_extension.begin());
// If there's a double extension, and the second extension is in the
// list of valid extensions for the given type, keep the double extension.
// This avoids renaming things like "foo.tar.gz" to "foo.gz".
if (base::Contains(all_mime_extensions, final_extension))
return extension;
return preferred_mime_extension;
}
} // namespace
void SanitizeGeneratedFileName(base::FilePath::StringType* filename,
bool replace_trailing) {
const base::FilePath::CharType kReplace[] = FILE_PATH_LITERAL("_");
if (filename->empty())
return;
if (replace_trailing) {
// Handle CreateFile() stripping trailing dots and spaces on filenames
// http://support.microsoft.com/kb/115827
size_t length = filename->size();
size_t pos = filename->find_last_not_of(FILE_PATH_LITERAL(" ."));
filename->resize((pos == std::string::npos) ? 0 : (pos + 1));
#if BUILDFLAG(IS_WIN)
base::TrimWhitespace(*filename, base::TRIM_TRAILING, filename);
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
base::TrimWhitespaceASCII(*filename, base::TRIM_TRAILING, filename);
#else
#error Unsupported platform
#endif
if (filename->empty())
return;
size_t trimmed = length - filename->size();
if (trimmed)
filename->insert(filename->end(), trimmed, kReplace[0]);
}
base::TrimString(*filename, FILE_PATH_LITERAL("."), filename);
if (filename->empty())
return;
// Replace any path information by changing path separators.
base::ReplaceSubstringsAfterOffset(
filename, 0, FILE_PATH_LITERAL("/"), kReplace);
base::ReplaceSubstringsAfterOffset(
filename, 0, FILE_PATH_LITERAL("\\"), kReplace);
}
// Returns the filename determined from the last component of the path portion
// of the URL. Returns an empty string if the URL doesn't have a path or is
// invalid. If the generated filename is not reliable,
// |should_overwrite_extension| will be set to true, in which case a better
// extension should be determined based on the content type.
std::string GetFileNameFromURL(const GURL& url,
const std::string& referrer_charset,
bool* should_overwrite_extension) {
// about: and data: URLs don't have file names, but esp. data: URLs may
// contain parts that look like ones (i.e., contain a slash). Therefore we
// don't attempt to divine a file name out of them.
if (!url.is_valid() || url.SchemeIs("about") || url.SchemeIs("data"))
return std::string();
std::string unescaped_url_filename = base::UnescapeBinaryURLComponent(
url.ExtractFileName(), base::UnescapeRule::NORMAL);
// The URL's path should be escaped UTF-8, but may not be.
std::string decoded_filename = unescaped_url_filename;
if (!base::IsStringUTF8(decoded_filename)) {
// TODO(jshin): this is probably not robust enough. To be sure, we need
// encoding detection.
std::u16string utf16_output;
if (!referrer_charset.empty() &&
ConvertToUTF16(unescaped_url_filename, referrer_charset.c_str(),
&utf16_output)) {
decoded_filename = base::UTF16ToUTF8(utf16_output);
} else {
decoded_filename =
base::WideToUTF8(base::SysNativeMBToWide(unescaped_url_filename));
}
}
// If the URL contains a (possibly empty) query, assume it is a generator, and
// allow the determined extension to be overwritten.
*should_overwrite_extension = !decoded_filename.empty() && url.has_query();
return decoded_filename;
}
// Returns whether the specified extension is automatically integrated into the
// windows shell.
bool IsShellIntegratedExtension(const base::FilePath::StringType& extension) {
base::FilePath::StringType extension_lower = base::ToLowerASCII(extension);
// .lnk files may be used to execute arbitrary code (see
// https://nvd.nist.gov/vuln/detail/CVE-2010-2568). .local files are used by
// Windows to determine which DLLs to load for an application.
if ((extension_lower == FILE_PATH_LITERAL("local")) ||
(extension_lower == FILE_PATH_LITERAL("lnk")))
return true;
// Setting a file's extension to a CLSID may conceal its actual file type on
// some Windows versions (see https://nvd.nist.gov/vuln/detail/CVE-2004-0420).
if (!extension_lower.empty() &&
(extension_lower.front() == FILE_PATH_LITERAL('{')) &&
(extension_lower.back() == FILE_PATH_LITERAL('}')))
return true;
return false;
}
// Examines the current extension in |file_name| and modifies it if necessary in
// order to ensure the filename is safe. If |file_name| doesn't contain an
// extension or if |ignore_extension| is true, then a new extension will be
// constructed based on the |mime_type|.
//
// We're addressing two things here:
//
// 1) Usability. If there is no reliable file extension, we want to guess a
// reasonable file extension based on the content type.
//
// 2) Shell integration. Some file extensions automatically integrate with the
// shell. We block these extensions to prevent a malicious web site from
// integrating with the user's shell.
void EnsureSafeExtension(const std::string& mime_type,
bool ignore_extension,
base::FilePath* file_name) {
DCHECK(file_name);
base::FilePath::StringType extension =
GetCorrectedExtensionUnsafe(mime_type, ignore_extension, *file_name);
#if BUILDFLAG(IS_WIN)
const base::FilePath::CharType kDefaultExtension[] =
FILE_PATH_LITERAL("download");
// Rename shell-integrated extensions.
// TODO(asanka): Consider stripping out the bad extension and replacing it
// with the preferred extension for the MIME type if one is available.
if (IsShellIntegratedExtension(extension))
extension = kDefaultExtension;
#endif
*file_name = file_name->ReplaceExtension(extension);
}
bool FilePathToString16(const base::FilePath& path, std::u16string* converted) {
#if BUILDFLAG(IS_WIN)
converted->assign(path.value().begin(), path.value().end());
return true;
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
std::string component8 = path.AsUTF8Unsafe();
return !component8.empty() &&
base::UTF8ToUTF16(component8.c_str(), component8.size(), converted);
#endif
}
std::u16string GetSuggestedFilenameImpl(
const GURL& url,
const std::string& content_disposition,
const std::string& referrer_charset,
const std::string& suggested_name,
const std::string& mime_type,
const std::string& default_name,
bool should_replace_extension,
ReplaceIllegalCharactersFunction replace_illegal_characters_function) {
// TODO: this function to be updated to match the httpbis recommendations.
// Talk to abarth for the latest news.
// We don't translate this fallback string, "download". If localization is
// needed, the caller should provide localized fallback in |default_name|.
static const base::FilePath::CharType kFinalFallbackName[] =
FILE_PATH_LITERAL("download");
std::string filename; // In UTF-8
bool overwrite_extension = false;
bool is_name_from_content_disposition = false;
// Try to extract a filename from content-disposition first.
if (!content_disposition.empty()) {
HttpContentDisposition header(content_disposition, referrer_charset);
filename = header.filename();
if (!filename.empty())
is_name_from_content_disposition = true;
}
// Then try to use the suggested name.
if (filename.empty() && !suggested_name.empty())
filename = suggested_name;
// Now try extracting the filename from the URL. GetFileNameFromURL() only
// looks at the last component of the URL and doesn't return the hostname as a
// failover.
if (filename.empty())
filename = GetFileNameFromURL(url, referrer_charset, &overwrite_extension);
// Finally try the URL hostname, but only if there's no default specified in
// |default_name|. Some schemes (e.g.: file:, about:, data:) do not have a
// host name.
if (filename.empty() && default_name.empty() && url.is_valid() &&
!url.host().empty()) {
// TODO(jungshik) : Decode a 'punycoded' IDN hostname. (bug 1264451)
filename = url.host();
}
bool replace_trailing = false;
base::FilePath::StringType result_str, default_name_str;
#if BUILDFLAG(IS_WIN)
replace_trailing = true;
result_str = base::UTF8ToWide(filename);
default_name_str = base::UTF8ToWide(default_name);
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
result_str = filename;
default_name_str = default_name;
#else
#error Unsupported platform
#endif
SanitizeGeneratedFileName(&result_str, replace_trailing);
if (result_str.find_last_not_of(FILE_PATH_LITERAL("-_")) ==
base::FilePath::StringType::npos) {
result_str = !default_name_str.empty()
? default_name_str
: base::FilePath::StringType(kFinalFallbackName);
overwrite_extension = false;
}
replace_illegal_characters_function(&result_str, '_');
base::FilePath result(result_str);
overwrite_extension |= should_replace_extension;
// extension should not appended to filename derived from
// content-disposition, if it does not have one.
// Hence mimetype and overwrite_extension values are not used.
if (is_name_from_content_disposition)
GenerateSafeFileName("", false, &result);
else
GenerateSafeFileName(mime_type, overwrite_extension, &result);
std::u16string result16;
if (!FilePathToString16(result, &result16)) {
result = base::FilePath(default_name_str);
if (!FilePathToString16(result, &result16)) {
result = base::FilePath(kFinalFallbackName);
FilePathToString16(result, &result16);
}
}
return result16;
}
base::FilePath GenerateFileNameImpl(
const GURL& url,
const std::string& content_disposition,
const std::string& referrer_charset,
const std::string& suggested_name,
const std::string& mime_type,
const std::string& default_file_name,
bool should_replace_extension,
ReplaceIllegalCharactersFunction replace_illegal_characters_function) {
std::u16string file_name = GetSuggestedFilenameImpl(
url, content_disposition, referrer_charset, suggested_name, mime_type,
default_file_name, should_replace_extension,
replace_illegal_characters_function);
#if BUILDFLAG(IS_WIN)
base::FilePath generated_name(base::AsWStringView(file_name));
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
base::FilePath generated_name(
base::SysWideToNativeMB(base::UTF16ToWide(file_name)));
#endif
DCHECK(!generated_name.empty());
return generated_name;
}
} // namespace net

View File

@@ -0,0 +1,62 @@
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Functions used internally by filename_util, and filename_util_icu.
#ifndef NET_BASE_FILENAME_UTIL_INTERNAL_H_
#define NET_BASE_FILENAME_UTIL_INTERNAL_H_
#include <string>
#include "base/files/file_path.h"
class GURL;
namespace net {
using ReplaceIllegalCharactersFunction =
void (*)(base::FilePath::StringType* file_name, char replace_char);
void SanitizeGeneratedFileName(base::FilePath::StringType* filename,
bool replace_trailing);
bool IsShellIntegratedExtension(const base::FilePath::StringType& extension);
void EnsureSafeExtension(const std::string& mime_type,
bool ignore_extension,
base::FilePath* file_name);
bool FilePathToString16(const base::FilePath& path, std::u16string* converted);
// Similar to GetSuggestedFilename(), but takes a function to replace illegal
// characters. If |should_replace_extension| is true, the file extension
// extracted from a URL will always be considered unreliable and the file
// extension will be determined by |mime_type|.
std::u16string GetSuggestedFilenameImpl(
const GURL& url,
const std::string& content_disposition,
const std::string& referrer_charset,
const std::string& suggested_name,
const std::string& mime_type,
const std::string& default_name,
bool should_replace_extension,
ReplaceIllegalCharactersFunction replace_illegal_characters_function);
// Similar to GenerateFileName(), but takes a function to replace illegal
// characters. If |should_replace_extension| is true, the file extension
// extracted from a URL will always be considered unreliable and the file
// extension will be determined by |mime_type|.
base::FilePath GenerateFileNameImpl(
const GURL& url,
const std::string& content_disposition,
const std::string& referrer_charset,
const std::string& suggested_name,
const std::string& mime_type,
const std::string& default_name,
bool should_replace_extension,
ReplaceIllegalCharactersFunction replace_illegal_characters_function);
} // namespace net
#endif // NET_BASE_FILENAME_UTIL_INTERNAL_H_

View File

@@ -0,0 +1,245 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/fuchsia/network_interface_cache.h"
#include <fuchsia/net/interfaces/cpp/fidl.h>
#include <optional>
#include <utility>
#include "base/containers/flat_map.h"
#include "base/logging.h"
#include "base/sequence_checker.h"
#include "base/synchronization/lock.h"
#include "net/base/network_change_notifier.h"
#include "net/base/network_interfaces.h"
#include "net/base/network_interfaces_fuchsia.h"
namespace net::internal {
namespace {
// Returns a ConnectionType derived from the supplied InterfaceProperties:
// - CONNECTION_NONE if the interface is not publicly routable.
// - Otherwise, returns a type derived from the interface's device_class.
NetworkChangeNotifier::ConnectionType GetEffectiveConnectionType(
const InterfaceProperties& properties,
bool require_wlan) {
if (!properties.IsPubliclyRoutable()) {
return NetworkChangeNotifier::CONNECTION_NONE;
}
NetworkChangeNotifier::ConnectionType connection_type =
ConvertConnectionType(properties.device_class());
if (require_wlan &&
connection_type != NetworkChangeNotifier::CONNECTION_WIFI) {
return NetworkChangeNotifier::CONNECTION_NONE;
}
return connection_type;
}
bool CanReachExternalNetwork(const InterfaceProperties& interface,
bool require_wlan) {
return GetEffectiveConnectionType(interface, require_wlan) !=
NetworkChangeNotifier::CONNECTION_NONE;
}
} // namespace
NetworkInterfaceCache::NetworkInterfaceCache(bool require_wlan)
: require_wlan_(require_wlan) {}
NetworkInterfaceCache::~NetworkInterfaceCache() = default;
std::optional<NetworkInterfaceCache::ChangeBits>
NetworkInterfaceCache::AddInterfaces(
std::vector<fuchsia::net::interfaces::Properties> interfaces) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::AutoLock auto_lock(lock_);
ChangeBits combined_changes = kNoChange;
for (auto& interface : interfaces) {
auto change_bits = AddInterfaceWhileLocked(std::move(interface));
if (!change_bits.has_value()) {
return std::nullopt;
}
combined_changes |= change_bits.value();
}
return combined_changes;
}
std::optional<NetworkInterfaceCache::ChangeBits>
NetworkInterfaceCache::AddInterface(
fuchsia::net::interfaces::Properties properties) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::AutoLock auto_lock(lock_);
return AddInterfaceWhileLocked(std::move(properties));
}
std::optional<NetworkInterfaceCache::ChangeBits>
NetworkInterfaceCache::AddInterfaceWhileLocked(
fuchsia::net::interfaces::Properties properties)
EXCLUSIVE_LOCKS_REQUIRED(lock_) VALID_CONTEXT_REQUIRED(sequence_checker_) {
if (error_state_) {
return std::nullopt;
}
auto interface = InterfaceProperties::VerifyAndCreate(std::move(properties));
if (!interface) {
LOG(ERROR) << "Incomplete interface properties.";
SetErrorWhileLocked();
return std::nullopt;
}
if (interfaces_.find(interface->id()) != interfaces_.end()) {
LOG(ERROR) << "Unexpected duplicate interface ID " << interface->id();
SetErrorWhileLocked();
return std::nullopt;
}
ChangeBits change_bits = kNoChange;
if (CanReachExternalNetwork(*interface, require_wlan_)) {
change_bits |= kIpAddressChanged;
}
interfaces_.emplace(interface->id(), std::move(*interface));
if (UpdateConnectionTypeWhileLocked()) {
change_bits |= kConnectionTypeChanged;
}
return change_bits;
}
std::optional<NetworkInterfaceCache::ChangeBits>
NetworkInterfaceCache::ChangeInterface(
fuchsia::net::interfaces::Properties properties) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::AutoLock auto_lock(lock_);
if (error_state_) {
return std::nullopt;
}
auto cache_entry = interfaces_.find(properties.id());
if (cache_entry == interfaces_.end()) {
LOG(ERROR) << "Unknown interface ID " << properties.id();
SetErrorWhileLocked();
return std::nullopt;
}
const bool old_can_reach =
CanReachExternalNetwork(cache_entry->second, require_wlan_);
const bool has_addresses = properties.has_addresses();
if (!cache_entry->second.Update(std::move(properties))) {
LOG(ERROR) << "Update failed";
SetErrorWhileLocked();
return std::nullopt;
}
const bool new_can_reach =
CanReachExternalNetwork(cache_entry->second, require_wlan_);
ChangeBits change_bits = kNoChange;
if (has_addresses || old_can_reach != new_can_reach) {
change_bits |= kIpAddressChanged;
}
if (UpdateConnectionTypeWhileLocked()) {
change_bits |= kConnectionTypeChanged;
}
return change_bits;
}
std::optional<NetworkInterfaceCache::ChangeBits>
NetworkInterfaceCache::RemoveInterface(
InterfaceProperties::InterfaceId interface_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::AutoLock auto_lock(lock_);
if (error_state_) {
return std::nullopt;
}
auto cache_entry = interfaces_.find(interface_id);
if (cache_entry == interfaces_.end()) {
LOG(ERROR) << "Unknown interface ID " << interface_id;
SetErrorWhileLocked();
return std::nullopt;
}
ChangeBits change_bits = kNoChange;
if (CanReachExternalNetwork(cache_entry->second, require_wlan_)) {
change_bits |= kIpAddressChanged;
}
interfaces_.erase(cache_entry);
if (UpdateConnectionTypeWhileLocked()) {
change_bits |= kConnectionTypeChanged;
}
return change_bits;
}
bool NetworkInterfaceCache::GetOnlineInterfaces(
NetworkInterfaceList* networks) const {
DCHECK(networks);
base::AutoLock auto_lock(lock_);
if (error_state_) {
return false;
}
for (const auto& [_, interface] : interfaces_) {
if (!interface.online()) {
continue;
}
if (interface.device_class().is_loopback()) {
continue;
}
interface.AppendNetworkInterfaces(networks);
}
return true;
}
NetworkChangeNotifier::ConnectionType NetworkInterfaceCache::GetConnectionType()
const {
base::AutoLock auto_lock(lock_);
if (error_state_) {
return NetworkChangeNotifier::CONNECTION_UNKNOWN;
}
return connection_type_;
}
void NetworkInterfaceCache::SetError() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::AutoLock auto_lock(lock_);
SetErrorWhileLocked();
}
bool NetworkInterfaceCache::UpdateConnectionTypeWhileLocked()
EXCLUSIVE_LOCKS_REQUIRED(lock_) VALID_CONTEXT_REQUIRED(sequence_checker_) {
NetworkChangeNotifier::ConnectionType connection_type =
NetworkChangeNotifier::ConnectionType::CONNECTION_NONE;
for (const auto& [_, interface] : interfaces_) {
connection_type = GetEffectiveConnectionType(interface, require_wlan_);
if (connection_type != NetworkChangeNotifier::CONNECTION_NONE) {
break;
}
}
if (connection_type != connection_type_) {
connection_type_ = connection_type;
return true;
}
return false;
}
void NetworkInterfaceCache::SetErrorWhileLocked()
EXCLUSIVE_LOCKS_REQUIRED(lock_) VALID_CONTEXT_REQUIRED(sequence_checker_) {
error_state_ = true;
interfaces_.clear();
interfaces_.shrink_to_fit();
}
} // namespace net::internal

View File

@@ -0,0 +1,124 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_FUCHSIA_NETWORK_INTERFACE_CACHE_H_
#define NET_BASE_FUCHSIA_NETWORK_INTERFACE_CACHE_H_
#include <fuchsia/net/interfaces/cpp/fidl.h>
#include <stdint.h>
#include <zircon/errors.h>
#include <optional>
#include "base/containers/flat_map.h"
#include "base/sequence_checker.h"
#include "base/thread_annotations.h"
#include "net/base/net_export.h"
#include "net/base/network_change_notifier.h"
#include "net/base/network_interfaces.h"
#include "net/base/network_interfaces_fuchsia.h"
namespace net::internal {
// Cache of network interfaces, keyed by unique interface IDs, kept up-to-date
// by NetworkChangeNotifierFuchsia.
//
// If `require_wlan` is `true`, only WLAN interfaces are observed.
//
// Can be accessed via `NetworkChangeNotifier::GetNetworkInterfaceCache()` to
// get the current list of networks. Methods that read the cache are
// thread-safe, but methods that modify the cache must be in sequence.
//
// NetworkInterfaceCache expects valid write operations only, and can go into
// unrecoverable error state if `SetError()` is called, or attempted to
// - Add an interface twice.
// - Add/Change an interface with incomplete properties.
// - Change/Remove an interface unknown to the cache.
//
// After entering error state, all subsequent write operations return
// `std::nullopt`, and subsequent read operations will not return a result
// (specifically, `GetOnlineInterfaces` returns `false`, and `GetConnectionType`
// returns `CONNECTION_UNKNOWN`).
class NET_EXPORT_PRIVATE NetworkInterfaceCache {
public:
using ChangeBits = uint32_t;
enum : ChangeBits {
kNoChange = 0,
kIpAddressChanged = 1 << 0,
kConnectionTypeChanged = 1 << 1,
};
explicit NetworkInterfaceCache(bool require_wlan);
~NetworkInterfaceCache();
NetworkInterfaceCache(const NetworkInterfaceCache&) = delete;
NetworkInterfaceCache& operator=(const NetworkInterfaceCache&) = delete;
// Returns `std::nullopt` if any of the interfaces fail to be added. See
// `AddInterface`.
std::optional<ChangeBits> AddInterfaces(
std::vector<fuchsia::net::interfaces::Properties> interfaces);
// Returns `std::nullopt` if `properties` is invalid or incomplete, or if the
// interface already exists in the cache.
std::optional<ChangeBits> AddInterface(
fuchsia::net::interfaces::Properties properties);
// Returns `std::nullopt` if `properties` is invalid or does not contain an
// `id`, or if the interface does not exist in the cache.
std::optional<ChangeBits> ChangeInterface(
fuchsia::net::interfaces::Properties properties);
// Returns `std::nullopt` if `interface_id` does not exist in the cache.
std::optional<ChangeBits> RemoveInterface(
InterfaceProperties::InterfaceId interface_id);
// Set cache to unrecoverable error state and clears the cache.
// Should be called when contents of the cache can no longer be updated to
// reflect the state of the system.
void SetError();
// Thread-safe method that populates a list of online network interfaces.
// Ignores loopback interface. Returns `false` if in error state.
bool GetOnlineInterfaces(NetworkInterfaceList* networks) const;
// Thread-safe method that returns the current connection type.
// Returns `CONNECTION_UNKNOWN` if in error state.
NetworkChangeNotifier::ConnectionType GetConnectionType() const;
private:
// Updates `connection_type_` from `interfaces_` and returns `true` if
// the connection type changed.
bool UpdateConnectionTypeWhileLocked() EXCLUSIVE_LOCKS_REQUIRED(lock_)
VALID_CONTEXT_REQUIRED(sequence_checker_);
std::optional<ChangeBits> AddInterfaceWhileLocked(
fuchsia::net::interfaces::Properties properties)
EXCLUSIVE_LOCKS_REQUIRED(lock_) VALID_CONTEXT_REQUIRED(sequence_checker_);
void SetErrorWhileLocked() EXCLUSIVE_LOCKS_REQUIRED(lock_)
VALID_CONTEXT_REQUIRED(sequence_checker_);
// Whether only WLAN interfaces should be taken into account.
const bool require_wlan_;
mutable base::Lock lock_;
base::flat_map<InterfaceProperties::InterfaceId, InterfaceProperties>
interfaces_ GUARDED_BY(lock_);
// The ConnectionType of the default network interface.
NetworkChangeNotifier::ConnectionType connection_type_ GUARDED_BY(lock_) =
NetworkChangeNotifier::CONNECTION_NONE;
// Set to true if any update is inconsistent with the network interfaces state
// that is currently cached.
bool error_state_ GUARDED_BY(lock_) = false;
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace net::internal
#endif // NET_BASE_FUCHSIA_NETWORK_INTERFACE_CACHE_H_

Some files were not shown because too many files have changed in this diff Show More