commit 5fa84e31b90d77bf1cabc28479341d7645a43959 Author: OfficialDakari Date: Mon Apr 29 19:42:07 2024 +0500 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7c02654 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +./node_modules/ +node_modules/* +node_modules/** +./node_modules/* +./node_modules/** +package-lock.json \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..32621a1 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# FearlessVPN-NG +Новый FearlessVPN (старый скоро будет доступен) работает по UDP (старый работал по HTTPS, хоть через Cloudflare). + +# Как настроить +Мне лень всё расписывать, вроде всё понятно. + +# Как перенаправить весь трафик +На Linux надо делать что-то с `ip route`. Не шарю + +# Завершение +Самый понятный README. Потом переделаю. \ No newline at end of file diff --git a/client.yaml b/client.yaml new file mode 100644 index 0000000..2797e96 --- /dev/null +++ b/client.yaml @@ -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 \ No newline at end of file diff --git a/client/index.js b/client/index.js new file mode 100644 index 0000000..810619e --- /dev/null +++ b/client/index.js @@ -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(); \ No newline at end of file diff --git a/config.js b/config.js new file mode 100644 index 0000000..896501a --- /dev/null +++ b/config.js @@ -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 +}; \ No newline at end of file diff --git a/createUser.js b/createUser.js new file mode 100644 index 0000000..f8dda0b --- /dev/null +++ b/createUser.js @@ -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); \ No newline at end of file diff --git a/encryption.js b/encryption.js new file mode 100644 index 0000000..9190aee --- /dev/null +++ b/encryption.js @@ -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 +}; \ No newline at end of file diff --git a/genkey.js b/genkey.js new file mode 100644 index 0000000..c568978 --- /dev/null +++ b/genkey.js @@ -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'); + } +}; \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..d786fc6 --- /dev/null +++ b/index.js @@ -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/'); +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..5596dfe --- /dev/null +++ b/package.json @@ -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" + } +} diff --git a/server.yaml b/server.yaml new file mode 100644 index 0000000..2100b73 --- /dev/null +++ b/server.yaml @@ -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 diff --git a/server/index.js b/server/index.js new file mode 100644 index 0000000..38d18c6 --- /dev/null +++ b/server/index.js @@ -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); \ No newline at end of file