-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
113 lines (105 loc) · 4.07 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { ChildProcess, exec } from 'child_process';
import { Resolver } from 'dns/promises';
import { Counter, fs, Logger } from '@hydrooj/utils';
const resolver = new Resolver();
resolver.setServers(['114.114.114.114', '8.8.8.8']);
const logger = new Logger('proxy');
const childrens: ChildProcess[] = [];
const fails = Counter();
const stop = {};
function createProxy(target: string, targetPort: string, local: string) {
if (stop[target]) return () => { };
const identifer = target + ':' + targetPort;
logger.info('Proxy to', identifer);
const child = exec(`ssh -tt -R 127.0.0.1:${targetPort}:${local} root@${target}.hydro.ac`);
childrens.push(child);
let token = '';
function onLineReceived(data: string) {
console.log(target + '/' + targetPort + ':' + data);
if (data.includes(token)) token = '';
if (data.includes('remote port forwarding failed for listen port')) {
logger.info(`Kill old ssh process on ${identifer}`);
child.stdin!.write(`fuser -k ${targetPort}/tcp\n`);
setTimeout(() => child.kill(), 500);
}
if (data.includes('Welcome')) {
fails[identifer] = 0;
logger.success(`Connected to ${identifer}`);
}
if (data.startsWith('2034324')) child.kill(); // nothing listening on port
}
function ondata(data: any) {
const lines = data.toString().split('\n').map((i) => i.trim()).filter((i) => i);
for (const line of lines) onLineReceived(line);
}
child.stderr!.on('data', ondata);
child.stdout!.on('data', ondata);
const interval = setInterval(() => {
if (token) child.kill();
else {
token = Math.random().toString();
child.stdin!.write(`echo ${token} && fuser ${targetPort}/tcp || echo $((114514+1919810))\n`);
}
}, 60000);
child.on('exit', (code, signal) => {
if (stop) return;
if (fails[identifer] % 10 === 9) global.sendMessage?.(`Proxy to ${identifer} failed after 10 retries.`);
fails[identifer]++;
childrens.splice(childrens.indexOf(child), 1);
logger.warn(`Proxy process to ${identifer} exited with code ${code}, signal ${signal}`);
clearInterval(interval);
setTimeout(() => createProxy(target, targetPort, local), 1000);
});
return () => {
stop[target] = true;
child.kill();
}
}
function initAllProxy(expr: string, target: string) {
stop[target] = false;
const tasks = expr.split(',');
const clean: (() => void)[] = [];
for (const task of tasks) {
const [l, r] = task.split('->');
clean.push(createProxy(target, l, r));
}
return () => {
for (const f of clean) f();
}
}
async function main(expr: string) {
expr = expr.trim();
const map = {};
const [[result]] = await resolver.resolveTxt('alias.hydro.ac');
let aliases = result.split(' ').filter(i => i);
console.log(aliases);
for (const r of aliases) map[r] = initAllProxy(expr, r);
setInterval(async () => {
try {
const [[result]] = await resolver.resolveTxt('alias.hydro.ac');
const newAliases = result.split(' ').filter(i => i);
console.log(newAliases);
for (const r of newAliases) {
if (map[r]) continue;
map[r] = initAllProxy(expr, r);
logger.info('New alias', r);
}
for (const r of aliases) {
if (newAliases.includes(r)) continue;
map[r]();
delete map[r];
logger.info('Alias removed', r);
}
aliases = newAliases;
} catch (e) {
logger.warn('Failed to resolve alias.hydro.ac');
}
}, 60000);
}
const alt = fs.existsSync('config') ? fs.readFileSync('config', 'utf-8') : '';
if (!process.argv[2] && !alt) logger.error("Usage: forward '2333->192.168.1.1:2333,2334->192.168.1.1:2444' (remote->local)");
else main(process.argv[2] || alt);
process.on('SIGINT', () => {
for (const child of childrens) child.kill();
process.exit();
});