Compare commits

...

8 Commits

Author SHA1 Message Date
ngosang
3f279e9aa9 Bump version 2.2.4 2022-04-17 09:43:55 +02:00
ngosang
d962e1a14e Detect DDoS-Guard challenge 2022-04-17 09:21:10 +02:00
ngosang
93d8350097 Bump version 2.2.3 2022-04-16 22:29:36 +02:00
ngosang
d34b43e0a8 Fix 2000 ms navigation timeout 2022-04-16 21:39:50 +02:00
ngosang
2bf4dc62da Update README.md (libseccomp2 package in Debian) 2022-04-16 20:47:31 +02:00
termonio
bb0d757755 Update README.md (clarify proxy parameter) (#307)
Clarify that `request.get` will not use the provided proxy when a session is set.
2022-04-16 20:08:48 +02:00
ngosang
fc1fa601eb Update NPM dependencies 2022-04-16 19:19:08 +02:00
ngosang
9b1f8332c7 Disable Cloudflare ban detection 2022-04-16 18:32:58 +02:00
6 changed files with 1940 additions and 1472 deletions

View File

@@ -8,11 +8,11 @@
[![Donate Bitcoin](https://en.cryptobadges.io/badge/micro/13Hcv77AdnFWEUZ9qUpoPBttQsUT7q9TTh)](https://en.cryptobadges.io/donate/13Hcv77AdnFWEUZ9qUpoPBttQsUT7q9TTh) [![Donate Bitcoin](https://en.cryptobadges.io/badge/micro/13Hcv77AdnFWEUZ9qUpoPBttQsUT7q9TTh)](https://en.cryptobadges.io/donate/13Hcv77AdnFWEUZ9qUpoPBttQsUT7q9TTh)
[![Donate Ethereum](https://en.cryptobadges.io/badge/micro/0x0D1549BbB00926BF3D92c1A8A58695e982f1BE2E)](https://en.cryptobadges.io/donate/0x0D1549BbB00926BF3D92c1A8A58695e982f1BE2E) [![Donate Ethereum](https://en.cryptobadges.io/badge/micro/0x0D1549BbB00926BF3D92c1A8A58695e982f1BE2E)](https://en.cryptobadges.io/donate/0x0D1549BbB00926BF3D92c1A8A58695e982f1BE2E)
FlareSolverr is a proxy server to bypass Cloudflare protection. FlareSolverr is a proxy server to bypass Cloudflare and DDoS-GUARD protection.
## How it works ## How it works
FlareSolverr starts a proxy server and it waits for user requests in an idle state using few resources. FlareSolverr starts a proxy server, and it waits for user requests in an idle state using few resources.
When some request arrives, it uses [puppeteer](https://github.com/puppeteer/puppeteer) with the When some request arrives, it uses [puppeteer](https://github.com/puppeteer/puppeteer) with the
[stealth plugin](https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-stealth) [stealth plugin](https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-stealth)
to create a headless browser (Firefox). It opens the URL with user parameters and waits until the Cloudflare challenge to create a headless browser (Firefox). It opens the URL with user parameters and waits until the Cloudflare challenge
@@ -56,6 +56,10 @@ docker run -d \
ghcr.io/flaresolverr/flaresolverr:latest ghcr.io/flaresolverr/flaresolverr:latest
``` ```
If your host OS is Debian, make sure `libseccomp2` version is 2.5.x. You can check the version with `sudo apt-cache policy libseccomp2`
and update the package with `sudo apt install libseccomp2=2.5.1-1~bpo10+1` or `sudo apt install libseccomp2=2.5.1-1+deb11u1`.
Remember to restart the Docker daemon and the container after the update.
### Precompiled binaries ### Precompiled binaries
This is the recommended way for Windows users. This is the recommended way for Windows users.
@@ -104,6 +108,7 @@ This also speeds up the requests since it won't have to launch a new browser ins
Parameter | Notes Parameter | Notes
|--|--| |--|--|
session | Optional. The session ID that you want to be assigned to the instance. If isn't set a random UUID will be assigned. session | Optional. The session ID that you want to be assigned to the instance. If isn't set a random UUID will be assigned.
proxy | Optional, default disabled. Eg: `"proxy": {"url": "http://127.0.0.1:8888"}`. You must include the proxy schema in the URL: `http://`, `socks4://` or `socks5://`. Authorization (username/password) is not supported.
#### + `sessions.list` #### + `sessions.list`
@@ -141,7 +146,7 @@ session | Optional. Will send the request from and existing browser instance. If
maxTimeout | Optional, default value 60000. Max timeout to solve the challenge in milliseconds. maxTimeout | Optional, default value 60000. Max timeout to solve the challenge in milliseconds.
cookies | Optional. Will be used by the headless browser. Follow [this](https://github.com/puppeteer/puppeteer/blob/v3.3.0/docs/api.md#pagesetcookiecookies) format. cookies | Optional. Will be used by the headless browser. Follow [this](https://github.com/puppeteer/puppeteer/blob/v3.3.0/docs/api.md#pagesetcookiecookies) format.
returnOnlyCookies | Optional, default false. Only returns the cookies. Response data, headers and other parts of the response are removed. returnOnlyCookies | Optional, default false. Only returns the cookies. Response data, headers and other parts of the response are removed.
proxy | Optional, default disabled. Eg: `"proxy": {"url": "http://127.0.0.1:8888"}`. You must include the proxy schema in the URL: `http://`, `socks4://` or `socks5://`. Authorization (username/password) is not supported. proxy | Optional, default disabled. Eg: `"proxy": {"url": "http://127.0.0.1:8888"}`. You must include the proxy schema in the URL: `http://`, `socks4://` or `socks5://`. Authorization (username/password) is not supported. (When the `session` parameter is set, the proxy is ignored; a session specific proxy can be set in `sessions.create`.)
:warning: If you want to use Cloudflare clearance cookie in your scripts, make sure you use the FlareSolverr User-Agent too. If they don't match you will see the challenge. :warning: If you want to use Cloudflare clearance cookie in your scripts, make sure you use the FlareSolverr User-Agent too. If they don't match you will see the challenge.

3340
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "flaresolverr", "name": "flaresolverr",
"version": "2.2.2", "version": "2.2.4",
"description": "Proxy server to bypass Cloudflare protection.", "description": "Proxy server to bypass Cloudflare protection.",
"scripts": { "scripts": {
"start": "tsc && node ./dist/server.js", "start": "tsc && node ./dist/server.js",
@@ -20,26 +20,26 @@
}, },
"dependencies": { "dependencies": {
"await-timeout": "^1.1.1", "await-timeout": "^1.1.1",
"body-parser": "^1.19.0", "body-parser": "^1.20.0",
"console-log-level": "^1.4.1", "console-log-level": "^1.4.1",
"express": "^4.17.1", "express": "^4.17.3",
"puppeteer": "^13.1.2", "puppeteer": "^13.5.2",
"uuid": "^8.3.2" "uuid": "^8.3.2"
}, },
"devDependencies": { "devDependencies": {
"@types/await-timeout": "^0.3.1", "@types/await-timeout": "^0.3.1",
"@types/body-parser": "^1.19.1", "@types/body-parser": "^1.19.1",
"@types/express": "^4.17.13", "@types/express": "^4.17.13",
"@types/jest": "^27.0.2", "@types/jest": "^27.4.1",
"@types/node": "^16.11.7", "@types/node": "^16.11.27",
"@types/supertest": "^2.0.11", "@types/supertest": "^2.0.12",
"@types/uuid": "^8.3.1", "@types/uuid": "^8.3.1",
"archiver": "^5.3.0", "archiver": "^5.3.1",
"nodemon": "^2.0.13", "nodemon": "^2.0.13",
"pkg": "^5.5.2", "pkg": "^5.6.0",
"supertest": "^6.1.6", "supertest": "^6.1.6",
"ts-jest": "^27.0.7", "ts-jest": "^27.1.4",
"ts-node": "^10.3.0", "ts-node": "^10.7.0",
"typescript": "^4.4.4" "typescript": "^4.6.3"
} }
} }

View File

@@ -6,18 +6,23 @@ import log from "../services/log";
* This class contains the logic to solve protections provided by CloudFlare * This class contains the logic to solve protections provided by CloudFlare
**/ **/
const BAN_SELECTORS = ['.text-gray-600']; // the selector '.text-gray-600' is not working well because it can be hidden
const CHALLENGE_SELECTORS = [ // <span style="display: none;" class="text-gray-600" data-translate="error">error code: 1020</span>
const BAN_SELECTORS: string[] = [];
const CHALLENGE_SELECTORS: string[] = [
'#trk_jschal_js', '.ray_id', '.attack-box', '#cf-please-wait', // CloudFlare '#trk_jschal_js', '.ray_id', '.attack-box', '#cf-please-wait', // CloudFlare
'#link-ddg', // DDoS-GUARD '#link-ddg', // DDoS-GUARD
'td.info #js_info' // Custom CloudFlare for EbookParadijs, Film-Paleis, MuziekFabriek and Puur-Hollands 'td.info #js_info' // Custom CloudFlare for EbookParadijs, Film-Paleis, MuziekFabriek and Puur-Hollands
]; ];
const CAPTCHA_SELECTORS = ['input[name="cf_captcha_kind"]']; const CAPTCHA_SELECTORS: string[] = [
'input[name="cf_captcha_kind"]'
];
export default async function resolveChallenge(url: string, page: Page, response: HTTPResponse): Promise<HTTPResponse> { export default async function resolveChallenge(url: string, page: Page, response: HTTPResponse): Promise<HTTPResponse> {
// look for challenge and return fast if not detected // look for challenge and return fast if not detected
let cfDetected = response.headers().server && response.headers().server.startsWith('cloudflare'); let cfDetected = response.headers().server &&
(response.headers().server.startsWith('cloudflare') || response.headers().server.startsWith('ddos-guard'));
if (cfDetected) { if (cfDetected) {
if (response.status() == 403 || response.status() == 503) { if (response.status() == 403 || response.status() == 503) {
cfDetected = true; // Defected CloudFlare and DDoS-GUARD cfDetected = true; // Defected CloudFlare and DDoS-GUARD
@@ -37,14 +42,8 @@ export default async function resolveChallenge(url: string, page: Page, response
} }
if (await findAnySelector(page, BAN_SELECTORS)) { if (await findAnySelector(page, BAN_SELECTORS)) {
const errorCodeElem = await page.$(BAN_SELECTORS[0]);
if (errorCodeElem) {
let displayCSSProperty = await errorCodeElem.evaluate(el => (<HTMLElement>el).style.display);
if (displayCSSProperty !== 'none') {
throw new Error('Cloudflare has blocked this request. Probably your IP is banned for this site, check in your web browser.'); throw new Error('Cloudflare has blocked this request. Probably your IP is banned for this site, check in your web browser.');
} }
}
}
// find Cloudflare selectors // find Cloudflare selectors
let selectorFound = false; let selectorFound = false;

View File

@@ -139,7 +139,7 @@ async function gotoPage(params: V1Request, page: Page): Promise<HTTPResponse> {
response = await page.goto(params.url, {waitUntil: 'domcontentloaded', timeout: pageTimeout}); response = await page.goto(params.url, {waitUntil: 'domcontentloaded', timeout: pageTimeout});
} catch (e) { } catch (e) {
// retry // retry
response = await page.goto(params.url, {waitUntil: 'domcontentloaded', timeout: 2000}); response = await page.goto(params.url, {waitUntil: 'domcontentloaded', timeout: pageTimeout});
} }
if (params.method == 'POST') { if (params.method == 'POST') {

View File

@@ -15,7 +15,7 @@ const postUrl = "https://ptsv2.com/t/qv4j3-1634496523";
const cfUrl = "https://pirateiro.com/torrents/?search=harry"; const cfUrl = "https://pirateiro.com/torrents/?search=harry";
const cfCaptchaUrl = "https://idope.se" const cfCaptchaUrl = "https://idope.se"
const cfBlockedUrl = "https://www.torrentmafya.org/table.php" const cfBlockedUrl = "https://www.torrentmafya.org/table.php"
const ddgUrl = "https://www.erai-raws.info/feed/?type=magnet"; const ddgUrl = "https://anidex.info/";
const ccfUrl = "https://www.muziekfabriek.org"; const ccfUrl = "https://www.muziekfabriek.org";
beforeAll(async () => { beforeAll(async () => {
@@ -189,12 +189,12 @@ describe("Test '/v1' path", () => {
expect(solution.url).toContain(ddgUrl) expect(solution.url).toContain(ddgUrl)
expect(solution.status).toBe(200); expect(solution.status).toBe(200);
expect(Object.keys(solution.headers).length).toBeGreaterThan(0) expect(Object.keys(solution.headers).length).toBeGreaterThan(0)
expect(solution.response).toContain("<rss version") expect(solution.response).toContain("<!DOCTYPE html>")
expect(Object.keys(solution.cookies).length).toBeGreaterThan(0) expect(Object.keys(solution.cookies).length).toBeGreaterThan(0)
expect(solution.userAgent).toContain("Firefox/") expect(solution.userAgent).toContain("Firefox/")
const cfCookie: string = (solution.cookies as any[]).filter(function(cookie) { const cfCookie: string = (solution.cookies as any[]).filter(function(cookie) {
return cookie.name == "__ddg1"; return cookie.name == "__ddg1_";
})[0].value })[0].value
expect(cfCookie.length).toBeGreaterThan(10) expect(cfCookie.length).toBeGreaterThan(10)
}); });