1extern crate base64;
2extern crate fake_useragent;
3extern crate regex;
4extern crate reqwest;
5extern crate url;
6
7use std::time::Duration;
9use reqwest::{
11 blocking::ClientBuilder,
12 header::{HeaderValue, COOKIE, REFERER, SET_COOKIE, USER_AGENT},
13};
14
15pub struct Bypasser<'a> {
16 wait: u8,
17 retry: u32,
18 proxy: Option<&'a str>,
19 user_agent: String,
20 client: reqwest::blocking::Client,
21 user_agents: Option<fake_useragent::UserAgents>,
22}
23
24impl<'a> Default for Bypasser<'a> {
25 fn default() -> Self {
26 Bypasser {
27 wait: 3,
28 retry: 1,
29 proxy: None,
30 user_agent: String::new(),
31 client: ClientBuilder::new()
32 .danger_accept_invalid_certs(true)
33 .danger_accept_invalid_hostnames(true)
34 .gzip(true)
35 .build()
36 .unwrap(),
37 user_agents: None,
38 }
39 }
40}
41
42impl<'a> Bypasser<'a> {
43 pub fn wait(mut self, secs: u8) -> Self {
44 self.wait = secs;
45 self
46 }
47
48 pub fn user_agent(mut self, user_agent: &str) -> Self {
49 self.user_agent = user_agent.to_owned();
50 self
51 }
52
53 pub fn random_user_agent(mut self, flag: bool) -> Self {
54 if flag {
55 self.user_agents = Some(
56 fake_useragent::UserAgentsBuilder::new()
57 .cache(false)
58 .set_browsers(fake_useragent::Browsers::new().set_chrome().set_firefox().set_safari())
59 .build(),
60 );
61 }
62 self
63 }
64
65 pub fn proxy(mut self, address: &'a str) -> Self {
66 self.proxy = Some(address);
67 self
68 }
69
70 pub fn retry(mut self, times: u32) -> Self {
71 self.retry = times;
72 self
73 }
74
75 fn build_client(&mut self) -> &mut Self {
76 let mut client_builder = ClientBuilder::new()
77 .danger_accept_invalid_certs(true)
78 .danger_accept_invalid_hostnames(true)
79 .gzip(true)
80 .redirect(reqwest::redirect::Policy::none())
81 .timeout(Duration::from_secs(30));
82 if let Some(address) = self.proxy {
83 client_builder = client_builder.proxy(reqwest::Proxy::all(address).unwrap());
84 }
85 self.client = client_builder.build().unwrap();
86 self
87 }
88
89 fn parse_challenge(html: &str) -> Vec<(String, String)> {
90 regex::Regex::new(r#"name="(r|jschl_vc|pass)"(?: [^<>]*)? value="(.+?)""#)
91 .unwrap()
92 .captures_iter(html)
93 .map(|caps| (caps[1].to_owned(), caps[2].to_owned()))
94 .collect()
95 }
96
97 fn parse_js(html: &str, domain: &str) -> String {
98 use regex::Regex;
100
101 let challenge = &Regex::new(
102 r#"setTimeout\(function\(\)\{\s+(var s,t,o,p,b,r,e,a,k,i,n,g,f.+?\r?\n[\s\S]+?a\.value =.+?)\r?\n"#,
103 )
104 .unwrap()
105 .captures(html)
106 .unwrap()[1];
107 let inner_html = if let Some(caps) = Regex::new(r#"<div(?: [^<>]*)? id="cf-dn.*?">([^<>]*)"#)
108 .unwrap()
109 .captures(html)
110 {
111 caps[1].to_owned()
112 } else {
113 String::new()
114 };
115 format!(
116 r#"
117 var document = {{
118 createElement: function () {{
119 return {{ firstChild: {{ href: "http://{}/" }} }}
120 }},
121 getElementById: function () {{
122 return {{"innerHTML": "{}"}};
123 }}
124 }};
125 {}; process.stdout.write(a.value);
126 "#,
127 domain, inner_html, challenge
128 )
129 }
130
131 fn run_js(js: &str) -> String {
132 String::from_utf8(
133 std::process::Command::new("node")
134 .args(&["-e", js])
135 .output()
136 .unwrap()
137 .stdout,
138 )
139 .unwrap()
140 }
141
142 fn request_challenge(&mut self, url: &str) -> (String, String, HeaderValue, String) {
143 self.build_client();
144 if let Some(ref user_agents) = self.user_agents {
145 self.user_agent = user_agents.random().to_string();
146 }
147 loop {
148 match self.client.get(url).header(USER_AGENT, self.user_agent.as_str()).send() {
149 Ok(resp) => {
150 let url = resp.url().as_str().to_owned();
151 let cookie = resp.headers()[SET_COOKIE].to_owned();
152 match resp.text() {
153 Ok(text) => {
154 let path = regex::Regex::new(r#"id="challenge-form" action="([^"]*)""#)
155 .unwrap()
156 .captures(&text)
157 .unwrap()[1]
158 .into();
159 return (text, url, cookie, path);
160 }
161 Err(e) => println!("At request_challenge() text(), {:?}", e),
162 }
163 }
164 Err(e) => println!("At, request_challenge() send(), {:?}", e),
165 }
166 }
167 }
168
169 fn solve_challenge(
170 &mut self,
171 url: &str,
172 cookie: &HeaderValue,
173 referer: &str,
174 params: &[(String, String)],
175 ) -> Result<(HeaderValue, HeaderValue), &str> {
176 let mut retry = 0u32;
177 loop {
178 match self
179 .client
180 .post(url)
181 .header(COOKIE, cookie)
182 .header(REFERER, referer)
183 .header(USER_AGENT, self.user_agent.as_str())
184 .form(params)
185 .send()
186 {
187 Ok(resp) => {
188 if resp.headers().contains_key(SET_COOKIE) {
189 return Ok((resp.headers()[SET_COOKIE].to_owned(), self.user_agent.parse().unwrap()));
190 }
191 }
192 Err(e) => println!("{:?}", e),
193 }
194
195 retry += 1;
196 if retry == self.retry {
197 return Err("reach max retries");
198 }
199 }
200 }
201
202 pub fn bypass(&mut self, url: &str) -> Result<(HeaderValue, HeaderValue), &str> {
203 let (html, referer, cookie, path) = self.request_challenge(url);
204
205 let (challenge_url, domain) = {
206 let url = url::Url::parse(url).unwrap();
207 let domain = url.domain().unwrap().to_owned();
208
209 (format!("{}://{}{}", url.scheme(), domain, path), domain)
210 };
211 let params = {
212 let mut p = Bypasser::parse_challenge(&html);
213 p.push((
214 String::from("jschl_answer"),
215 Bypasser::run_js(&Bypasser::parse_js(&html, &domain)),
216 ));
217
218 p
219 };
220
221 std::thread::sleep(Duration::from_secs(self.wait as u64));
222
223 self.solve_challenge(&challenge_url, &cookie, &referer, ¶ms)
224 }
225}