1 // Copyright (C) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 use std::error::Error;
15
16 use ylong_http_client::async_impl::{Client, Request};
17 use ylong_http_client::{
18 Certificate, HttpClientError, Interceptor, Proxy, PubKeyPins, Redirect, Timeout, TlsVersion,
19 };
20
21 cfg_oh! {
22 use crate::manage::SystemConfig;
23 use crate::utils::url_policy::check_url_domain;
24 }
25
26 use super::files::BundleCache;
27 use crate::task::config::{Action, TaskConfig};
28 use crate::task::files::convert_path;
29
30 const CONNECT_TIMEOUT: u64 = 60;
31 const SECONDS_IN_ONE_WEEK: u64 = 7 * 24 * 60 * 60;
32
build_client( config: &TaskConfig, #[cfg(feature = "oh")] mut system: SystemConfig, ) -> Result<Client, Box<dyn Error + Send + Sync>>33 pub(crate) fn build_client(
34 config: &TaskConfig,
35 #[cfg(feature = "oh")] mut system: SystemConfig,
36 ) -> Result<Client, Box<dyn Error + Send + Sync>> {
37 let mut client = Client::builder()
38 .connect_timeout(Timeout::from_secs(CONNECT_TIMEOUT))
39 .request_timeout(Timeout::from_secs(SECONDS_IN_ONE_WEEK))
40 .min_tls_version(TlsVersion::TLS_1_2);
41
42 client = client.sockets_owner(config.common_data.uid as u32, config.common_data.uid as u32);
43 // Set redirect strategy.
44 if config.common_data.redirect {
45 client = client.redirect(Redirect::limited(usize::MAX));
46 } else {
47 client = client.redirect(Redirect::none());
48 }
49
50 // Set HTTP proxy.
51 #[cfg(feature = "oh")]
52 if let Some(proxy) = build_task_proxy(config)? {
53 client = client.proxy(proxy);
54 } else if let Some(proxy) = build_system_proxy(&system)? {
55 client = client.proxy(proxy);
56 }
57
58 // HTTP url that contains redirects also require a certificate when
59 // redirected to HTTPS.
60
61 // Set system certs.
62 #[cfg(feature = "oh")]
63 if let Some(certs) = system.certs.take() {
64 for cert in certs.into_iter() {
65 client = client.add_root_certificate(cert)
66 }
67 }
68
69 // Set task certs.
70 let certificates = build_task_certs(config)?;
71 for cert in certificates.into_iter() {
72 client = client.add_root_certificate(cert)
73 }
74
75 // Set task certificate pinned_key.
76 if let Some(pinned_key) = build_task_certificate_pins(config)? {
77 client = client.add_public_key_pins(pinned_key);
78 }
79
80 const ATOMIC_SERVICE: u32 = 1;
81 if config.bundle_type == ATOMIC_SERVICE {
82 let domain_type = action_to_domain_type(config.common_data.action);
83 info!(
84 "ApiPolicy Domain check, tid {}, bundle {}, domain_type {}, url {}",
85 config.common_data.task_id, &config.bundle, &domain_type, &config.url
86 );
87 #[cfg(feature = "oh")]
88 if let Some(is_accessed) = check_url_domain(&config.bundle, &domain_type, &config.url) {
89 if !is_accessed {
90 error!(
91 "Intercept request by domain check, tid {}, bundle {}, domain_type {}, url {}",
92 config.common_data.task_id, &config.bundle, &domain_type, &config.url
93 );
94 sys_event!(
95 ExecFault,
96 DfxCode::URL_POLICY_FAULT_00,
97 &format!(
98 "Intercept request by domain check, tid {}, bundle {}, domain_type {}, url {}",
99 config.common_data.task_id, &config.bundle, &domain_type, &config.url)
100 );
101
102 return Err(Box::new(HttpClientError::other(
103 "Intercept request by domain check",
104 )));
105 }
106 } else {
107 info!(
108 "Intercept request by domain check, tid {}, domain_type {}, url {}",
109 config.common_data.task_id, &domain_type, &config.url
110 );
111 }
112
113 #[cfg(feature = "oh")]
114 {
115 let interceptors = DomainInterceptor::new(config.bundle.clone(), domain_type);
116 client = client.interceptor(interceptors);
117 }
118
119 info!(
120 "add interceptor domain check, tid {}",
121 config.common_data.task_id
122 );
123 }
124
125 // Build client.
126 Ok(cvt_res_error!(
127 client.build().map_err(Box::new),
128 "Build client failed",
129 ))
130 }
131
build_task_proxy(config: &TaskConfig) -> Result<Option<Proxy>, Box<dyn Error + Send + Sync>>132 fn build_task_proxy(config: &TaskConfig) -> Result<Option<Proxy>, Box<dyn Error + Send + Sync>> {
133 if config.proxy.is_empty() {
134 return Ok(None);
135 }
136
137 Ok(Some(cvt_res_error!(
138 Proxy::all(&config.proxy).build().map_err(Box::new),
139 "Create task proxy failed",
140 )))
141 }
142
build_task_certificate_pins( config: &TaskConfig, ) -> Result<Option<PubKeyPins>, Box<dyn Error + Send + Sync>>143 fn build_task_certificate_pins(
144 config: &TaskConfig,
145 ) -> Result<Option<PubKeyPins>, Box<dyn Error + Send + Sync>> {
146 if config.certificate_pins.is_empty() {
147 return Ok(None);
148 }
149
150 Ok(Some(cvt_res_error!(
151 PubKeyPins::builder()
152 .add(&config.url, &config.certificate_pins)
153 .build()
154 .map_err(Box::new),
155 "Create task certificate pinned_key failed",
156 )))
157 }
158
159 #[cfg(feature = "oh")]
build_system_proxy( system: &SystemConfig, ) -> Result<Option<Proxy>, Box<dyn Error + Send + Sync>>160 fn build_system_proxy(
161 system: &SystemConfig,
162 ) -> Result<Option<Proxy>, Box<dyn Error + Send + Sync>> {
163 let proxy_host = &system.proxy_host;
164
165 if proxy_host.is_empty() {
166 return Ok(None);
167 }
168
169 let proxy_port = &system.proxy_port;
170 let proxy_url = match proxy_port.is_empty() {
171 true => proxy_host.clone(),
172 false => format!("{}:{}", proxy_host, proxy_port),
173 };
174 let no_proxy = &system.proxy_exlist;
175 Ok(Some(cvt_res_error!(
176 Proxy::all(&proxy_url)
177 .no_proxy(no_proxy)
178 .build()
179 .map_err(Box::new),
180 "Create system proxy failed",
181 )))
182 }
183
build_task_certs(config: &TaskConfig) -> Result<Vec<Certificate>, Box<dyn Error + Send + Sync>>184 fn build_task_certs(config: &TaskConfig) -> Result<Vec<Certificate>, Box<dyn Error + Send + Sync>> {
185 let uid = config.common_data.uid;
186 let paths = config.certs_path.as_slice();
187 let mut bundle_cache = BundleCache::new(config);
188
189 let mut certs = Vec::new();
190 for (idx, path) in paths.iter().enumerate() {
191 let bundle_name = bundle_cache.get_value()?;
192 let path = convert_path(uid, &bundle_name, path);
193 let cert = cvt_res_error!(
194 Certificate::from_path(&path).map_err(Box::new),
195 "Parse task cert failed - idx: {}",
196 idx,
197 );
198 certs.push(cert);
199 }
200 Ok(certs)
201 }
202
action_to_domain_type(action: Action) -> String203 fn action_to_domain_type(action: Action) -> String {
204 match action {
205 Action::Download => "download".to_string(),
206 Action::Upload => "upload".to_string(),
207 Action::Any => "".to_string(),
208 _ => unreachable!(),
209 }
210 }
211
212 struct DomainInterceptor {
213 app_id: String,
214 domain_type: String,
215 }
216
217 impl DomainInterceptor {
new(app_id: String, domain_type: String) -> Self218 fn new(app_id: String, domain_type: String) -> Self {
219 DomainInterceptor {
220 app_id,
221 domain_type,
222 }
223 }
224 }
225
226 #[cfg(feature = "oh")]
227 impl Interceptor for DomainInterceptor {
228 /// Intercepts the redirect request.
intercept_redirect_request(&self, request: &Request) -> Result<(), HttpClientError>229 fn intercept_redirect_request(&self, request: &Request) -> Result<(), HttpClientError> {
230 let url = &request.uri().to_string();
231 info!(
232 "ApiPolicy Domain check redirect, bundle {}, domain_type {}, url {}",
233 &self.app_id, &self.domain_type, &url
234 );
235 match check_url_domain(&self.app_id, &self.domain_type, url).unwrap_or(true) {
236 true => Ok(()),
237 false => Err(HttpClientError::other(
238 "Intercept redirect request by domain check",
239 )),
240 }
241 }
242 }
243