Comparing version 2.2.7 to 2.3.0
@@ -26,6 +26,6 @@ | ||
const remoteProtocol = localProtocol(localsocket, localsocket.remoteAddress) | ||
localsocket.setTimeout(60000); | ||
localsocket.setTimeout(30000); | ||
localsocket.on('data', remoteProtocol.message); | ||
localsocket.on("close", remoteProtocol.close) | ||
localsocket.on("error", onerror) | ||
localsocket.on("error", remoteProtocol.close) | ||
}); | ||
@@ -45,2 +45,3 @@ return { | ||
} else if (data.type == "ws") { | ||
const wss = new WebSocketServer({ noServer: true }); | ||
if (data.option.fake) { | ||
@@ -53,4 +54,3 @@ var server = http.createServer(function (req, res) { | ||
} | ||
const wss = new WebSocketServer({ server, path: data.option.path }); | ||
wss.on('connection', function (ws) { | ||
function connected(ws) { | ||
ws.localMessage = function (buffer) { | ||
@@ -64,3 +64,10 @@ ws.send(buffer) | ||
ws.on("close", function () { remoteProtocol.close() }) | ||
ws.on("error", function () { remoteProtocol.close() }) | ||
ws.on('message', function (buffer) { remoteProtocol.message(buffer) }); | ||
} | ||
server.on('upgrade', function (request, socket, head) { | ||
if (typeof data.option.path == "string" ? request.url == data.option.path : data.option.path.includes(request.url)) { | ||
wss.handleUpgrade(request, socket, head, connected); | ||
} else | ||
socket.destroy(); | ||
}); | ||
@@ -106,4 +113,4 @@ return { | ||
const remoteProtocol = localProtocol(localsocket, localsocket.remoteAddress) | ||
localsocket.setTimeout(60000); | ||
localsocket.on("error", onerror) | ||
localsocket.setTimeout(30000); | ||
localsocket.on("error", remoteProtocol.close) | ||
localsocket.on("close", remoteProtocol.close) | ||
@@ -173,4 +180,4 @@ localsocket.on('data', function (buffer) { | ||
const remoteProtocol = localProtocol(localsocket, localsocket.remoteAddress) | ||
localsocket.setTimeout(60000); | ||
localsocket.on("error", onerror) | ||
localsocket.setTimeout(30000); | ||
localsocket.on("error", remoteProtocol.close) | ||
localsocket.on("close", remoteProtocol.close) | ||
@@ -177,0 +184,0 @@ localsocket.on('data', function (buffer) { |
@@ -20,11 +20,24 @@ | ||
log("tcp client connected to " + address + " port " + port, 2) | ||
const remotesocket = new net.Socket(); | ||
remotesocket.setTimeout(60000); | ||
remotesocket.on('error', error); | ||
remotesocket.on('close', remoteClose); | ||
var remotesocket = new net.Socket(); | ||
function close() { | ||
if (remotesocket == null) | ||
return | ||
remotesocket.destroy() | ||
remotesocket = null | ||
} | ||
remotesocket.setTimeout(30000); | ||
remotesocket.on('error', close); | ||
remotesocket.on('close', function () { | ||
remoteClose() | ||
remotesocket = null | ||
}); | ||
remotesocket.on('data', remoteMessage); | ||
remotesocket.connect(port, address, remoteConnect); | ||
return { | ||
message: remotesocket.write.bind(remotesocket), | ||
close: remotesocket.destroy.bind(remotesocket) | ||
message: function (buffer) { | ||
if (remotesocket == null) | ||
return | ||
remotesocket.write(buffer) | ||
}, | ||
close | ||
} | ||
@@ -35,21 +48,32 @@ } | ||
log("ws client connected to " + address + " port " + port, 2) | ||
const remotesocket = new WebSocket('ws://' + address + ':' + port + data.option.path, { | ||
var remotesocket = new WebSocket('ws://' + address + ':' + port + data.option.path, { | ||
headers: data.option.headers | ||
}); | ||
remotesocket.on('error', error); | ||
function close() { | ||
if (remotesocket == null) | ||
return | ||
remotesocket.close() | ||
remotesocket = null | ||
} | ||
// ===================== | ||
const timeout = setTimeout(close, 30000) | ||
remotesocket.on('error', close); | ||
remotesocket.on('close', function () { | ||
remoteClose() | ||
clearTimeout(timeout) | ||
remotesocket = null | ||
}); | ||
remotesocket.on('message', function (buffer) { | ||
remoteMessage(buffer) | ||
clearTimeout(timeout) | ||
}); | ||
// ===================== | ||
remotesocket.on('open', remoteConnect); | ||
return { | ||
message: function (buffer) { | ||
if (remotesocket == null) | ||
return | ||
remotesocket.send(buffer) | ||
}, | ||
close: function () { | ||
remotesocket.close() | ||
} | ||
close | ||
} | ||
@@ -66,6 +90,15 @@ } | ||
log("http client connected to " + address + " port " + port, 2) | ||
const remotesocket = new net.Socket(); | ||
remotesocket.setTimeout(60000); | ||
remotesocket.on('error', error); | ||
remotesocket.on('close', remoteClose); | ||
var remotesocket = new net.Socket(); | ||
function close() { | ||
if (remotesocket == null) | ||
return | ||
remotesocket.destroy() | ||
remotesocket = null | ||
} | ||
remotesocket.setTimeout(30000); | ||
remotesocket.on('error', close); | ||
remotesocket.on('close', function () { | ||
remoteClose() | ||
remotesocket = null | ||
}); | ||
remotesocket.on('data', function (buffer) { | ||
@@ -87,3 +120,3 @@ var indhttp = buffer.indexOf('\r\n\r\n') | ||
message: function (buffer) { | ||
if (remotesocket.readyState == "closed") | ||
if (remotesocket == null) | ||
return | ||
@@ -96,3 +129,3 @@ if (remotesocket.rh != true) { | ||
}, | ||
close: remotesocket.destroy.bind(remotesocket) | ||
close | ||
} | ||
@@ -99,0 +132,0 @@ } |
@@ -5,3 +5,3 @@ | ||
const freedom = require('./protocols/freedom'); | ||
const bridge = require('./protocols/bridge'); | ||
// const bridge = require('./protocols/bridge'); | ||
const localNetwork = require('./core/localNetwork'); | ||
@@ -62,2 +62,3 @@ const remoteNetwork = require('./core/remoteNetwork'); | ||
} | ||
return false; | ||
} | ||
@@ -64,0 +65,0 @@ if (data.api) |
@@ -11,3 +11,3 @@ const fs = require("fs") | ||
Restart=always | ||
RestartSec=10 | ||
RestartSec=200ms | ||
StandardOutput=syslog | ||
@@ -14,0 +14,0 @@ StandardError=syslog |
{ | ||
"name": "js2ray", | ||
"version": "2.2.7", | ||
"version": "2.3.0", | ||
"description": "The v2ray vmess protocol, based on nodejs javascript which you can use on hosts and servers", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -6,5 +6,2 @@ | ||
function onerror(e) { | ||
// log(e) | ||
} | ||
function freedom(address, port, cmd, onconnect, onmessage, onclose) { | ||
@@ -14,9 +11,27 @@ if (cmd == 2) { | ||
var dgramsocket = dgram.createSocket('udp4'); | ||
function close() { | ||
if (dgramsocket == null) | ||
return | ||
dgramsocket.close() | ||
dgramsocket = null | ||
} | ||
dgramsocket.on('error', close); | ||
dgramsocket.on('message', onmessage) | ||
// ===================== | ||
const timeout = setTimeout(close, 30000) | ||
dgramsocket.on('close', function () { | ||
onclose() | ||
dgramsocket = null | ||
clearTimeout(timeout) | ||
}); | ||
dgramsocket.on('message', clearTimeout.bind(null, timeout)); | ||
// ===================== | ||
dgramsocket.connect(port, address, onconnect); | ||
dgramsocket.on('close', onclose); | ||
dgramsocket.on('error', onerror); | ||
dgramsocket.on('message', onmessage) | ||
return { | ||
close: dgramsocket.close.bind(dgramsocket), | ||
message: dgramsocket.send.bind(dgramsocket), | ||
close, | ||
message: function (buffer) { | ||
if (dgramsocket == null) | ||
return | ||
dgramsocket.send(buffer) | ||
}, | ||
} | ||
@@ -26,10 +41,23 @@ } else { | ||
var remotesocket = new net.Socket(); | ||
remotesocket.setTimeout(60000) | ||
function close() { | ||
if (remotesocket == null) | ||
return | ||
remotesocket.destroy() | ||
remotesocket = null | ||
} | ||
remotesocket.on('close', function () { | ||
onclose() | ||
remotesocket = null | ||
}); | ||
remotesocket.on('data', onmessage); | ||
remotesocket.on('error', close) | ||
remotesocket.setTimeout(30000) | ||
remotesocket.connect(port, address, onconnect); | ||
remotesocket.on('close', onclose); | ||
remotesocket.on('error', onerror); | ||
remotesocket.on('data', onmessage); | ||
return { | ||
close: remotesocket.destroy.bind(remotesocket), | ||
message: remotesocket.write.bind(remotesocket), | ||
close, | ||
message: function (buffer) { | ||
if (remotesocket == null) | ||
return | ||
remotesocket.write(buffer) | ||
} | ||
} | ||
@@ -40,41 +68,2 @@ } | ||
// // UDP socket with timeout | ||
// const dns = require("dns"); | ||
// const dgram = require("dgram"); | ||
// const dnsPacket = require("dns-packet"); | ||
// const _resolveUDP = (packet, addr, port = 53, timeout) => { | ||
// return new Promise((resolve, reject) => { | ||
// const socket = dgram.createSocket("udp4"); | ||
// const id = setTimeout(() => { | ||
// clearTimeout(id); | ||
// socket.close(); | ||
// reject("timed out"); | ||
// return; | ||
// }, parseInt(timeout)); | ||
// socket.on("message", message => { | ||
// clearTimeout(id); // Clear the timeout if you are going to close the socket manually. | ||
// socket.close(); | ||
// resolve(dnsPacket.decode(message)); | ||
// return; | ||
// }); | ||
// socket.on("error", err => { | ||
// clearTimeout(id); // Clear the timeout if you are going to close the socket manually. | ||
// socket.close(); | ||
// reject(err); | ||
// return; | ||
// }); | ||
// socket.send(packet, 0, packet.length, parseInt(port), addr, err => { | ||
// if (err) { | ||
// clearTimeout(id); // Clear the timeout if you are going to close the socket manually. | ||
// socket.close(); | ||
// reject(err); | ||
// } | ||
// }); | ||
// }); | ||
// }; | ||
@@ -119,7 +119,9 @@ | ||
ipCount: a.ipCount || 0, | ||
ipCountDuration: a.ipCountDuration || 40, | ||
ipCountDuration: a.ipCountDuration || 600, | ||
email: a.email, | ||
expire: a.expire, | ||
deactive: a.deactive || 0, | ||
ipList: a.ipList || {} | ||
ipList: a.ipList || {}, | ||
ipWarning: a.ipWarning || {}, | ||
ipBlock: a.ipBlock || {}, | ||
} | ||
@@ -126,0 +128,0 @@ users[tag][id] = user |
@@ -47,6 +47,9 @@ | ||
socket.app = app | ||
socket.app.close = socket.close | ||
return { | ||
message: EncodeRequestBody.bind(socket, randomuser), | ||
close: onclose.bind(socket), | ||
close: function () { | ||
onclose(socket.app) | ||
delete socket.app | ||
} | ||
} | ||
@@ -56,5 +59,8 @@ } | ||
function onclose() { | ||
this.close() | ||
const app = this.app | ||
function onerror(app, error, ind) { | ||
onclose(app); | ||
log(error, ind) | ||
} | ||
function onclose(app) { | ||
app._isConnecting = false; | ||
@@ -77,2 +83,3 @@ app._isHeaderSent = false; | ||
app._chunkLenDecMaskGenerator = null; | ||
app.close() | ||
@@ -84,20 +91,22 @@ } | ||
const app = this.app | ||
if (!app._isHeaderSent) { | ||
app._isHeaderSent = true; | ||
const header = EncodeRequestHeader(app, randomuser) | ||
if (header) { | ||
if (app) { | ||
if (!app._isHeaderSent) { | ||
app._isHeaderSent = true; | ||
const header = EncodeRequestHeader(app, randomuser) | ||
if (header) { | ||
if (app.user.deactive) | ||
return | ||
app.user.bytesRead += buffer.length | ||
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 onerror(app, `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")}`, 1); | ||
app.user.bytesRead += buffer.length | ||
const chunks = common.getChunks(buffer, 0x3fff).map(resolveChunk.bind(app)); | ||
this.message(Buffer.concat([header, ...chunks])) | ||
this.message(Buffer.concat(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")}`, 1); | ||
app.user.bytesRead += buffer.length | ||
const chunks = common.getChunks(buffer, 0x3fff).map(resolveChunk.bind(app)); | ||
this.message(Buffer.concat(chunks)) | ||
} | ||
@@ -113,3 +122,3 @@ } | ||
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")}`, 1) | ||
return onerror(app, `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")}`, 1) | ||
} | ||
@@ -148,2 +157,5 @@ // IV and Key for data chunks encryption/decryption | ||
} | ||
if (app._host.length > 255) { | ||
return onerror(app, `domain name is too long`, 1) | ||
} | ||
// create encrypted command | ||
@@ -179,7 +191,7 @@ let command = Buffer.from([ | ||
if (aeadHeader == undefined) { | ||
return log(`AEAD read failed`, 1) | ||
return onerror(app, `AEAD read failed`, 1) | ||
} | ||
return aeadHeader; | ||
} else { | ||
return log(`invalid user`, 1); | ||
return onerror(app, `invalid user`, 1); | ||
} | ||
@@ -199,3 +211,3 @@ } | ||
if (app._v !== header[0]) { | ||
return log(`server response v doesn't match, expect ${app._v} but got ${header[0]}`, 1); | ||
return onerror(app, `server response v doesn't match, expect ${app._v} but got ${header[0]}`, 1); | ||
} | ||
@@ -226,3 +238,3 @@ app._isHeaderRecv = true; | ||
if (app._v !== header[0]) { | ||
return log(`server response v doesn't match, expect ${app._v} but got ${header[0]}`, 1); | ||
return onerror(app, `server response v doesn't match, expect ${app._v} but got ${header[0]}`, 1); | ||
} | ||
@@ -233,3 +245,3 @@ app._isHeaderRecv = true; | ||
} catch (error) { | ||
return log(`unable to authenticate server response data: ${error}`) | ||
return onerror(app, `unable to authenticate server response data: ${error}`) | ||
} | ||
@@ -244,3 +256,3 @@ } | ||
if (data === null) { | ||
return log(`fail to decrypt data chunk`, 1); | ||
return onerror(app, `fail to decrypt data chunk`, 1); | ||
} | ||
@@ -247,0 +259,0 @@ app.user.bytesWrit += data.length |
@@ -13,10 +13,9 @@ | ||
function ntb(num, len = 2, byteOrder = 0) { | ||
if (len < 1) { | ||
throw Error('len must be greater than 0'); | ||
} | ||
const buf = Buffer.alloc(len); | ||
if (byteOrder === 0) { | ||
buf.writeUIntBE(num, 0, len); | ||
} else { | ||
buf.writeUIntLE(num, 0, len); | ||
if (len > 0 && num >= 0 && num <= Math.pow(256, len) - 1) { | ||
if (byteOrder === 0) { | ||
buf.writeUIntBE(num, 0, len); | ||
} else { | ||
buf.writeUIntLE(num, 0, len); | ||
} | ||
} | ||
@@ -73,24 +72,27 @@ return buf; | ||
const security = app._security; | ||
const nonce = Buffer.concat([ntb(app._cipherNonce), app._dataEncIV.subarray(2, 12)]); | ||
let ciphertext = null; | ||
let tag = null; | ||
if (security === consts.SECURITY_TYPE_AES_128_GCM) { | ||
const cipher = crypto.createCipheriv('aes-128-gcm', app._dataEncKey, nonce); | ||
ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]); | ||
tag = cipher.getAuthTag(); | ||
app._cipherNonce += 1; | ||
if (app._cipherNonce > 65535) | ||
app._cipherNonce = 0 | ||
if (app._dataEncIV) { | ||
let tag = null; | ||
const nonce = Buffer.concat([ntb(app._cipherNonce), app._dataEncIV.subarray(2, 12)]); | ||
let ciphertext = null; | ||
if (security === consts.SECURITY_TYPE_AES_128_GCM) { | ||
const cipher = crypto.createCipheriv('aes-128-gcm', app._dataEncKey, nonce); | ||
ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]); | ||
tag = cipher.getAuthTag(); | ||
app._cipherNonce += 1; | ||
if (app._cipherNonce > 65535) | ||
app._cipherNonce = 0 | ||
} | ||
else if (security === consts.SECURITY_TYPE_CHACHA20_POLY1305) { | ||
const cipher = crypto.createCipheriv('chacha20-poly1305', app._dataEncKeyForChaCha20, nonce, { | ||
authTagLength: 16 | ||
}) | ||
ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]); | ||
tag = cipher.getAuthTag(); | ||
app._cipherNonce += 1; | ||
if (app._cipherNonce > 65535) | ||
app._cipherNonce = 0 | ||
} | ||
return Buffer.concat([ciphertext, tag]); | ||
} | ||
else if (security === consts.SECURITY_TYPE_CHACHA20_POLY1305) { | ||
const cipher = crypto.createCipheriv('chacha20-poly1305', app._dataEncKeyForChaCha20, nonce, { | ||
authTagLength: 16 | ||
}) | ||
ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]); | ||
tag = cipher.getAuthTag(); | ||
app._cipherNonce += 1; | ||
if (app._cipherNonce > 65535) | ||
app._cipherNonce = 0 | ||
} | ||
return Buffer.concat([ciphertext, tag]); | ||
return Buffer.alloc(0) | ||
} | ||
@@ -157,6 +159,27 @@ function decrypt(ciphertext, app) { | ||
} | ||
function iplimit(ip, user) { | ||
function iplimit(ip, user) { | ||
var now = Math.round(new Date() / 1000) | ||
if (!(ip in user.ipList)) { | ||
if (user.ipCount && !(ip in user.ipList)) { | ||
if (Object.keys(user.ipList).length >= user.ipCount) { | ||
if (user.ipBlock[ip] && user.ipBlock[ip] + user.ipCountDuration > now) { | ||
// ======================================= soft allow for 1 or 2 ip | ||
if (ip in user.ipWarning) { | ||
if (user.ipWarning[ip] + user.ipCountDuration / 20 < now) { | ||
return false | ||
} | ||
} else if (Object.keys(user.ipWarning).length <= Math.max(1, Math.round(user.ipCount / 2.1))) { | ||
user.ipWarning[ip] = now | ||
} else { | ||
if (!user.maxip) { | ||
event.emit("ip", user.id.UUID) | ||
user.maxip = true | ||
} | ||
} | ||
return true | ||
// =================================================== | ||
} else { | ||
delete user.ipBlock[ip] | ||
user.maxip = false | ||
} | ||
//================================================ clear ips | ||
for (const i in user.ipList) { | ||
@@ -167,15 +190,65 @@ if (user.ipList[i] + user.ipCountDuration < now) { | ||
} | ||
if (Object.keys(user.ipList).length >= user.ipCount) { | ||
if (!user.maxip) { | ||
event.emit("ip", user.id.UUID) | ||
user.maxip = true | ||
if (user.ipList.length == 0) | ||
user.maxip = false | ||
for (const i in user.ipWarning) { | ||
if (user.ipWarning[i] + user.ipCountDuration < now) { | ||
delete user.ipWarning[i] | ||
} | ||
return true; | ||
} | ||
//=================================================== | ||
const keys = Object.keys(user.ipList) | ||
if (keys.length >= user.ipCount) { | ||
var wip = keys.reduce((key, v) => user.ipList[v] < user.ipList[key] ? v : key); | ||
user.ipBlock[wip] = user.ipList[wip] | ||
delete user.ipList[wip]; | ||
for (const i in user.ipBlock) { | ||
if (user.ipBlock[i] + user.ipCountDuration < now) { | ||
delete user.ipBlock[i] | ||
} | ||
} | ||
} | ||
} | ||
} | ||
if (user.maxip) | ||
user.maxip = false | ||
user.ipList[ip] = now | ||
return false | ||
} | ||
// function update_ip(ip, user) { | ||
// user.ipList[ip] = now | ||
// } | ||
// 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.ipBlock) { | ||
// if (user.ipBlock[i] + user.ipCountDuration < now) { | ||
// delete user.ipBlock[i] | ||
// } | ||
// } | ||
// if (Object.keys(user.ipBlock).length >= Math.max(2, Math.round(user.ipCount / 2))) { | ||
// if (!user.maxip) { | ||
// event.emit("ip", user.id.UUID) | ||
// user.maxip = true | ||
// } | ||
// return true | ||
// } | ||
// //================================================ clear ips | ||
// for (const i in user.ipList) { | ||
// if (user.ipList[i] + 30 < now) { | ||
// delete user.ipList[i] | ||
// } | ||
// } | ||
// //================================================ | ||
// if (Object.keys(user.ipList).length >= user.ipCount) { | ||
// user.ipBlock[ip] = now | ||
// return true | ||
// } | ||
// } | ||
// } | ||
// if (user.maxip) | ||
// user.maxip = false | ||
// user.ipList[ip] = now | ||
// } | ||
function iptoString(buff, offset, length) { | ||
@@ -278,2 +351,3 @@ offset = ~~offset; | ||
trafficlimit, | ||
update_ip: iplimit, | ||
iplimit | ||
@@ -280,0 +354,0 @@ } |
@@ -20,4 +20,4 @@ | ||
function Check(filter, sum) { | ||
var now = Math.round(new Date() / 1000) | ||
if (now - filter.lastSwap >= filter.Interval) { | ||
var now = Math.round(new Date() / 1000) | ||
if (now - filter.lastSwap >= filter.interval) { | ||
if (filter.poolSwap) { | ||
@@ -24,0 +24,0 @@ filter.poolA.clear() |
@@ -32,3 +32,6 @@ | ||
message: DecodeRequestHeader.bind(socket, remoteProtocol, EncodeResponseBody.bind(socket), socket.localClose, checkuser), | ||
close: onclose.bind(socket), | ||
close: function () { | ||
onclose(socket.app); | ||
delete socket.app | ||
}, | ||
} | ||
@@ -41,185 +44,189 @@ } | ||
const app = this.app | ||
if (!app._isHeaderRecv) { | ||
if (app._isConnecting) { | ||
app._staging = Buffer.concat([app._staging, buffer]); | ||
return; | ||
} | ||
if (app) { | ||
if (!app._isHeaderRecv) { | ||
if (app._isConnecting) { | ||
app._staging = Buffer.concat([app._staging, buffer]); | ||
return; | ||
} | ||
if (buffer.length < 16) { | ||
return log(`fail to parse request header: ${buffer.toString("hex")}`, 1); | ||
} | ||
if (buffer.length < 16) { | ||
return onerror(app, `fail to parse request header: ${buffer.toString("hex")}`, 1); | ||
} | ||
const reqCommand = Buffer.from(buffer.subarray(16)); | ||
var aeadUser = checkuser(buffer.subarray(0, 16), true) | ||
if (aeadUser == undefined) { | ||
var dataoffset = 16 | ||
const user = checkuser(buffer.subarray(0, 16).toString("hex")); | ||
if (user == undefined) | ||
return log(`cannot find ${buffer.subarray(0, 16).toString("hex")} in cache, maybe a wrong auth info`, 1); | ||
var ts = user[1] | ||
aeadUser = user[0] | ||
var decipher = crypto.createDecipheriv('aes-128-cfb', aeadUser.id.cmdKey, | ||
common.hash('md5', Buffer.concat([ts, ts, ts, ts]))); | ||
decipher.subarray = function (a, b) { | ||
return this.update(reqCommand.subarray(a, b)) | ||
const reqCommand = Buffer.from(buffer.subarray(16)); | ||
var aeadUser = checkuser(buffer.subarray(0, 16), true) | ||
if (aeadUser == undefined) { | ||
var dataoffset = 16 | ||
const user = checkuser(buffer.subarray(0, 16).toString("hex")); | ||
if (user == undefined) | ||
return onerror(app, `cannot find ${buffer.subarray(0, 16).toString("hex")} in cache, maybe a wrong auth info`, 1); | ||
var ts = user[1] | ||
aeadUser = user[0] | ||
var decipher = crypto.createDecipheriv('aes-128-cfb', aeadUser.id.cmdKey, | ||
common.hash('md5', Buffer.concat([ts, ts, ts, ts]))); | ||
decipher.subarray = function (a, b) { | ||
return this.update(reqCommand.subarray(a, b)) | ||
} | ||
app.user = aeadUser | ||
app._isAEADRequest = false | ||
} else if (typeof aeadUser == "object") { | ||
var decipher = validator.OpenVMessAEADHeader(aeadUser.id.cmdKey, buffer) | ||
// 16 + 12 + 12 + 18 | ||
var dataoffset = 58 | ||
if (decipher == undefined) { | ||
return onerror(app, `AEAD read failed`, 1) | ||
} | ||
app.user = aeadUser | ||
app._isAEADRequest = true | ||
} else { | ||
return onerror(app, `invalid user`, 1); | ||
} | ||
app.user = aeadUser | ||
app._isAEADRequest = false | ||
} else if (typeof aeadUser == "object") { | ||
var decipher = validator.OpenVMessAEADHeader(aeadUser.id.cmdKey, buffer) | ||
// 16 + 12 + 12 + 18 | ||
var dataoffset = 58 | ||
if (decipher == undefined) { | ||
return log(`AEAD read failed`, 1) | ||
if (aeadUser.deactive) | ||
return | ||
if (aeadUser.ipCount != 0 && common.iplimit(app.ip, aeadUser)) | ||
return onerror(app, `maximum ip used by user ${aeadUser.id.UUID.toString("hex")}`, 1) | ||
if (aeadUser.traffic != 0 && common.trafficlimit(aeadUser)) { | ||
return onerror(app, `maximum traffic ${aeadUser.traffic / 1024 / 1024}MB (bytesRead:${aeadUser.bytesRead},bytesWrit:${aeadUser.bytesWrit}) used by user ${aeadUser.id.UUID.toString("hex")}`, 1) | ||
} | ||
app.user = aeadUser | ||
app._isAEADRequest = true | ||
} else { | ||
return log(`invalid user`, 1); | ||
} | ||
if (aeadUser.deactive) | ||
return | ||
if (aeadUser.expire && aeadUser.expire < new Date().getTime()) { | ||
return onerror(app, `expire user ${aeadUser.id.UUID.toString("hex")}`, 1) | ||
} | ||
if (aeadUser.ipCount != 0 && common.iplimit(app.ip, aeadUser)) | ||
return log(`maximum ip used by user ${aeadUser.id.UUID.toString("hex")}`, 1) | ||
if (reqCommand.length < 41) { | ||
return onerror(app, `request command is too short: ${reqCommand.length}bytes, command=${reqCommand.toString('hex')}`, 1); | ||
} | ||
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")}`, 1) | ||
} | ||
if (aeadUser.expire && aeadUser.expire < new Date().getTime()) { | ||
return log(`expire user ${aeadUser.id.UUID.toString("hex")}`, 1) | ||
} | ||
const reqHeader = decipher.subarray(0, 41); | ||
const ver = reqHeader[0]; | ||
if (reqCommand.length < 41) { | ||
return log(`request command is too short: ${reqCommand.length}bytes, command=${reqCommand.toString('hex')}`, 1); | ||
} | ||
if (ver !== 0x01) { | ||
return onerror(app, `invalid version number: ${ver}`, 1); | ||
} | ||
const reqHeader = decipher.subarray(0, 41); | ||
const ver = reqHeader[0]; | ||
app._dataDecIV = reqHeader.subarray(1, 17); | ||
app._dataDecKey = reqHeader.subarray(17, 33); | ||
if (app._isAEADRequest) { | ||
app._dataEncIV = common.hash('sha256', app._dataDecIV).subarray(0, 16); | ||
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 { | ||
app._dataEncIV = common.hash('md5', app._dataDecIV); | ||
app._dataEncKey = common.hash('md5', app._dataDecKey); | ||
} | ||
if (ver !== 0x01) { | ||
return log(`invalid version number: ${ver}`, 1); | ||
} | ||
app._dataDecIV = reqHeader.subarray(1, 17); | ||
app._dataDecKey = reqHeader.subarray(17, 33); | ||
if (app._isAEADRequest) { | ||
app._dataEncIV = common.hash('sha256', app._dataDecIV).subarray(0, 16); | ||
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 { | ||
app._dataEncIV = common.hash('md5', app._dataDecIV); | ||
app._dataEncKey = common.hash('md5', app._dataDecKey); | ||
} | ||
app._chunkLenDecMaskGenerator = common.shake128(app._dataDecIV); | ||
app._chunkLenEncMaskGenerator = common.shake128(app._dataEncIV); | ||
app._responseHeader = reqHeader[33]; | ||
app._option = reqHeader[34]; | ||
const paddingLen = reqHeader[35] >> 4; | ||
// 2 'auto' | ||
// 3 'aes-128-gcm' | ||
// 4 'chacha20-poly1305' | ||
// 5 'none' | ||
// 6 'zero' | ||
const securityType = reqHeader[35] & 0x0F; | ||
if (!(aeadUser.security == 2 || aeadUser.security == undefined || securityType == 2) && securityType != aeadUser.security) { | ||
return onerror(app, `not match securety type`, 1); | ||
} | ||
if (securityType == consts.SECURITY_TYPE_CHACHA20_POLY1305) { | ||
app._dataEncKeyForChaCha20 = common.createChacha20Poly1305Key(app._dataEncKey); | ||
app._dataDecKeyForChaCha20 = common.createChacha20Poly1305Key(app._dataDecKey); | ||
} | ||
//=========== | ||
let offset = 40; | ||
var cmd = reqHeader[37]; | ||
if (![0x01, 0x02, 0x03].includes(cmd)) { | ||
return onerror(app, `unsupported cmd: ${cmd}`, 1); | ||
} | ||
if (cmd == 0x03) { | ||
var port = 0 | ||
var addr = "v1.mux.cool" | ||
var addrType = ATYP_DOMAIN | ||
} | ||
else { | ||
var port = reqHeader.readUInt16BE(38); | ||
var addrType = reqHeader[40]; | ||
var addr = null; | ||
if (addrType === ATYP_V4) { | ||
if (reqCommand.length < 45) { | ||
return onerror(app, `request command is too short ${reqCommand.length}bytes to get ipv4, command=${reqCommand.toString('hex')}`, 1); | ||
} | ||
app._chunkLenDecMaskGenerator = common.shake128(app._dataDecIV); | ||
app._chunkLenEncMaskGenerator = common.shake128(app._dataEncIV); | ||
addr = decipher.subarray(41, 45); | ||
offset += 4; | ||
} else if (addrType === ATYP_V6) { | ||
if (reqCommand.length < 57) { | ||
return onerror(app, `request command is too short: ${reqCommand.length}bytes to get ipv6, command=${reqCommand.toString('hex')}`, 1); | ||
} | ||
app._responseHeader = reqHeader[33]; | ||
app._option = reqHeader[34]; | ||
const paddingLen = reqHeader[35] >> 4; | ||
// 2 'auto' | ||
// 3 'aes-128-gcm' | ||
// 4 'chacha20-poly1305' | ||
// 5 'none' | ||
// 6 'zero' | ||
const securityType = reqHeader[35] & 0x0F; | ||
if (!(aeadUser.security == 2 || aeadUser.security == undefined || securityType == 2) && securityType != aeadUser.security) { | ||
return log(`not match securety type`, 1); | ||
} | ||
if (securityType == consts.SECURITY_TYPE_CHACHA20_POLY1305) { | ||
app._dataEncKeyForChaCha20 = common.createChacha20Poly1305Key(app._dataEncKey); | ||
app._dataDecKeyForChaCha20 = common.createChacha20Poly1305Key(app._dataDecKey); | ||
} | ||
//=========== | ||
let offset = 40; | ||
var cmd = reqHeader[37]; | ||
if (![0x01, 0x02, 0x03].includes(cmd)) { | ||
return log(`unsupported cmd: ${cmd}`, 1); | ||
} | ||
if (cmd == 0x03) { | ||
var port = 0 | ||
var addr = "v1.mux.cool" | ||
var addrType = ATYP_DOMAIN | ||
} | ||
else { | ||
var port = reqHeader.readUInt16BE(38); | ||
var addrType = reqHeader[40]; | ||
var addr = null; | ||
if (addrType === ATYP_V4) { | ||
if (reqCommand.length < 45) { | ||
return log(`request command is too short ${reqCommand.length}bytes to get ipv4, command=${reqCommand.toString('hex')}`, 1); | ||
} | ||
addr = decipher.subarray(41, 57); | ||
offset += 16; | ||
} else if (addrType === ATYP_DOMAIN) { | ||
if (reqCommand.length < 42) { | ||
return onerror(app, `request command is too short: ${reqCommand.length}bytes to get host name, command=${reqCommand.toString('hex')}`, 1); | ||
} | ||
addr = decipher.subarray(41, 45); | ||
offset += 4; | ||
} else if (addrType === ATYP_V6) { | ||
if (reqCommand.length < 57) { | ||
return log(`request command is too short: ${reqCommand.length}bytes to get ipv6, command=${reqCommand.toString('hex')}`, 1); | ||
} | ||
const addrLen = decipher.subarray(41, 42)[0]; | ||
addr = decipher.subarray(41, 57); | ||
offset += 16; | ||
} else if (addrType === ATYP_DOMAIN) { | ||
if (reqCommand.length < 42) { | ||
return log(`request command is too short: ${reqCommand.length}bytes to get host name, command=${reqCommand.toString('hex')}`, 1); | ||
} | ||
if (reqCommand.length < 42 + addrLen) { | ||
return onerror(app, `request command is too short: ${reqCommand.length}bytes, command=${reqCommand.toString('hex')}`, 1); | ||
} | ||
const addrLen = decipher.subarray(41, 42)[0]; | ||
if (reqCommand.length < 42 + addrLen) { | ||
return log(`request command is too short: ${reqCommand.length}bytes, command=${reqCommand.toString('hex')}`, 1); | ||
addr = decipher.subarray(42, 42 + addrLen); | ||
offset += 1 + addrLen; | ||
} else { | ||
return onerror(app, `unknown address type: ${addrType}, command=${reqHeader.toString('hex')}`, 1); | ||
} | ||
} | ||
addr = decipher.subarray(42, 42 + addrLen); | ||
offset += 1 + addrLen; | ||
} else { | ||
return log(`unknown address type: ${addrType}, command=${reqHeader.toString('hex')}`, 1); | ||
if (reqCommand.length < offset + paddingLen + 4) { | ||
return onerror(app, `request command is too short: ${reqCommand.length}bytes to get padding and f, command=${reqCommand.toString('hex')}`, 1); | ||
} | ||
} | ||
if (reqCommand.length < offset + paddingLen + 4) { | ||
return log(`request command is too short: ${reqCommand.length}bytes to get padding and f, command=${reqCommand.toString('hex')}`, 1); | ||
} | ||
const padding = decipher.subarray(offset, offset + paddingLen); | ||
offset += paddingLen; | ||
const f = decipher.subarray(offset, offset + 4); | ||
const padding = decipher.subarray(offset, offset + paddingLen); | ||
offset += paddingLen; | ||
const f = decipher.subarray(offset, offset + 4); | ||
const plainReqHeader = Buffer.from([...reqHeader.subarray(0, 41), ...(addrType === ATYP_DOMAIN ? [addr.length] : []), ...addr, ...padding]); | ||
const plainReqHeader = Buffer.from([...reqHeader.subarray(0, 41), ...(addrType === ATYP_DOMAIN ? [addr.length] : []), ...addr, ...padding]); | ||
if (common.fnv1a(plainReqHeader).equals(f)) { | ||
return log('fail to verify request command', 1); | ||
if (common.fnv1a(plainReqHeader).equals(f)) { | ||
return onerror(app, 'fail to verify request command', 1); | ||
} | ||
const data = buffer.subarray(dataoffset + plainReqHeader.length + 4); | ||
app._security = securityType; | ||
app._isConnecting = true; | ||
app.remote = remoteProtocol( | ||
addrType === ATYP_DOMAIN ? addr.toString() : common.iptoString(addr), | ||
port, | ||
cmd, | ||
function () { | ||
if (!app._adBuf) | ||
return | ||
app._adBuf.put(Buffer.concat([data, app._staging]), app); | ||
app._isHeaderRecv = true; | ||
app._isConnecting = false; | ||
app._staging = null; | ||
}, | ||
onRemoteMessage, | ||
onRemoteClose | ||
) | ||
} else { | ||
if (app.user.deactive) | ||
return | ||
if (common.update_ip(app.ip, app.user)) | ||
return onerror(app, `maximum ip used by user ${app.user.id.UUID.toString("hex")}`, 1) | ||
if (app.user.traffic != 0 && common.trafficlimit(app.user)) | ||
return onerror(app, `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")}`, 1); | ||
if (app.user.expire && app.user.expire < new Date().getTime()) { | ||
return onerror(app, `expire user ${app.user.id.UUID.toString("hex")}`, 1) | ||
} | ||
app._adBuf.put(buffer, app); | ||
} | ||
const data = buffer.subarray(dataoffset + plainReqHeader.length + 4); | ||
app._security = securityType; | ||
app._isConnecting = true; | ||
app.remote = remoteProtocol( | ||
addrType === ATYP_DOMAIN ? addr.toString() : common.iptoString(addr), | ||
port, | ||
cmd, | ||
function () { | ||
if (!app._adBuf) | ||
return | ||
app._adBuf.put(Buffer.concat([data, app._staging]), app); | ||
app._isHeaderRecv = true; | ||
app._isConnecting = false; | ||
app._staging = null; | ||
}, | ||
onRemoteMessage, | ||
onRemoteClose | ||
) | ||
} 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")}`, 1); | ||
if (app.user.expire && app.user.expire < new Date().getTime()) { | ||
return log(`expire user ${app.user.id.UUID.toString("hex")}`, 1) | ||
} | ||
app._adBuf.put(buffer, app); | ||
} | ||
@@ -247,3 +254,3 @@ } | ||
} catch (error) { | ||
log(error) | ||
onerror(app, error) | ||
} | ||
@@ -254,13 +261,15 @@ } | ||
const app = this.app | ||
if (!app._isHeaderSent) { | ||
app._isHeaderSent = true; | ||
const header = EncodeResponseHeader(app) | ||
app.user.bytesRead += buffer.length | ||
const chunks = common.getChunks(buffer, 0x3fff).map(resolveChunk.bind(app)); | ||
this.localMessage(header) | ||
this.localMessage(Buffer.concat([...chunks])) | ||
} else { | ||
app.user.bytesRead += buffer.length | ||
const chunks = common.getChunks(buffer, 0x3fff).map(resolveChunk.bind(app)); | ||
this.localMessage(Buffer.concat(chunks)) | ||
if (app) { | ||
if (!app._isHeaderSent) { | ||
app._isHeaderSent = true; | ||
const header = EncodeResponseHeader(app) | ||
app.user.bytesRead += buffer.length | ||
const chunks = common.getChunks(buffer, 0x3fff).map(resolveChunk.bind(app)); | ||
this.localMessage(header) | ||
this.localMessage(Buffer.concat([...chunks])) | ||
} else { | ||
app.user.bytesRead += buffer.length | ||
const chunks = common.getChunks(buffer, 0x3fff).map(resolveChunk.bind(app)); | ||
this.localMessage(Buffer.concat(chunks)) | ||
} | ||
} | ||
@@ -272,3 +281,3 @@ } | ||
if (data === null) { | ||
return log("fail to decrypt data chunk", 1); | ||
return onerror(app, "fail to decrypt data chunk", 1); | ||
} | ||
@@ -332,24 +341,30 @@ app.user.bytesWrit += data.length | ||
function onclose() { | ||
if (this.app.remote) | ||
this.app.remote.close() | ||
this.app.remote = null | ||
function onerror(app, error, ind) { | ||
onclose(app); | ||
log(error, ind) | ||
} | ||
this.app._isConnecting = false; | ||
this.app._isHeaderSent = false; | ||
this.app._isHeaderRecv = false; | ||
if (this.app._adBuf) | ||
this.app._adBuf.clear(); | ||
this.app._adBuf = null; | ||
this.app._host = null; | ||
this.app._port = null; | ||
this.app._staging = null; | ||
this.app._dataEncKey = null; | ||
this.app._dataEncKeyForChaCha20 = null; | ||
this.app._dataEncIV = null; | ||
this.app._dataDecKey = null; | ||
this.app._dataDecKeyForChaCha20 = null; | ||
this.app._dataDecIV = null; | ||
this.app._chunkLenEncMaskGenerator = null; | ||
this.app._chunkLenDecMaskGenerator = null; | ||
function onclose(app) { | ||
if (app.remote) | ||
app.remote.close() | ||
app.remote = null | ||
app._isConnecting = false; | ||
app._isHeaderSent = false; | ||
app._isHeaderRecv = false; | ||
if (app._adBuf) | ||
app._adBuf.clear(); | ||
app._adBuf = null; | ||
app._option = null | ||
app._host = null; | ||
app._port = null; | ||
app._staging = null; | ||
app._dataEncKey = null; | ||
app._dataEncKeyForChaCha20 = null; | ||
app._dataEncIV = null; | ||
app._dataDecKey = null; | ||
app._dataDecKeyForChaCha20 = null; | ||
app._dataDecIV = null; | ||
app._chunkLenEncMaskGenerator = null; | ||
app._chunkLenDecMaskGenerator = null; | ||
} | ||
@@ -356,0 +371,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
137365
3527