rust_rcs_client/
context.rs

1// Copyright 2023 宋昊文
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15extern crate rustls;
16extern crate walkdir;
17
18use std::fs::File;
19use std::io::{BufReader, Read};
20use std::path::PathBuf;
21use std::sync::Arc;
22use std::{iter, panic};
23
24use rustls::pki_types::CertificateDer;
25use rustls::{ClientConfig, RootCertStore};
26use rustls_pemfile::{read_one, Item};
27
28use rust_rcs_core::dns::DnsClient;
29
30use rust_rcs_core::ffi::log::platform_log;
31
32use rust_rcs_core::http::HttpClient;
33
34use rust_rcs_core::security::gba::GbaContext;
35use rust_rcs_core::security::SecurityContext;
36
37use rust_rcs_core::third_gen_pp::{addressing, CountryCode, NetworkCode};
38
39use tokio::runtime::Runtime;
40use tokio::sync::broadcast;
41use walkdir::WalkDir;
42
43const LOG_TAG: &str = "context";
44
45pub struct Context {
46    pub fs_root_dir: String,
47
48    dns_client: Arc<DnsClient>,
49    http_client: Arc<HttpClient>,
50
51    tls_client_config: Arc<ClientConfig>,
52
53    security_context: Arc<SecurityContext>,
54
55    otp_broadcaster: broadcast::Sender<String>,
56}
57
58impl Context {
59    pub fn new(fs_root_dir: &str, rt: Arc<Runtime>) -> Context {
60        let tls_client_config = make_tls_client_config(fs_root_dir);
61        let tls_client_config = Arc::new(tls_client_config);
62
63        let dns_client = DnsClient::new(Arc::clone(&rt));
64        let dns_client = Arc::new(dns_client);
65
66        let http_client =
67            HttpClient::new(Arc::clone(&tls_client_config), Arc::clone(&dns_client), rt);
68        let http_client = Arc::new(http_client);
69
70        let (otp_broadcaster, _) = broadcast::channel(1);
71
72        Context {
73            fs_root_dir: fs_root_dir.to_string(),
74
75            dns_client,
76            http_client,
77
78            tls_client_config,
79
80            security_context: Arc::new(SecurityContext::new()),
81
82            otp_broadcaster,
83        }
84    }
85
86    pub fn get_fs_root_dir(&self) -> &str {
87        &self.fs_root_dir
88    }
89
90    pub fn get_dns_client(&self) -> Arc<DnsClient> {
91        Arc::clone(&self.dns_client)
92    }
93
94    pub fn get_http_client(&self) -> Arc<HttpClient> {
95        Arc::clone(&self.http_client)
96    }
97
98    pub fn get_tls_client_config(&self) -> Arc<ClientConfig> {
99        Arc::clone(&self.tls_client_config)
100    }
101
102    pub fn get_security_context(&self) -> Arc<SecurityContext> {
103        Arc::clone(&self.security_context)
104    }
105
106    pub fn subscribe_otp(&self) -> broadcast::Receiver<String> {
107        self.otp_broadcaster.subscribe()
108    }
109
110    pub fn broadcast_otp(&self, otp: &str) {
111        if let Err(e) = self.otp_broadcaster.send(String::from(otp)) {
112            platform_log(LOG_TAG, format!("broadcast_otp failed with error {}", &e));
113        }
114    }
115
116    pub fn make_gba_context(
117        &self,
118        imsi: &str,
119        mcc: CountryCode,
120        mnc: NetworkCode,
121        subscription_id: i32,
122    ) -> GbaContext {
123        let impi = addressing::impi_from_imsi(imsi, mcc, mnc);
124
125        let bsf_realm = addressing::bsf_address(mcc, mnc);
126        let bsf_url = format! {"{}:8080", bsf_realm}; // port that actually works
127
128        GbaContext::new(impi, bsf_url, bsf_realm, subscription_id)
129    }
130}
131
132fn make_tls_client_config(root_dir: &str) -> ClientConfig {
133    let mut root_certs = RootCertStore::empty();
134
135    let mut path = PathBuf::from(root_dir);
136    path.push("certs");
137
138    platform_log(
139        LOG_TAG,
140        format!("searching for certificates under path {:?}", path),
141    );
142
143    for entry in WalkDir::new(path)
144        .min_depth(1)
145        .into_iter()
146        .filter_entry(|e| -> bool {
147            platform_log(LOG_TAG, format!("found entry {:?}", e));
148
149            if let Some(file_name) = e.file_name().to_str() {
150                file_name.ends_with(".der") || file_name.ends_with(".pem")
151            } else {
152                false
153            }
154        })
155    {
156        match entry {
157            Ok(e) => {
158                let path = e.path();
159                platform_log(LOG_TAG, format!("init certificate with file {:?}", path));
160                let mut f = File::open(path).unwrap();
161                match path.extension() {
162                    Some(ext) => {
163                        if ext.eq_ignore_ascii_case("pem") {
164                            let mut buf_reader = BufReader::new(f);
165                            for item in iter::from_fn(|| read_one(&mut buf_reader).transpose()) {
166                                match item {
167                                    Ok(cert_item) => match cert_item {
168                                        Item::X509Certificate(cert) => {
169                                            platform_log(LOG_TAG, "adding X509Certificate");
170                                            root_certs.add(cert).unwrap();
171                                        }
172                                        Item::Pkcs1Key(key) => platform_log(
173                                            LOG_TAG,
174                                            format!("rsa pkcs1 key {:?} not handled", key),
175                                        ),
176                                        Item::Pkcs8Key(key) => platform_log(
177                                            LOG_TAG,
178                                            format!("pkcs8 key {:?} not handled", key),
179                                        ),
180                                        Item::Sec1Key(key) => platform_log(
181                                            LOG_TAG,
182                                            format!("sec1 ec key {:?} not handled", key),
183                                        ),
184                                        _ => platform_log(LOG_TAG, "no certificate found"),
185                                    },
186
187                                    Err(e) => platform_log(LOG_TAG, format!("pem error {:?}", e)),
188                                }
189                            }
190                        } else {
191                            let mut v = vec![0; 4 * 1024];
192                            f.read_to_end(&mut v).unwrap();
193
194                            let cert = CertificateDer::from(v);
195                            platform_log(LOG_TAG, format!("adding certificate {:?}", cert));
196                            root_certs.add(cert).unwrap();
197                        }
198                    }
199                    None => panic!(""),
200                }
201            }
202
203            Err(e) => {}
204        }
205    }
206
207    let client_config = ClientConfig::builder()
208        .with_root_certificates(root_certs)
209        .with_no_client_auth();
210
211    client_config
212}