cloudflare_bypasser/
lib.rs

1extern crate base64;
2extern crate fake_useragent;
3extern crate regex;
4extern crate reqwest;
5extern crate url;
6
7// --- std ---
8use std::time::Duration;
9// --- external ---
10use 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		// --- external ---
99		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, &params)
224	}
225}