Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

js2ray

Package Overview
Dependencies
Maintainers
1
Versions
53
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

js2ray - npm Package Compare versions

Comparing version 1.2.2 to 2.0.0

core/api.js

40

index.js
var protocols = {
vmess: require('./protocols/vmess')
}
const protocols = {}
protocols.vmess = require('./protocols/vmess')
const freedom = require('./protocols/freedom');
const bridge = require('./protocols/bridge');
const localNetwork = require('./localNetwork');
const remoteNetwork = require('./remoteNetwork');
const event = require('./event');
const localNetwork = require('./core/localNetwork');
const remoteNetwork = require('./core/remoteNetwork');
const event = require('./core/event');
const storage = require('./core/storage');
const api = require('./core/api');
storage.read()
function config(data) {

@@ -18,2 +19,4 @@ global.log = function (...mess) {

}
if (data.api)
api.init(data.api, protocols)
//===========================

@@ -25,3 +28,3 @@ if (data.bridge == true) {

throw ("do not use outbound protocol in bridge mode")
var remoteProtocol = bridge(data.outbound.network, remoteNetwork(data.outbound.network.type))
var remoteProtocol = bridge(data.outbound.networks, remoteNetwork(data.outbound.networks))
for (const i of data.inbounds) {

@@ -36,31 +39,20 @@ if (i.protocol)

throw ("inbound protocol is not defined")
localNetwork(i.networks, protocols[i.protocol].server(i, freedom))
localNetwork(i.networks, protocols[i.protocol].server(i, freedom, storage))
}
} else {
var remoteProtocol = protocols[data.outbound.protocol].client(data.outbound, remoteNetwork(data.outbound.network.type))
var remoteProtocol = protocols[data.outbound.protocol].client(data.outbound, remoteNetwork(data.outbound.networks))
for (const i of data.inbounds) {
if (!i.protocol || !(i.protocol in protocols))
throw ("inbound protocol is not defined")
localNetwork(i.networks, protocols[i.protocol].server(i, remoteProtocol))
localNetwork(i.networks, protocols[i.protocol].server(i, remoteProtocol, storage))
}
}
//===========================
return event;
}
function user() {
}
module.exports = {
config,
user,
on: event.on,
protocols
}
// const server = outboundInit(data.outbound, vmess)
// for (const i of data.inbounds) {
// if (i.protocol == "vmess")
// inbound(i, server)
// else
// throw ("inbound protocol '" + i.protocol + "' not supported")

4

package.json
{
"name": "js2ray",
"version": "1.2.2",
"version": "2.0.0",
"description": "The v2ray vmess protocol, based on nodejs javascript which you can use on hosts and servers",

@@ -29,7 +29,5 @@ "main": "index.js",

"dependencies": {
"cbor-js": "^0.1.0",
"cuckoo-filter": "^1.1.4",
"libsodium-wrappers": "^0.7.11",
"ws": "^8.12.0"
}
}
function init(data, remoteNetwork) {
function init(networks, remoteNetwork) {
const data = networks[Math.floor(Math.random() * networks.length)]
return function (localsocket) {

@@ -5,0 +6,0 @@ localsocket.pause();

const crypto = require('crypto');
const fs = require('fs');
const { setInterval } = require('timers');
const consts = require("./consts")
const storage = require("../../core/storage")
function New() {

@@ -73,2 +73,7 @@ return crypto.randomBytes(16)

// 2 'auto'
// 3 'aes-128-gcm'
// 4 'chacha20-poly1305'
// 5 'none'
// 6 'zero'

@@ -84,38 +89,46 @@ var securityTypes = {

var users = usesave()
var users = {}
function user(tag, inp, del) {
if (typeof inp == 'object') {
addUser(tag, a)
save()
} else if (typeof inp == "string") {
var sub_user = []
for (var i in usersp[tag]) {
if (usersp[tag][i].email == inp) {
sub_user.push(usersp[tag][i])
if (del)
usersp[tag].splice(i, 1)
}
function getUser(tag, id) {
if (typeof id == "string") {
return users[tag][id]
} else if (tag == undefined) {
return users;
} else if (id == undefined) {
return users[tag];
}
}
function getUserByEmail(tag, email) {
var sub_user = []
for (var i in users[tag]) {
if (users[tag][i].email == email) {
sub_user.push(users[tag][i])
if (del)
delete users[tag][i]
// users[tag].splice(i, 1)
}
return sub_user;
} else if (inp == undefined) {
return usersp[tag];
}
return sub_user;
}
function deleteUser(tag, id) {
users[tag][id].deactive = true
return delete users[tag][id]
}
function AsAccount(tag, data_user) {
if (tag == undefined)
tag = (Math.random() + 1).toString(36).substring(3)
function addUsers(tag, data_user) {
if (users[tag] == undefined)
users[tag] = []
users[tag] = {}
var users_ids = []
for (var i in users[tag]) {
users_ids.push(users[tag][i].id.UUID.toString())
// var users_ids = []
// for (var i in users[tag]) {
// users_ids.push(users[tag][i].id.UUID.toString())
// }
// for (var i of data_user) {
// if (!users_ids.includes(ParseString(i.id).toString())) {
// addUser(tag, i)
// }
// }
for (var item of data_user) {
addUser(tag, item)
}
for (var i of data_user) {
if (!users_ids.includes(ParseString(i.id).toString())) {
addUser(tag, i)
}
}
save()

@@ -126,3 +139,27 @@ return users[tag];

function addUser(tag, a) {
var protoID = NewID(ParseString(a.id))
const id = ParseString(a.id)
const idstr = id.toString()
if (idstr in users[tag]) {
const item = users[tag][idstr]
var exist = {
bytesRead: item.bytesRead,
bytesWrit: item.bytesWrit,
traffic: a.traffic || item.traffic,
ipCount: a.ipCount || item.ipCount,
ipCountDuration: a.ipCountDuration || item.ipCountDuration,
ipList: item.ipList
}
} else {
var exist = {
bytesRead: 0,
bytesWrit: 0,
traffic: a.traffic || 0,
ipCount: a.ipCount || 0,
ipCountDuration: a.ipCountDuration || 180,
ipList: {}
}
}
var protoID = NewID(id)
if (!a.alterId)

@@ -136,10 +173,5 @@ a.alterId = 0

security: securityTypes[a.security || 'auto'],
bytesRead: 0,
bytesWrit: 0,
traffic: a.traffic || 0,
ipCount: a.ipCount || 0,
ipCountDuration: a.ipCountDuration || 180,
ipList: {},
}
users[tag].push(user)
user = { ...exist, ...user }
users[tag][a.id] = user
return user

@@ -149,53 +181,30 @@ }

setInterval(save, 60000);
function usesave() {
return {}
try {
var usage = fs.readFileSync(__dirname + "/data.json");
if (usage == "") {
usage = {}
} else {
usage = JSON.parse(usage)
}
for (var i in usage) {
usage[i].id.UUID = Buffer.from(usage[i].id.UUID)
usage[i].id.cmdKey = Buffer.from(usage[i].id.cmdKey)
for (var j in usage[i].alterIDs) {
usage[i].alterIDs[j].UUID = Buffer.from(usage[i].alterIDs[j].UUID)
usage[i].alterIDs[j].cmdKey = Buffer.from(usage[i].alterIDs[j].cmdKey)
function getSavedUsers() {
if (!("vmess" in storage.data)) {
storage.data.vmess = {}
}
const users = storage.data.vmess
for (var j in users) {
for (var i in users[j]) {
users[j][i].id.UUID = Buffer.from(users[j][i].id.UUID)
users[j][i].id.cmdKey = Buffer.from(users[j][i].id.cmdKey)
for (var j in users[j][i].alterIDs) {
users[j][i].alterIDs[j].UUID = Buffer.from(users[j][i].alterIDs[j].UUID)
users[j][i].alterIDs[j].cmdKey = Buffer.from(users[j][i].alterIDs[j].cmdKey)
}
}
return usage;
} catch (error) {
console.error(error)
throw Error("read file error: ")
return {}
}
return users;
}
function save(e) {
try {
// fs.writeFileSync(__dirname + "/data.json", JSON.stringify(users));
} catch (error) {
if (!e === true)
save(true)
console.log("write file error")
}
function save() {
storage.write()
}
//=================================================================== before exit
process.stdin.resume();
function exitHandler(options, e) {
if (options.error) console.error(e)
if (options.cleanup) save();
if (options.exit) process.exit();
}
process.on('exit', exitHandler.bind(null, { cleanup: true }));
process.on('SIGINT', exitHandler.bind(null, { exit: true }));
process.on('SIGUSR1', exitHandler.bind(null, { exit: true }));
process.on('SIGUSR2', exitHandler.bind(null, { exit: true }));
process.on('uncaughtException', exitHandler.bind(null, { exit: true, error: true }));
module.exports = {
AsAccount,
user,
addUsers,
getSavedUsers,
getUser,
getUserByEmail,
deleteUser,
New,

@@ -202,0 +211,0 @@ tostring,

@@ -6,3 +6,2 @@

const consts = require("./consts")
const AdvancedBuffer = require("./advanced-buffer")

@@ -28,3 +27,3 @@ const kdf = require("./kdf")

app._atyp = type;
app._host = (type === ATYP_DOMAIN) ? Buffer.from(address) : toBuffer(address);
app._host = (type === ATYP_DOMAIN) ? Buffer.from(address) : common.iptoBuffer(address);

@@ -39,8 +38,9 @@

app._adBuf.on('data', DecodeResponseBody);
console.log(data)
const network = data.networks[Math.floor(Math.random() * data.networks.length)]
var socket = remoteNetwork(
data.network.address,
data.network.port,
data.network.option,
network.address,
network.port,
network.option,
localConnect,

@@ -89,7 +89,11 @@ DecodeResponseHeader.bind(app),

app.user.bytesRead += buffer.length
const chunks = getChunks(buffer, 0x3fff).map(resolveChunk.bind(app));
const chunks = common.getChunks(buffer, 0x3fff).map(resolveChunk.bind(app));
this.message(Buffer.concat([header, ...chunks]))
} else {
if (app.user.deactive)
return
if (app.user.traffic != 0 && common.trafficlimit(app.user))
return log(`maximum traffic ${app.user.traffic / 1024 / 1024}MB (bytesRead:${app.user.bytesRead},bytesWrit:${app.user.bytesWrit}) used by user ${app.user.id.UUID.toString("hex")}`);
app.user.bytesRead += buffer.length
const chunks = getChunks(buffer, 0x3fff).map(resolveChunk.bind(app));
const chunks = common.getChunks(buffer, 0x3fff).map(resolveChunk.bind(app));
this.message(Buffer.concat(chunks))

@@ -105,2 +109,5 @@ }

if (random_user.traffic != 0 && common.trafficlimit(random_user)) {
return log(`maximum traffic ${random_user.traffic / 1024 / 1024}MB (bytesRead:${random_user.bytesRead},bytesWrit:${random_user.bytesWrit}) used by user ${random_user.id.UUID.toString("hex")}`)
}
// IV and Key for data chunks encryption/decryption

@@ -113,2 +120,6 @@ app._dataEncIV = rands.subarray(0, 16);

app._dataDecKey = common.hash('sha256', app._dataEncKey).subarray(0, 16);
app.lengthEnKey = kdf.KDF16(app._dataDecKey, consts.KDFSaltConstAEADRespHeaderLenKey)
app.lengthEnIV = kdf.KDF(app._dataDecIV, consts.KDFSaltConstAEADRespHeaderLenIV).subarray(0, 12)
app.payloadEnKey = kdf.KDF16(app._dataDecKey, consts.KDFSaltConstAEADRespHeaderPayloadKey)
app.payloadEnIV = kdf.KDF(app._dataDecIV, consts.KDFSaltConstAEADRespHeaderPayloadIV).subarray(0, 12)
} else {

@@ -119,5 +130,2 @@ app._dataDecIV = common.hash('md5', app._dataEncIV);

app._dataEncKeyForChaCha20 = common.createChacha20Poly1305Key(app._dataEncKey);
app._dataDecKeyForChaCha20 = common.createChacha20Poly1305Key(app._dataDecKey);
app._chunkLenEncMaskGenerator = common.shake128(app._dataEncIV);

@@ -135,2 +143,6 @@ app._chunkLenDecMaskGenerator = common.shake128(app._dataDecIV);

if (app._security == consts.SECURITY_TYPE_CHACHA20_POLY1305) {
app._dataEncKeyForChaCha20 = common.createChacha20Poly1305Key(app._dataEncKey);
app._dataDecKeyForChaCha20 = common.createChacha20Poly1305Key(app._dataDecKey);
}
// create encrypted command

@@ -184,7 +196,10 @@ let command = Buffer.from([

var header = decipher.update(buffer.subarray(0, 4));
if (app._v !== header[0]) {
return fail(`server response v doesn't match, expect ${app._v} but got ${header[0]}`);
}
app._isHeaderRecv = true;
return app._adBuf.put(buffer.subarray(4 + header[3]), app);
} else {
const aeadResponseHeaderLengthEncryptionKey = kdf.KDF16(app._dataDecKey, consts.KDFSaltConstAEADRespHeaderLenKey)
const aeadResponseHeaderLengthEncryptionIV = kdf.KDF(app._dataDecIV, consts.KDFSaltConstAEADRespHeaderLenIV).subarray(0, 12)
const decipher = crypto.createDecipheriv('aes-128-gcm', aeadResponseHeaderLengthEncryptionKey, aeadResponseHeaderLengthEncryptionIV);
const decipher = crypto.createDecipheriv('aes-128-gcm', app.lengthEnKey, app.lengthEnIV);

@@ -199,8 +214,4 @@ const aeadEncryptedResponseHeaderLength = buffer.subarray(0, 2)

const aeadResponseHeaderPayloadEncryptionKey = kdf.KDF16(app._dataDecKey, consts.KDFSaltConstAEADRespHeaderPayloadKey)
const aeadResponseHeaderPayloadEncryptionIV = kdf.KDF(app._dataDecIV, consts.KDFSaltConstAEADRespHeaderPayloadIV).subarray(0, 12)
const decipher2 = crypto.createDecipheriv('aes-128-gcm', aeadResponseHeaderPayloadEncryptionKey, aeadResponseHeaderPayloadEncryptionIV);
const decipher2 = crypto.createDecipheriv('aes-128-gcm', app.payloadEnKey, app.payloadEnIV);
const encryptedResponseHeaderBuffer = buffer.subarray(18, decryptedResponseHeaderLength)

@@ -212,2 +223,8 @@ const encryptedResponseHeaderBufferTag = buffer.subarray(decryptedResponseHeaderLength, decryptedResponseHeaderLength + 16)

var header = Buffer.concat([decipher2.update(encryptedResponseHeaderBuffer), decipher2.final()])
if (app._v !== header[0]) {
return fail(`server response v doesn't match, expect ${app._v} but got ${header[0]}`);
}
app._isHeaderRecv = true;
return app._adBuf.put(buffer.subarray(decryptedResponseHeaderLength + 16 + header[3]), app);
}

@@ -217,10 +234,4 @@ } catch (error) {

}
if (app._v !== header[0]) {
return fail(`server response v doesn't match, expect ${app._v} but got ${header[0]}`);
}
app._isHeaderRecv = true;
return app._adBuf.put(buffer.subarray(4 + header[3]), app);
}
app._adBuf.put(buffer, app);
return app._adBuf.put(buffer, app);
}

@@ -242,4 +253,2 @@ function DecodeResponseBody(chunk, app) {

// ================================================= functions

@@ -256,3 +265,2 @@ function resolveChunk(chunk) {

}
console.log(10, this._option, _len)
return Buffer.concat([common.ntb(_len), _chunk]);

@@ -270,80 +278,6 @@ }

}
console.log(20,app._option, len)
return 2 + len;
}
function getChunks(buffer, maxSize) {
const totalLen = buffer.length;
const bufs = [];
let ptr = 0;
while (ptr < totalLen - 1) {
bufs.push(buffer.subarray(ptr, ptr + maxSize));
ptr += maxSize;
}
if (ptr < totalLen) {
bufs.push(buffer.subarray(ptr));
}
return bufs;
}
const ipv4Regex = /^(\d{1,3}\.){3,3}\d{1,3}$/;
const ipv6Regex = /^(::)?(((\d{1,3}\.){3}(\d{1,3}){1})?([0-9a-f]){0,4}:{0,2}){1,8}(::)?$/i;
function toBuffer(ip, buff, offset) {
offset = ~~offset;
let result;
if (ipv4Regex.test(ip)) {
result = buff || Buffer.alloc(offset + 4);
ip.split(/\./g).map((byte) => {
result[offset++] = parseInt(byte, 10) & 0xff;
});
} else if (ipv6Regex.test(ip)) {
const sections = ip.split(':', 8);
let i;
for (i = 0; i < sections.length; i++) {
const isv4 = ipv4Regex.test(sections[i]);
let v4Buffer;
if (isv4) {
v4Buffer = this.toBuffer(sections[i]);
sections[i] = v4Buffer.subarray(0, 2).toString('hex');
}
if (v4Buffer && ++i < 8) {
sections.splice(i, 0, v4Buffer.subarray(2, 4).toString('hex'));
}
}
if (sections[0] === '') {
while (sections.length < 8) sections.unshift('0');
} else if (sections[sections.length - 1] === '') {
while (sections.length < 8) sections.push('0');
} else if (sections.length < 8) {
for (i = 0; i < sections.length && sections[i] !== ''; i++);
const argv = [i, 1];
for (i = 9 - sections.length; i > 0; i--) {
argv.push('0');
}
sections.splice(...argv);
}
result = buff || Buffer.alloc(offset + 16);
for (i = 0; i < sections.length; i++) {
const word = parseInt(sections[i], 16);
result[offset++] = (word >> 8) & 0xff;
result[offset++] = word & 0xff;
}
}
if (!result) {
throw Error(`Invalid ip address: ${ip}`);
}
return result;
};
function getAddrType(host) {

@@ -350,0 +284,0 @@ if (net.isIPv4(host)) {

@@ -5,2 +5,3 @@

const consts = require("./consts")
const event = require("./../../core/event")

@@ -69,4 +70,3 @@ function uint64ToBuffer(uint64) {

const libsodium = require('libsodium-wrappers');
libsodium.ready
function encrypt(plaintext, app) {

@@ -119,3 +119,3 @@ const security = app._security;

return plaintext;
} catch (err) {
} catch (err) {
return null;

@@ -126,2 +126,132 @@ }

function getChunks(buffer, maxSize) {
const totalLen = buffer.length;
const bufs = [];
let ptr = 0;
while (ptr < totalLen - 1) {
bufs.push(buffer.subarray(ptr, ptr + maxSize));
ptr += maxSize;
}
if (ptr < totalLen) {
bufs.push(buffer.subarray(ptr));
}
return bufs;
}
// ================================================= utils
function trafficlimit(user) {
if (user.bytesRead + user.bytesWrit >= user.traffic) {
if (!user.maxtraffic) {
event.emit("traffic", user.id.UUID)
user.maxtraffic = true
}
return true;
}
}
function iplimit(ip, user) {
var now = Math.round(new Date() / 1000)
if (!(ip in user.ipList)) {
if (Object.keys(user.ipList).length >= user.ipCount) {
for (const i in user.ipList) {
if (user.ipList[i] + user.ipCountDuration < now) {
delete user.ipList[i]
}
}
if (Object.keys(user.ipList).length >= user.ipCount) {
if (!user.maxip) {
event.emit("ip", user.id.UUID)
user.maxip = true
}
return true;
}
}
if (user.maxip)
user.maxip = false
}
user.ipList[ip] = now
}
function iptoString(buff, offset, length) {
offset = ~~offset;
length = length || (buff.length - offset);
var result = [];
var i;
if (length === 4) {
// IPv4
for (i = 0; i < length; i++) {
result.push(buff[offset + i]);
}
result = result.join('.');
} else if (length === 16) {
// IPv6
for (i = 0; i < length; i += 2) {
result.push(buff.readUInt16BE(offset + i).toString(16));
}
result = result.join(':');
result = result.replace(/(^|:)0(:0)*:0(:|$)/, '$1::$3');
result = result.replace(/:{3,4}/, '::');
}
return result;
};
const ipv4Regex = /^(\d{1,3}\.){3,3}\d{1,3}$/;
const ipv6Regex = /^(::)?(((\d{1,3}\.){3}(\d{1,3}){1})?([0-9a-f]){0,4}:{0,2}){1,8}(::)?$/i;
function iptoBuffer(ip, buff, offset) {
offset = ~~offset;
let result;
if (ipv4Regex.test(ip)) {
result = buff || Buffer.alloc(offset + 4);
ip.split(/\./g).map((byte) => {
result[offset++] = parseInt(byte, 10) & 0xff;
});
} else if (ipv6Regex.test(ip)) {
const sections = ip.split(':', 8);
let i;
for (i = 0; i < sections.length; i++) {
const isv4 = ipv4Regex.test(sections[i]);
let v4Buffer;
if (isv4) {
v4Buffer = this.toBuffer(sections[i]);
sections[i] = v4Buffer.subarray(0, 2).toString('hex');
}
if (v4Buffer && ++i < 8) {
sections.splice(i, 0, v4Buffer.subarray(2, 4).toString('hex'));
}
}
if (sections[0] === '') {
while (sections.length < 8) sections.unshift('0');
} else if (sections[sections.length - 1] === '') {
while (sections.length < 8) sections.push('0');
} else if (sections.length < 8) {
for (i = 0; i < sections.length && sections[i] !== ''; i++);
const argv = [i, 1];
for (i = 9 - sections.length; i > 0; i--) {
argv.push('0');
}
sections.splice(...argv);
}
result = buff || Buffer.alloc(offset + 16);
for (i = 0; i < sections.length; i++) {
const word = parseInt(sections[i], 16);
result[offset++] = (word >> 8) & 0xff;
result[offset++] = word & 0xff;
}
}
if (!result) {
throw Error(`Invalid ip address: ${ip}`);
}
return result;
};
module.exports = {

@@ -135,5 +265,10 @@ ntb,

encrypt,
decrypt
decrypt,
getChunks,
iptoString,
iptoBuffer,
trafficlimit,
iplimit
}
const account = require("./account")
const validator = require("./validator")
module.exports = {
server: require('./server'),
client: require('./client'),
user: account.user
client: require('./client'),
api: {
getUserByEmail: validator.getUserByEmail,
getUsers: validator.getUser,
addUsers: validator.addUsers,
removeUser: validator.removeUser
}
}

@@ -6,3 +6,2 @@

const kdf = require("./kdf")
const event = require("../../event")
const consts = require("./consts")

@@ -41,3 +40,2 @@ const crypto = require("crypto");

function DecodeRequestHeader(remoteProtocol, onRemoteMessage, onRemoteClose, checkuser, buffer) {
const app = this.app

@@ -82,6 +80,6 @@ if (!app._isHeaderRecv) {

}
if (aeadUser.ipCount != 0 && iplimit(app, aeadUser))
if (aeadUser.ipCount != 0 && common.iplimit(app.ip, aeadUser))
return log(`maximum ip used by user ${aeadUser.id.UUID.toString("hex")}`)
if (aeadUser.traffic != 0 && trafficlimit(aeadUser)) {
if (aeadUser.traffic != 0 && common.trafficlimit(aeadUser)) {
return log(`maximum traffic ${aeadUser.traffic / 1024 / 1024}MB (bytesRead:${aeadUser.bytesRead},bytesWrit:${aeadUser.bytesWrit}) used by user ${aeadUser.id.UUID.toString("hex")}`)

@@ -106,2 +104,6 @@ }

app._dataEncKey = common.hash('sha256', app._dataDecKey).subarray(0, 16);
app.lengthEnKey = kdf.KDF16(app._dataEncKey, consts.KDFSaltConstAEADRespHeaderLenKey)
app.lengthEnIV = kdf.KDF(app._dataEncIV, consts.KDFSaltConstAEADRespHeaderLenIV).subarray(0, 12)
app.payloadEnKey = kdf.KDF16(app._dataEncKey, consts.KDFSaltConstAEADRespHeaderPayloadKey)
app.payloadEnIV = kdf.KDF(app._dataEncIV, consts.KDFSaltConstAEADRespHeaderPayloadIV).subarray(0, 12)
} else {

@@ -111,4 +113,7 @@ app._dataEncIV = common.hash('md5', app._dataDecIV);

}
app._chunkLenDecMaskGenerator = common.shake128(app._dataDecIV);
app._chunkLenEncMaskGenerator = common.shake128(app._dataEncIV);
app._responseHeader = reqHeader[33];

@@ -126,2 +131,6 @@ app._option = reqHeader[34];

}
if (securityType == consts.SECURITY_TYPE_CHACHA20_POLY1305) {
app._dataEncKeyForChaCha20 = common.createChacha20Poly1305Key(app._dataEncKey);
app._dataDecKeyForChaCha20 = common.createChacha20Poly1305Key(app._dataDecKey);
}
//===========

@@ -184,3 +193,3 @@ const cmd = reqHeader[37];

app.remote = remoteProtocol(
addrType === ATYP_DOMAIN ? addr.toString() : iptoString(addr),
addrType === ATYP_DOMAIN ? addr.toString() : common.iptoString(addr),
port,

@@ -200,3 +209,5 @@ cmd,

} else {
if (app.user.traffic != 0 && trafficlimit(app.user))
if (app.user.deactive)
return
if (app.user.traffic != 0 && common.trafficlimit(app.user))
return log(`maximum traffic ${app.user.traffic / 1024 / 1024}MB (bytesRead:${app.user.bytesRead},bytesWrit:${app.user.bytesWrit}) used by user ${app.user.id.UUID.toString("hex")}`);

@@ -229,18 +240,10 @@ app._adBuf.put(buffer, app);

} else {
const aeadResponseHeaderLengthEncryptionKey = kdf.KDF16(app._dataEncKey, consts.KDFSaltConstAEADRespHeaderLenKey)
const aeadResponseHeaderLengthEncryptionIV = kdf.KDF(app._dataEncIV, consts.KDFSaltConstAEADRespHeaderLenIV).subarray(0, 12)
const aeadResponseHeaderLengthEncryptionBuffer = Buffer.alloc(2);
aeadResponseHeaderLengthEncryptionBuffer.writeUInt16BE(outBuffer.length)
const cipher = crypto.createCipheriv('aes-128-gcm', aeadResponseHeaderLengthEncryptionKey, aeadResponseHeaderLengthEncryptionIV);
const cipher = crypto.createCipheriv('aes-128-gcm', app.lengthEnKey, app.lengthEnIV);
var AEADEncryptedLength = Buffer.concat([cipher.update(aeadResponseHeaderLengthEncryptionBuffer), cipher.final(), cipher.getAuthTag()])
const cipher2 = crypto.createCipheriv('aes-128-gcm', app.payloadEnKey, app.payloadEnIV);
const aeadResponseHeaderPayloadEncryptionKey = kdf.KDF16(app._dataEncKey, consts.KDFSaltConstAEADRespHeaderPayloadKey)
const aeadResponseHeaderPayloadEncryptionIV = kdf.KDF(app._dataEncIV, consts.KDFSaltConstAEADRespHeaderPayloadIV).subarray(0, 12)
const cipher2 = crypto.createCipheriv('aes-128-gcm', aeadResponseHeaderPayloadEncryptionKey, aeadResponseHeaderPayloadEncryptionIV);
var aeadEncryptedHeaderPayload = Buffer.concat([cipher2.update(outBuffer), cipher2.final(), cipher2.getAuthTag()])

@@ -256,10 +259,11 @@ return Buffer.concat([AEADEncryptedLength, aeadEncryptedHeaderPayload])

const app = this.app
app.user.bytesRead += buffer.length
if (!app._isHeaderSent) {
app._isHeaderSent = true;
const header = EncodeResponseHeader(app)
const chunks = getChunks(buffer, 0x3fff).map(resolveChunk.bind(app));
app.user.bytesRead += buffer.length
const chunks = common.getChunks(buffer, 0x3fff).map(resolveChunk.bind(app));
this.localMessage(Buffer.concat([header, ...chunks]))
} else {
const chunks = getChunks(buffer, 0x3fff).map(resolveChunk.bind(app));
app.user.bytesRead += buffer.length
const chunks = common.getChunks(buffer, 0x3fff).map(resolveChunk.bind(app));
this.localMessage(Buffer.concat(chunks))

@@ -279,3 +283,2 @@ }

}
console.log(10, this._option, _len)
return Buffer.concat([common.ntb(_len), _chunk]);

@@ -290,10 +293,8 @@ }

if (app._option >= 0x05) {
var mask = app._chunkLenDecMaskGenerator.nextBytes(2).readUInt16BE(0);
const mask = app._chunkLenDecMaskGenerator.nextBytes(2).readUInt16BE(0);
len = mask ^ len;
}
console.log(20, app._option, len)
return 2 + len;
}
function onclose() {

@@ -322,77 +323,2 @@ if (this.app.remote)

// ================================================= sender
function getChunks(buffer, maxSize) {
const totalLen = buffer.length;
const bufs = [];
let ptr = 0;
while (ptr < totalLen - 1) {
bufs.push(buffer.subarray(ptr, ptr + maxSize));
ptr += maxSize;
}
if (ptr < totalLen) {
bufs.push(buffer.subarray(ptr));
}
return bufs;
}
// ================================================= utils
function trafficlimit(user) {
if (user.bytesRead + user.bytesWrit > user.traffic) {
if (!user.maxtraffic) {
event.emit("traffic", user.id.UUID)
user.maxtraffic = true
}
return true;
}
}
function iplimit(app, user) {
var now = Math.round(new Date() / 1000)
if (!(app.ip in user.ipList)) {
if (Object.keys(user.ipList).length >= user.ipCount) {
for (const i in user.ipList) {
if (user.ipList[i] + user.ipCountDuration < now) {
delete user.ipList[i]
}
}
if (Object.keys(user.ipList).length >= user.ipCount) {
if (!user.maxip) {
event.emit("ip", user.id.UUID)
user.maxip = true
}
return true;
}
}
if (user.maxip)
user.maxip = false
}
user.ipList[app.ip] = now
}
function iptoString(buff, offset, length) {
offset = ~~offset;
length = length || (buff.length - offset);
var result = [];
var i;
if (length === 4) {
// IPv4
for (i = 0; i < length; i++) {
result.push(buff[offset + i]);
}
result = result.join('.');
} else if (length === 16) {
// IPv6
for (i = 0; i < length; i += 2) {
result.push(buff.readUInt16BE(offset + i).toString(16));
}
result = result.join(':');
result = result.replace(/(^|:)0(:0)*:0(:|$)/, '$1::$3');
result = result.replace(/:{3,4}/, '::');
}
return result;
};
module.exports = init
const crypto = require("crypto"),
common = require("./common"),
_accont = require("./account"),
account = require("./account"),
aead = require('./aead'),
antireplay = require('./replayfilter');
const tuvs = {}
function init(users_config) {
var users = []
var AuthIDDecoderHolder = {
decoders: {},
filter: antireplay.NewReplayFilter(120)
if (users_config.tag == undefined)
users_config.tag = (Math.random() + 1).toString(36).substring(3)
const tuv = {
users: [],
userHash: {},
baseTime: unix() - cacheDurationSec * 2,
aeadDecoderHolder: {
decoders: {},
filter: antireplay.NewReplayFilter(120)
},
}
tuv.task = periodic(updateUserHash.bind(tuv), updateInterval)
tuvs[users_config.tag] = tuv
var nowSec = unix()
addUsers(users_config.tag, users_config.users)
return {
get: getRandomUser.bind(tuv),
check: checkUser.bind(tuv),
}
}
///====================================================
for (const user of _accont.AsAccount(users_config.tag, users_config.users)) {
function addUsers(tag, users) {
const tuv = tuvs[tag]
for (const user of Object.values(account.addUsers(tag, users))) {
if (user.alterIDs.length == 0) {
aead.AddUser(AuthIDDecoderHolder, user.id.cmdKey, user)
aead.AddUser(tuv.aeadDecoderHolder, user.id.cmdKey, user)
} else {
users.push({
tuv.users.push({
user,
lastSec: (nowSec - cacheDurationSec),
lastSec: (unix() - cacheDurationSec),
})
}
}
return NewTimedUserValidator(AuthIDDecoderHolder, users);
checkUserHash(tuv)
}
function Close() {
return v.task.close()
function removeUser(tag, id) {
const tuv = tuvs[tag]
const user = account.getUser(tag, id)
if (user) {
account.deleteUser(tag, id)
if (user.alterIDs.length == 0)
aead.RemoveUser(tuv.aeadDecoderHolder, user.id.cmdKey)
else {
for (key in v.userHash) {
if (v.userHash[key].user.user.id.UUID == id) {
delete v.userHash[key]
}
}
}
checkUserHash(tuv)
return true;
}
return false
}
///====================================================
function checkUserHash(tuv) {
if (tuv.users.length != 0) {
tuv.task.start()
} else {
tuv.userHash = {}
tuv.task.close()
}
}
function getRandomUser() {
const keys = Object.keys(this.aeadDecoderHolder.decoders)
if (keys.length != 0) {
const key = keys[Math.floor(Math.random() * keys.length)]
const cacheItem = this.aeadDecoderHolder.decoders[key]
return [true, cacheItem.ticket]
// return [true, cacheItem.ticket, Buffer.from(key, "hex")]
} else {
const keys = Object.keys(this.userHash)
const key = keys[Math.floor(Math.random() * keys.length)]
const cacheItem = this.userHash[key]
return [false, cacheItem.user.user, Buffer.from(key, "hex"), common.uint64ToBuffer(cacheItem.timeInc + this.baseTime)]
}
}
function checkUser(authInfo, isaead) {
if (isaead)
return aead.Match(this.aeadDecoderHolder, authInfo)
else if (authInfo in this.userHash) {
const cacheItem = this.userHash[authInfo]
return [cacheItem.user.user, common.uint64ToBuffer(cacheItem.timeInc + this.baseTime)]
}
}
const updateInterval = 10

@@ -41,41 +103,2 @@ const cacheDurationSec = 120

function NewTimedUserValidator(AuthIDDecoderHolder, users) {
const tuv = {
users: users,
userHash: {},
baseTime: unix() - cacheDurationSec * 2,
aeadDecoderHolder: AuthIDDecoderHolder
}
if (users.length != 0) {
tuv.task = periodic(function () {
updateUserHash(tuv)
}, updateInterval)
tuv.task.start()
}
return {
get: function () {
const keys = Object.keys(tuv.aeadDecoderHolder.decoders)
if (keys.length != 0) {
const key = keys[Math.floor(Math.random() * keys.length)]
const cacheItem = tuv.aeadDecoderHolder.decoders[key]
return [true, cacheItem.ticket]
// return [true, cacheItem.ticket, Buffer.from(key, "hex")]
} else {
const keys = Object.keys(tuv.userHash)
const key = keys[Math.floor(Math.random() * keys.length)]
const cacheItem = tuv.userHash[key]
return [false, cacheItem.user.user, Buffer.from(key, "hex"), common.uint64ToBuffer(cacheItem.timeInc + tuv.baseTime)]
}
},
check: function (authInfo, isaead) {
if (isaead)
return aead.Match(tuv.aeadDecoderHolder, authInfo)
else if (authInfo in tuv.userHash) {
const cacheItem = tuv.userHash[authInfo]
return [cacheItem.user.user, common.uint64ToBuffer(cacheItem.timeInc + tuv.baseTime)]
}
}
}
}
function generateNewHashes(v, nowSec, user) {

@@ -112,12 +135,12 @@ var genEndSec = nowSec + cacheDurationSec

function updateUserHash(v) {
function updateUserHash() {
var nowSec = (unix())
for (var user of v.users) {
generateNewHashes(v, nowSec, user)
for (var user of this.users) {
generateNewHashes(this, nowSec, user)
}
var expire = (unix() - cacheDurationSec)
if (expire > v.baseTime) {
removeExpiredHashes(v, (expire - v.baseTime))
if (expire > this.baseTime) {
removeExpiredHashes(this, (expire - this.baseTime))
}

@@ -158,5 +181,8 @@ }

init,
Close,
addUsers,
removeUser,
getUser: account.getUser,
getUserByEmail: account.getUserByEmail,
OpenVMessAEADHeader: aead.OpenVMessAEADHeader,
SealVMessAEADHeader: aead.SealVMessAEADHeader
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc