puppyproxy 0.0.0-security → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,311 @@
1
+ import { parentPort } from 'worker_threads';
2
+ import fetch from 'node-fetch';
3
+ import WebSocket from 'ws';
4
+ import pLimit from 'p-limit';
5
+ import { SocksProxyAgent } from 'socks-proxy-agent';
6
+ import { HttpProxyAgent } from 'http-proxy-agent';
7
+ import { HttpsProxyAgent } from 'https-proxy-agent';
8
+ import { PROXY_SOURCES } from '../constants.js';
9
+ import scrambled from 'puppyscrambled';
10
+ import log from 'puppylog';
11
+ import { createBasicAgent } from '../utils.js';
12
+
13
+ //fuck it
14
+ process.on('unhandledRejection', () => {});
15
+ process.on('uncaughtException', () => {});
16
+
17
+ let config = {};
18
+ const wsUrl = 'wss://shellshock.io/matchmaker/';
19
+ const httpUrl = 'https://st1.lumidiagames.com';
20
+
21
+ /**
22
+ * Fetches and parses proxy lists from multiple public repositories
23
+ */
24
+ async function fetchAllProxies() {
25
+ let proxyList = [];
26
+
27
+ async function parseTraditionalList(url, kind) {
28
+ try {
29
+ const response = await fetch(`${url}?v=${Date.now()}`);
30
+ const txt = await response.text();
31
+ const proxies = txt.trim()
32
+ .split("\n")
33
+ .map(p => p.trim())
34
+ .filter(p => p.length > 0)
35
+ .map(p => p.includes("://") ? p : `${kind}${p}`);
36
+
37
+ proxyList.push(...proxies);
38
+ } catch (err) {
39
+ // Silently fail individual lists to keep the worker moving
40
+ }
41
+ }
42
+
43
+ const tasks = [];
44
+
45
+ // Add SOCKS5 sources
46
+ if (config.scraper.proxyTypes.includes("socks5")) PROXY_SOURCES.socks5.forEach(url => tasks.push(parseTraditionalList(url, "socks5://")));
47
+ // Add SOCKS4 sources
48
+ if (config.scraper.proxyTypes.includes("socks4")) PROXY_SOURCES.socks4.forEach(url => tasks.push(parseTraditionalList(url, "socks4://")));
49
+ // Add HTTP/S sources
50
+ if (config.scraper.proxyTypes.includes("http")) PROXY_SOURCES.http.forEach(url => tasks.push(parseTraditionalList(url, "http://")));
51
+ if (config.scraper.proxyTypes.includes("https")) PROXY_SOURCES.https.forEach(url => tasks.push(parseTraditionalList(url, "https://")));
52
+
53
+ await Promise.allSettled(tasks);
54
+
55
+ let uniqueProxies = [...new Set(scrambled.shuffleArray(proxyList))];
56
+
57
+ const limit = config.scraper?.limitProxies || 40000;
58
+ if (uniqueProxies.length > limit) {
59
+ uniqueProxies = uniqueProxies.slice(0, limit);
60
+ }
61
+
62
+ return uniqueProxies;
63
+ }
64
+
65
+ /**
66
+ * Verifies if a proxy is alive and meets the latency requirements
67
+ */
68
+ async function checkProxy(socksOrHttpProxy) {
69
+ const proxyRegex = /^(socks5|socks4|socks5h|http|https):\/\/(?:[^:@\s]+(?::[^:@\s]*)?@)?\d{1,3}(?:\.\d{1,3}){3}:\d{2,5}$/i;
70
+
71
+ if (!proxyRegex.test(socksOrHttpProxy)) {
72
+ // log.warning("Invalid proxy format:", socksOrHttpProxy);
73
+ return false;
74
+ }
75
+
76
+ const maxTimeAllowed = config.scraper.timeoutMax; //in seconds, lower for less proxies but higher quality
77
+
78
+ let method = "http"; // 'ws' or 'http' , ws requires LS dev to be running
79
+ // log.dim(socksOrHttpProxy)
80
+
81
+ if (socksOrHttpProxy.startsWith("http")) {
82
+ method = "http";
83
+ };
84
+
85
+ const timeoutMs = (config.scraper.timeoutPerProxy || 15) * 1e3;
86
+
87
+ if (method === "http") {
88
+ return new Promise(async (resolve) => {
89
+ try {
90
+ let agent = createBasicAgent(socksOrHttpProxy);
91
+ let timeStart = Date.now();
92
+
93
+ const controller = new AbortController();
94
+ setTimeout(() => controller.abort(), timeoutMs);
95
+
96
+ const response = await fetch(httpUrl, {
97
+ method: 'HEAD',
98
+ agent,
99
+ signal: controller.signal
100
+ });
101
+
102
+ let timeTaken = (Date.now() - timeStart)/1000;
103
+
104
+ if (response.status === 200) {
105
+ if (timeTaken > maxTimeAllowed) {
106
+ config.scraper.verbose && log.orange(`HTTP response returned for proxy: ${socksOrHttpProxy} in [${timeTaken.toFixed(2)}s]`);
107
+ socksOrHttpProxy = null;
108
+ } else {
109
+ config.scraper.verbose && log.success(`HTTP response returned for proxy: ${socksOrHttpProxy} in [${timeTaken.toFixed(2)}s]`);
110
+ };
111
+ resolve(socksOrHttpProxy);
112
+ } else {
113
+ resolve(null);
114
+ };
115
+ } catch (error) {
116
+ if (error.code === 'ECONNRESET') {
117
+ return resolve(null);
118
+ }
119
+ // console.error("Error checking proxy:", error);
120
+ resolve(null);
121
+ };
122
+ });
123
+ } else return new Promise(async (resolve) => {
124
+ try {
125
+ const agent = createBasicAgent(socksOrHttpProxy);
126
+ const options = { agent,
127
+ headers: {
128
+ 'accept-encoding': 'gzip, deflate, br, zstd',
129
+ 'accept-language': 'en-US,en;q=0.9',
130
+ 'cache-control': 'no-cache',
131
+ 'connection': 'Upgrade',
132
+ 'host': 'shellshock.io',
133
+ 'origin': 'https://shellshock.io',
134
+ 'pragma': 'no-cache',
135
+ 'sec-websocket-extensions': 'permessage-deflate; client_max_window_bits',
136
+ // 'sec-websocket-key': generateWebSocketKey(),
137
+ // 'sec-websocket-version': '13',
138
+ // 'upgrade': 'websocket',
139
+ 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36'
140
+ }
141
+ };
142
+
143
+ let func = ()=>{};
144
+
145
+ let timeout = setTimeout(() => {
146
+ func();
147
+ resolve(null);
148
+ }, timeoutMs);
149
+
150
+ // await new Promise(resolve => setTimeout(resolve, scrambled.getRandomInt(0, 15e3)));
151
+
152
+ const ws = new WebSocket(wsUrl, options);
153
+
154
+ func = ()=>ws.close();
155
+
156
+ let timeStart = Date.now();
157
+
158
+ ws.on('open', () => {
159
+ config.scraper.verbose && log.magenta(`Connected using proxy: ${socksProxy}`);
160
+ ws.send('{ "command": "regionList" }');
161
+ });
162
+
163
+ ws.on('message', (data) => {
164
+ clearTimeout(timeout);
165
+ const msg = JSON.parse(data.toString());
166
+ if (msg.command == "regionList") {
167
+ let timeTaken = (Date.now() - timeStart)/1000;
168
+ // ips.add(ip.ip);
169
+ ws.close();
170
+ if (timeTaken > maxTimeAllowed) {
171
+ config.scraper.verbose && log.orange(`Result returned for proxy: ${socksOrHttpProxy} in [${timeTaken.toFixed(2)}s]`);
172
+ socksOrHttpProxy = null;
173
+ } else {
174
+ config.scraper.verbose && log.success(`Result returned for proxy: ${socksOrHttpProxy} in [${timeTaken.toFixed(2)}s]`);
175
+ };
176
+ resolve(socksOrHttpProxy);
177
+ } else {
178
+ // log.dim(msg)
179
+ }
180
+ });
181
+
182
+ ws.on('error', (err) => {
183
+ clearTimeout(timeout);
184
+ // log.dim(`Error with proxy ${socksProxy}:`, err.message);
185
+ // log.error(err);
186
+ resolve(null);
187
+ });
188
+
189
+ ws.on('close', () => {
190
+ clearTimeout(timeout);
191
+ resolve(null);
192
+ });
193
+ } catch (error) {
194
+ // log.error("Error checking proxy:", error);
195
+ resolve(null);
196
+ };
197
+ });
198
+ };
199
+
200
+ async function getProxies() {
201
+ log.dim("Fetching all proxies from sources...");
202
+
203
+ let allProxies = await fetchAllProxies();
204
+
205
+ allProxies = scrambled.shuffleArray(allProxies);
206
+
207
+ if (allProxies.length === 0) {
208
+ log.dim("No proxies to test.");
209
+ return;
210
+ };
211
+
212
+ const checkCount = config.scraper?.maxProxiesToCheck || 4000;
213
+ log.dim(`Got ${allProxies.length} proxies. Testing up to ${checkCount} proxies of each type...`);
214
+ let time = Date.now();
215
+
216
+ async function testProxiesSpaced(type, proxies) {
217
+ const limit = pLimit(250);
218
+ let completed = 0;
219
+ let success = 0;
220
+ const total = proxies.length;
221
+ const start = Date.now();
222
+
223
+ const interval = setInterval(() => {
224
+ const elapsed = (Date.now() - start) / 1000;
225
+ const rate = completed / elapsed;
226
+ const remaining = total - completed;
227
+ const eta = rate > 0 ? remaining / rate : Infinity;
228
+
229
+ config.scraper.logStatus && log.boldGreen(
230
+ `✅ Checked ${completed}/${total} proxies ` +
231
+ `(${((completed / total) * 100).toFixed(1)}%) ` +
232
+ `| 🟢 Success: ${success} ` +
233
+ `| Elapsed: ${elapsed.toFixed(1)}s ` +
234
+ `| ETA: ${eta.toFixed(1)}s`
235
+ );
236
+ }, 5000);
237
+
238
+ const results = await Promise.all(
239
+ proxies.map(proxy =>
240
+ limit(() =>
241
+ checkProxy(proxy.replaceAll("\r", ""))
242
+ .then(result => {
243
+ if (result) success++;
244
+ return result;
245
+ })
246
+ .finally(() => completed++)
247
+ )
248
+ )
249
+ );
250
+
251
+ clearInterval(interval);
252
+ const duration = ((Date.now() - start) / 1000).toFixed(1);
253
+ log.dim(`🏁 Finished checking all ${total} ${type} proxies in ${duration}s. 🟢 Success: ${success}`);
254
+
255
+ return results;
256
+ }
257
+
258
+ const socksProxiesOnly = allProxies.filter(p => p.startsWith("socks")).slice(0, checkCount);
259
+ const httpProxiesOnly = allProxies.filter(p => p.startsWith("http")).slice(0, checkCount);
260
+
261
+ log.dim(`Will test ${socksProxiesOnly.length} SOCKS proxies and ${httpProxiesOnly.length} HTTP proxies. ATTENTION! This can take AGES!! Like up to 15 minutes or more depending on the amount of proxies and their quality.`);
262
+ log.dim("doing socks.", socksProxiesOnly.length);
263
+ const socksResults = await testProxiesSpaced("socks", socksProxiesOnly);
264
+ log.dim("finished socks. doing http.", httpProxiesOnly.length);
265
+ const httpResults = await testProxiesSpaced("http", httpProxiesOnly);
266
+ log.dim("finished http. continuing...");
267
+
268
+ const workingSocks = socksResults.filter(Boolean);
269
+ const workingHttp = httpResults.filter(Boolean);
270
+
271
+ log.dim(`✅ SOCKS proxies working: ${workingSocks.length} / ${socksProxiesOnly.length}`);
272
+ log.dim(`✅ HTTP(S) proxies working: ${workingHttp.length} / ${httpProxiesOnly.length}`);
273
+
274
+ let workingProxies = [...workingSocks, ...workingHttp];
275
+ log.dim(`\nTesting complete. Found ${workingProxies.length} working proxies. Total: ${allProxies.length}. ${((workingProxies.length/allProxies.length)*100).toFixed(2)}%`);
276
+ log.dim(`\nTime taken: ${(Date.now() - time)/1000}s`);
277
+ // log.dim("Working proxies:", workingProxies);
278
+ // log.dim("Unique IPs:", Array.from(ips));
279
+
280
+ workingProxies = scrambled.shuffleArray(workingProxies);
281
+
282
+ allProxies = null;
283
+ // ips.clear();
284
+
285
+ return workingProxies;
286
+ };
287
+
288
+ /**
289
+ * Main Thread Communication
290
+ */
291
+ if (parentPort) {
292
+ parentPort.on('message', async (msg) => {
293
+ const [cmd, payload] = msg;
294
+
295
+ if (cmd === "deploy") {
296
+ config = payload.config;
297
+
298
+ log.dim("Worker: Started.");
299
+ // const allProxies = await fetchAllProxies();
300
+ // log.dim(`Worker: Testing ${allProxies.length} proxies...`);
301
+
302
+ // const limit = pLimit(250); // Concurrent limit to prevent OS socket exhaustion
303
+ // const results = await Promise.all(
304
+ // allProxies.map(p => limit(() => checkProxy(p.replace("\r", ""))))
305
+ // );
306
+
307
+ // const workingProxies = results.filter(Boolean);
308
+ parentPort.postMessage(await getProxies());
309
+ }
310
+ });
311
+ }