UNPKG

js2ray

Version:

The v2ray vmess protocol, based on nodejs javascript which you can use on hosts and servers

156 lines (130 loc) 4.32 kB
const net = require('net') const dgram = require('dgram') 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; // so slow function iptoBuffer(ip, buff, offset) { if (ipv4Regex.test(ip)) { return ip4toBuffer(ip, buff, offset) } else if (net.isIPv6(ip)) { return ip6toBuffer(ip, buff, offset) } }; function ip6toBuffer(ip = "", buff, offset) { offset = ~~offset; let result; 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 ip4toBuffer(ip = "", buff, offset) { offset = ~~offset; let result; result = buff || Buffer.alloc(offset + 4); ip.split(/\./g).map((byte) => { result[offset++] = parseInt(byte, 10) & 0xff; }); if (!result) { throw Error(`Invalid ip address: ${ip}`); } return result; }; 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; }; function int2ip(ipInt) { return ((ipInt >>> 24) + '.' + (ipInt >> 16 & 255) + '.' + (ipInt >> 8 & 255) + '.' + (ipInt & 255)); } function ip2int(ip) { return ip.split('.').reduce(function (ipInt, octet) { return (ipInt << 8) + parseInt(octet, 10) }, 0) >>> 0; } function UDPBind(onconnect, onmessage, onclose, v6) { let socket = dgram.createSocket(v6 ? 'udp6' : 'udp4'); if (typeof onmessage === "object") { const stream = onmessage; onmessage = stream.write.bind(stream); onclose = stream.destroy?.bind(stream); stream.on("error", close); stream.on("close", close); stream.on("data", (buffer) => message(buffer)); } function close() { if (!socket) return; socket.close(); socket = null; } function message(buffer, port, host) { if (!socket || !port || port < 1 || port > 65535) return; socket.send(buffer, port, host); } const timeout = setTimeout(close, 10000); socket.on('error', close); socket.on('message', onmessage); socket.on('close', () => { clearTimeout(timeout); onclose?.(); socket = null; }); socket.once('message', () => clearTimeout(timeout)); socket.bind(onconnect); return { close, message }; } module.exports = { ip4toBuffer, ip6toBuffer, iptoString, iptoBuffer, int2ip, ip2int, UDPBind, }