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.
- package/README.md +261 -1
- package/package.json +29 -6
- package/src/PuppyProxy.js +131 -0
- package/src/constants.js +82 -0
- package/src/database/DatabaseUtils.js +9 -0
- package/src/database/ProxyDatabase.js +77 -0
- package/src/index.d.ts +186 -0
- package/src/index.js +2 -0
- package/src/network/RequestClient.js +284 -0
- package/src/providers/IPVanish.js +36 -0
- package/src/providers/Scraper.js +144 -0
- package/src/utils.js +12 -0
- package/src/workers/collector.js +311 -0
|
@@ -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
|
+
}
|