diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 211c641..02901f7 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -29,6 +29,7 @@ const config = { }, ], "@typescript-eslint/no-misused-promises": "off", + "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-unused-vars": "off", "@next/next/no-img-element": "off", }, diff --git a/package-lock.json b/package-lock.json index a9b47c6..7c04a7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,18 @@ { - "name": "cloudflare-scanner", + "name": "kscanner", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "cloudflare-scanner", + "name": "kscanner", "version": "0.1.0", "dependencies": { "@heroicons/react": "^2.1.1", "@serwist/next": "^8.4.4", "@serwist/precaching": "^8.4.4", "@serwist/sw": "^8.4.4", + "axios": "^1.6.7", "next": "^14.1.0", "react": "18.2.0", "react-dom": "18.2.0", @@ -1623,6 +1624,11 @@ "has-symbols": "^1.0.3" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/autoprefixer": { "version": "10.4.18", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz", @@ -1684,6 +1690,16 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", + "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "dependencies": { + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -1926,6 +1942,17 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -2150,6 +2177,14 @@ "rimraf": "bin.js" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -3010,6 +3045,25 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -3034,6 +3088,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -4112,7 +4179,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "peer": true, "engines": { "node": ">= 0.6" } @@ -4121,7 +4187,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -4878,6 +4943,11 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/package.json b/package.json index 722fb51..df20472 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@serwist/next": "^8.4.4", "@serwist/precaching": "^8.4.4", "@serwist/sw": "^8.4.4", + "axios": "^1.6.7", "next": "^14.1.0", "react": "18.2.0", "react-dom": "18.2.0", diff --git a/public/css/style.css b/public/css/style.css index c28db3b..0860987 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -170,6 +170,21 @@ label { border-radius: 30px; } +.groupInput { + float: left; + width: 100%; +} +.groupInput label { + float: left; +} +.groupInput label:first-child { + width: calc( 100% - 110px); +} +.groupInput label:last-child { + width: 100px; + float: right; +} + footer { margin: 50px 0 20px 0; padding: 10px 0; diff --git a/src/hooks/axiosWithSNI.ts b/src/hooks/axiosWithSNI.ts new file mode 100644 index 0000000..99144f4 --- /dev/null +++ b/src/hooks/axiosWithSNI.ts @@ -0,0 +1,22 @@ +import axios from 'axios'; +import https from 'https'; + +const axiosWithSNI = (url: string, sni: string, signal: AbortSignal | undefined, timeout: number) => { + let agent = null; + if (sni !== '') { + agent = new https.Agent({ + servername: sni, + host: sni + }); + } + + return axios.create({ + baseURL: url, + method: "GET", + signal: signal, + httpsAgent: agent, + timeout: timeout + }); +}; + +export default axiosWithSNI; \ No newline at end of file diff --git a/src/hooks/useIPScanner.ts b/src/hooks/useIPScanner.ts index 03aa35b..b68fad0 100644 --- a/src/hooks/useIPScanner.ts +++ b/src/hooks/useIPScanner.ts @@ -1,6 +1,7 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; import { randomizeElements } from "~/helpers/randomizeElements"; +import axiosWithSNI from "./axiosWithSNI"; type ValidIP = { ip: string; @@ -15,6 +16,8 @@ export type Settings = { maxIPCount: number; maxLatency: number; ipRegex: string; + sniValue: string; + portValue: number; }; type SettingKeys = keyof Settings; @@ -55,6 +58,8 @@ export const settingsInitialValues: Pick = { maxIPCount: 5, maxLatency: 1000, ipRegex: "", + sniValue: "", + portValue: 80, }; const initialState: Omit = { @@ -158,10 +163,25 @@ export const useIPScanner = ({ allIps }: IPScannerProps) => { } } + const ports = { + http : [80, 8080, 2052, 2082, 2086, 2095], + https: [443, 8443, 2053, 2083, 2087, 2096], + }; + async function testIPs(ipList: string[]) { + let isSSL = false; + if (ports.https.includes(state.portValue)) { + isSSL = true; + } for (const ip of ipList) { increaseTestNo(); - const url = `http://${ip}/cdn-cgi/trace`; + + let url = `http://${ip}`; + let path = `/cdn-cgi/trace`; + if (state.sniValue !== '' && isSSL ) { + url = `https://${ip}`; + path = `/__down`; + } let testCount = 0; @@ -191,8 +211,18 @@ export const useIPScanner = ({ allIps }: IPScannerProps) => { } dispatch(newState); - try { + + const axiosInstance = axiosWithSNI(url, state.sniValue, controller.signal, timeout); + const response = await axiosInstance.get(path); + testCount++; + } catch (error) { + if (error instanceof Error && !["AbortError", "TypeError"].includes(error.name)) { + testCount++; + } + } + + /*try { await fetch(url, { signal: controller.signal, //mode: 'no-cors' @@ -203,7 +233,7 @@ export const useIPScanner = ({ allIps }: IPScannerProps) => { if (error instanceof Error && !["AbortError", "TypeError"].includes(error.name)) { testCount++; } - } + }*/ clearTimeout(timeoutId); } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index ca392e9..3e4981e 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -16,7 +16,7 @@ import { allIps } from "~/consts"; import { useUserIPInfo } from "~/hooks/useUserIPInfo"; import { toast } from "react-hot-toast"; import { useEffect, useState } from "react"; -import { useRouter } from 'next/router'; +//import { useRouter } from 'next/router'; const UserIP = dynamic(() => import("~/components/UserIP"), { ssr: false }); const Home: NextPage = () => { @@ -28,6 +28,8 @@ const Home: NextPage = () => { currentIP, currentLatency, ipRegex, + sniValue, + portValue, maxIPCount, maxLatency, scanState, @@ -148,6 +150,35 @@ const Home: NextPage = () => { className="form-control dirLeft" /> +
+ + +
{/*