host_utils/
utils.rs

1use std::io;
2
3use colored::Colorize;
4use sha2::Digest;
5
6use crate::db::UserData;
7
8pub fn print_allow<R: io::Read, W: io::Write>(r: R, stdout: &mut W) -> io::Result<()> {
9    let data = UserData::from_read(r)?;
10    let mut list = Vec::with_capacity(data.allow.len());
11    list.extend(data.allow.iter().map(|v| v.as_bytes()));
12    list.sort();
13    writeln!(stdout, "\t{}", "Allow List".yellow().bold().underline())?;
14    for i in list {
15        stdout.write_all(i)?;
16        stdout.write_all(b"\n")?;
17    }
18    stdout.flush()
19}
20pub fn print_block<R: io::Read, W: io::Write>(r: R, stdout: &mut W) -> io::Result<()> {
21    let data = UserData::from_read(r)?;
22    let mut list = Vec::with_capacity(data.block.len());
23    list.extend(data.block.iter().map(|v| v.as_bytes()));
24    list.sort();
25    writeln!(stdout, "\t{}", "Block List".yellow().bold().underline())?;
26    for i in list {
27        stdout.write_all(i)?;
28        stdout.write_all(b"\n")?;
29    }
30    stdout.flush()
31}
32pub fn print_redirect<R: io::Read, W: io::Write>(r: R, stdout: &mut W) -> io::Result<()> {
33    let data = UserData::from_read(r)?;
34    let mut list = Vec::with_capacity(data.redirect.len());
35    list.extend(
36        data.redirect
37            .iter()
38            .map(|(k, v)| (v.as_bytes(), k.as_bytes())),
39    );
40    list.sort();
41    writeln!(stdout, "\t{}", "Redirect List".yellow().bold().underline())?;
42    for (to, from) in list {
43        stdout.write_all(to)?;
44        stdout.write_all(b"  ")?;
45        stdout.write_all(from)?;
46        stdout.write_all(b"\n")?;
47    }
48    stdout.flush()
49}
50pub fn print_sources<R: io::Read, W: io::Write>(r: R, stdout: &mut W) -> io::Result<()> {
51    let data = UserData::from_read(r)?;
52    let mut list = Vec::with_capacity(data.sources.len());
53    list.extend(data.sources.keys().map(|k| k.as_bytes()));
54    list.sort();
55    writeln!(stdout, "\t{}", "Source List".yellow().bold().underline())?;
56    for i in list {
57        stdout.write_all(i)?;
58        stdout.write_all(b"\n")?;
59    }
60    stdout.flush()
61}
62
63pub(crate) fn is_valid_host<T: AsRef<str>>(value: T) -> bool {
64    let value = value.as_ref();
65    if value.is_empty()
66        || value.starts_with(' ')
67        || value.ends_with(' ')
68        || value.starts_with('.')
69        || value.ends_with('.')
70        || !value.contains('.')
71    {
72        return false;
73    };
74    for c in value.chars() {
75        if c.is_ascii_uppercase()
76            || c.is_ascii_lowercase()
77            || c.is_ascii_digit()
78            || (c == '.')
79            || (c == '-')
80            || (c == '_')
81        {
82            continue;
83        } else {
84            return false;
85        }
86    }
87    true
88}
89pub(crate) fn get_host_from_url<'a, T: AsRef<str> + ?Sized>(webs: &'a T) -> Option<&'a str> {
90    let url = webs.as_ref().trim_start();
91    let f = |v: &'a str| {
92        if let Some((host, _)) = v.split_once(['/', '?', '#', ':']) {
93            if is_valid_host(host) {
94                Some(host)
95            } else {
96                None
97            }
98        } else if is_valid_host(v) {
99            Some(v)
100        } else {
101            None
102        }
103    };
104    if url.starts_with("http") {
105        url.split_once("://").and_then(|(left, right)| {
106            if !matches!(left, "http" | "https") {
107                None
108            } else {
109                f(right)
110            }
111        })
112    } else {
113        f(url)
114    }
115}
116
117pub(crate) fn is_valid_url<T: AsRef<str>>(value: T) -> bool {
118    value
119        .as_ref()
120        .split_once(r"://")
121        .and_then(|(l, r)| {
122            if !matches!(l, "http" | "https") {
123                None
124            } else {
125                r.split_once('.')
126            }
127        })
128        .and_then(|(l, r)| {
129            if r.len() < 2
130                || l.is_empty()
131                || l.chars().any(|c| {
132                    !matches!(&c,
133                        'a'..='z' | 'A'..='Z' |
134                        '0'..='9' | '_' | '-'
135                    )
136                })
137            {
138                return None;
139            }
140            if let Some((i, c)) = r.chars().enumerate().find(|(_, c)| {
141                matches!(c, '/' | '?' | ':' | '#')
142                    || !matches!(c,
143                      'a'..='z' | 'A'..='Z' |
144                      '0'..='9' | '.' | '_' | '-'
145                    )
146            }) {
147                match (i, c) {
148                    (_, 'a'..='z' | 'A'..='Z' | '0'..='9' | '.' | '_' | '-' | '/' | '?' | '#') => {
149                        Some(())
150                    }
151                    (index, ':') => {
152                        let mut port: u16 = 0;
153                        let zero = '0' as u16;
154                        for i in r.chars().skip(index + 1) {
155                            match i {
156                                '0'..='9' => {
157                                    let v = i as u16 - zero;
158                                    if let Some(v) = port.checked_mul(10) {
159                                        port = v;
160                                    } else {
161                                        return None;
162                                    }
163                                    if let Some(u) = port.checked_add(v) {
164                                        port = u;
165                                    } else {
166                                        return None;
167                                    }
168                                }
169                                '/' | '?' | '#' => break,
170                                _ => return None,
171                            }
172                        }
173                        Some(())
174                    }
175                    _ => None,
176                }
177            } else {
178                Some(())
179            }
180        })
181        .is_some()
182}
183
184pub(crate) fn get_host_from_url_or_host(value: &str) -> Option<&str> {
185    if is_comment(value) {
186        return None;
187    };
188    let v: Vec<_> = value.split_whitespace().collect();
189    if v.is_empty() {
190        return None;
191    };
192    let u = if v.len() == 1 { v[0] } else { v[1] };
193    if is_valid_host(u) {
194        Some(u)
195    } else if is_valid_url(u) {
196        get_host_from_url(u)
197    } else {
198        None
199    }
200}
201
202pub(crate) fn is_comment<T: AsRef<str>>(value: T) -> bool {
203    let v = value.as_ref().trim_start();
204    if v.is_empty() || v.starts_with('#') {
205        return true;
206    };
207    false
208}
209
210pub(crate) fn sha256<T: AsRef<str>>(value: T) -> [u8; 32] {
211    let mut hasher = sha2::Sha256::new();
212    hasher.update(value.as_ref().as_bytes());
213    hasher.finalize().into()
214}
215
216#[cfg(test)]
217mod tests {}