initial commit

This commit is contained in:
OfficialDakari 2024-04-29 19:42:07 +05:00
commit 5fa84e31b9
12 changed files with 378 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
node_modules/
./node_modules/
node_modules/*
node_modules/**
./node_modules/*
./node_modules/**
package-lock.json

11
README.md Normal file
View File

@ -0,0 +1,11 @@
# FearlessVPN-NG
Новый FearlessVPN (старый скоро будет доступен) работает по UDP (старый работал по HTTPS, хоть через Cloudflare).
# Как настроить
Мне лень всё расписывать, вроде всё понятно.
# Как перенаправить весь трафик
На Linux надо делать что-то с `ip route`. Не шарю
# Завершение
Самый понятный README. Потом переделаю.

10
client.yaml Normal file
View File

@ -0,0 +1,10 @@
type: client
port: 5454
endpoint: 192.168.0.158
mtu: 1400
subnet: 10.3.8.0
len: 24
iv: aiD6UuDsVPJyKudkc_SZBUlH-MRzLaZROiQwD11irtI
globalKey: mcCOmjwTUF6jNFQkbTWHrwyOMA6KV7oRFrK8lIawT5U
algorithm: aes-256-cbc
privateKey: Wf7U1DzfrkkFs9OugXHauYqmgfyWVLwxXoskrOebObg

82
client/index.js Normal file
View File

@ -0,0 +1,82 @@
// 13 b4 37 XX XX XX XX > (pk) - Authenticated
// 13 37 > (pk) - I am <...>
// e7 5a 3d ed > (gk) - Authenticate yourself
// 55 44 e9 37 < (pk) - Forget me
// e7 5a 3d ea > (pk) - Forget me. OK
const dgram = require('dgram');
const net = require('net');
const { Tun } = require('tuntap2');
const { encrypt, decrypt } = require('../encryption');
const IP = require('ip-packet');
const sock = dgram.createSocket('udp4');
const tun = new Tun();
var established = false;
sock.on('message', (msg, info) => {
const pd = decrypt(msg, config.privateKey, config.iv, config.algorithm);
const gd = decrypt(msg, config.globalKey, config.iv, config.algorithm);
const dec = pd.length == 0 ? gd : pd;
if (dec.length == 4 && dec[0] == 0xe7 && dec[1] == 0x5a && dec[2] == 0x3d && dec[3] == 0xed) {
console.log(`Server asks to authenticate. Authenticating...`);
authenticateAndCheck();
return;
}
if (dec.length == 7 && dec[0] == 0x13 && dec[1] == 0xb4 && dec[2] == 0x37) {
const ip = dec.slice(3);
const strIp = ip.map(s => s.toString()).join('.');
console.log(`My IP: ${strIp}`);
tun.mtu = config.mtu;
tun.ipv4 = `${strIp}/${config.len}`;
tun.isUp = true;
tun.on('data', (d) => {
sock.send(
encrypt(
d,
config.privateKey,
config.iv,
config.algorithm
),
config.port,
config.endpoint
);
});
established = true;
}
if (established && (dec[0] >> 4 == 4)) {
tun.write(dec);
}
});
function authenticate() {
sock.send(
encrypt(
Buffer.from([
0x13, 0x37
]),
config.privateKey,
config.iv,
config.algorithm
),
config.port,
config.endpoint
);
}
function authenticateAndCheck() {
authenticate();
var i = setInterval(() => {
if (!established) {
console.log(`Connection not established. Authenticating again...`);
authenticate();
} else {
clearInterval(i);
console.log(`Authenticated.`);
}
}, 5000);
}
authenticateAndCheck();

15
config.js Normal file
View File

@ -0,0 +1,15 @@
const fs = require('fs');
const YAML = require('yaml');
function getConfig(file) {
return YAML.parse(fs.readFileSync(file, 'utf-8'));
}
function saveConfig(file, conf) {
fs.writeFileSync(file, YAML.stringify(conf));
}
module.exports = {
getConfig,
saveConfig
};

21
createUser.js Normal file
View File

@ -0,0 +1,21 @@
const { getConfig, saveConfig } = require("./config");
const genkey = require("./genkey");
const configFile = process.argv[2];
const config = getConfig(configFile);
if (config.type != 'server') {
console.error('Not server');
process.exit(-1);
}
const [ username, addr ] = process.argv.slice(3);
const user = {
username,
addr,
key: genkey.genkey()
};
config.users[username] = user;
saveConfig(configFile, config);

45
encryption.js Normal file
View File

@ -0,0 +1,45 @@
const crypto = require('crypto');
function encrypt(payload, key, secret_iv, algorithm) {
try {
const iv = crypto
.createHash('sha512')
.update(secret_iv)
.digest('hex')
.substring(0, 16)
const cipher = crypto.createCipheriv(algorithm, crypto
.createHash('sha512')
.update(key)
.digest('hex')
.substring(0, 32), iv);
let encrypted = cipher.update(payload, 'binary', 'binary');
encrypted += cipher.final('binary');
return Buffer.from(encrypted, 'binary');
} catch (error) {
return Buffer.from('');
}
}
function decrypt(payload, key, secret_iv, algorithm) {
try {
const iv = crypto
.createHash('sha512')
.update(secret_iv)
.digest('hex')
.substring(0, 16)
const decipher = crypto.createDecipheriv(algorithm, crypto
.createHash('sha512')
.update(key)
.digest('hex')
.substring(0, 32), iv);
let decrypted = decipher.update(payload, 'binary', 'binary');
decrypted += decipher.final('binary');
return Buffer.from(decrypted, 'binary');
} catch (error) {
return Buffer.from('');
}
}
module.exports = {
encrypt, decrypt
};

10
genkey.js Normal file
View File

@ -0,0 +1,10 @@
const md5 = require('md5');
const { v4 } = require('uuid');
console.log(Buffer.from(md5(v4()) + md5(v4()), 'hex').toString('base64url'));
module.exports = {
genkey: () => {
return Buffer.from(md5(v4()) + md5(v4()), 'hex').toString('base64url');
}
};

14
index.js Normal file
View File

@ -0,0 +1,14 @@
const { getConfig } = require("./config");
const [ nodejs, indexjs, configFile ] = process.argv;
const conf = getConfig(configFile);
globalThis.configFile = configFile;
globalThis.config = conf;
if (conf.type == 'server') {
require('./server/');
} else if (conf.type == 'client') {
require('./client/');
}

19
package.json Normal file
View File

@ -0,0 +1,19 @@
{
"name": "fearlessvpn_ng",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "OfficialDakari",
"license": "MIT",
"dependencies": {
"crystals-kyber": "^5.1.0",
"ip-packet": "^1.1.0",
"md5": "^2.3.0",
"tuntap2": "^0.9.1",
"uuid": "^9.0.1",
"yaml": "^2.4.2"
}
}

14
server.yaml Normal file
View File

@ -0,0 +1,14 @@
type: server
port: 5454
mtu: 1400
subnet: 10.3.8.0
len: 24
addr: 10.3.8.1
iv: aiD6UuDsVPJyKudkc_SZBUlH-MRzLaZROiQwD11irtI
globalKey: mcCOmjwTUF6jNFQkbTWHrwyOMA6KV7oRFrK8lIawT5U
algorithm: aes-256-cbc
users:
officialdakari:
username: officialdakari
addr: 10.3.8.2
key: Wf7U1DzfrkkFs9OugXHauYqmgfyWVLwxXoskrOebObg

130
server/index.js Normal file
View File

@ -0,0 +1,130 @@
// 13 b4 37 XX XX XX XX > (pk) - Authenticated
// 13 37 > (pk) - I am <...>
// e7 5a 3d ed > (gk) - Authenticate yourself
// 55 44 e9 37 < (pk) - Forget me
// e7 5a 3d ea > (pk) - Forget me. OK
const dgram = require('dgram');
const net = require('net');
const { Tun } = require('tuntap2');
const { encrypt, decrypt } = require('../encryption');
const IP = require('ip-packet');
const conns = {};
const ips = {};
const ports = {};
const sock = dgram.createSocket('udp4');
const l = new net.BlockList();
l.addSubnet(config.subnet, config.len);
const tun = new Tun();
tun.mtu = config.mtu;
tun.ipv4 = `${config.addr}/${config.len}`;
tun.isUp = true;
tun.on('data', (buff) => {
if (buff[0] >> 4 !== 4) return;
const p = IP.decode(buff);
//(p);
const user = Object.values(config.users).find(x => x.addr == p.destinationIp);
//(user);
if (!user) return;
const targetIp = ips[user.username];
const targetPort = ports[user.username];
//(targetIp, targetPort);
if (!targetIp || !targetPort) return;
sock.send(encrypt(IP.encode(p), user.key, config.iv, config.algorithm), targetPort, targetIp);
})
sock.on('listening', () => {
const a = sock.address();
console.log(`Listening on ${a.address}:${a.port}`);
});
sock.on('message', (msg, info) => {
if (!conns[info.address]) {
for (const uname in config.users) {
const u = config.users[uname];
const dec = decrypt(msg, u.key, config.iv, config.algorithm);
if (dec.length == 2 && dec[0] == 0x13 && dec[1] == 0x37) {
conns[info.address] = uname;
ips[uname] = info.address;
const spl = u.addr.split('.').map(s => parseInt(s));
const buff = Buffer.from([
0x13,
0xb4,
0x37,
...spl
]);
const enc = encrypt(buff, u.key, config.iv, config.algorithm);
sock.send(enc, info.port, info.address);
return;
}
}
const buff = Buffer.from([
0xe7,
0x5a,
0x3d,
0xed
]);
const enc = encrypt(buff, config.globalKey, config.iv, config.algorithm);
sock.send(enc, info.port, info.address);
return;
} else {
const uname = conns[info.address];
ports[uname] = info.port;
ips[uname] = info.address;
const acc = config.users[uname];
const dec = decrypt(msg, acc.key, config.iv, config.algorithm);
//(dec);
if (dec.length == 0) {
console.error(`Empty or malformed packet from ${uname}`);
return;
}
if (dec.length == 2 && dec[0] == 0x13 && dec[1] == 0x37) {
const spl = acc.addr.split('.').map(s => parseInt(s));
const buff = Buffer.from([
0x13,
0xb4,
0x37,
...spl
]);
const enc = encrypt(buff, acc.key, config.iv, config.algorithm);
sock.send(enc, info.port, info.address);
return;
}
if (dec.length == 4 &&
dec[0] == 0x55 && dec[1] == 0x44 && dec[2] == 0xe9 && dec[3] == 0x37) {
const buff = Buffer.from([
0xe7,
0x5a,
0x3d,
0xea
]);
const enc = encrypt(buff, acc.key, config.iv, config.algorithm);
sock.send(enc, info.port, info.address);
delete conns[info.address];
return;
}
if ((dec[0] >> 4) !== 4) return;
const packet = IP.decode(dec);
//(packet);
packet.sourceIp = acc.addr;
if ((config.allow_outbound && !l.check(packet.destinationIp)) || packet.destinationIp == config.addr) {
tun.write(dec);
return;
}
const user = Object.values(config.users).find(x => x.addr == packet.destinationIp);
if (!user) return;
const targetIp = conns[user.username];
const targetPort = ports[user.username];
if (!targetIp || !targetPort) return;
sock.send(encrypt(dec, user.key, config.iv, config.algorithm), targetPort, targetIp);
}
});
sock.bind(config.port);