Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 10 additions & 61 deletions app.ext.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const fs = require('fs');
const path = require('path');
const net = require('net');
const crypto = require('crypto');
const {URL} = require('url');
const {exec} = require('child_process');
Expand Down Expand Up @@ -106,53 +105,7 @@ const server = createServer((req, res) => {
}
});

/**
* Refer to: https://xtls.github.io/development/protocols/vless.html
* Parse the client handshake message and extract the version, target host/port and message offset
* @param {Buffer} buf Handshake Message Buffer
* @returns {{version:number, command:number, host:string, port:number, offset:number}}
*/
function parseHandshake(buf) {
let offset = 0;
const version = buf.readUInt8(offset);
offset += 1;

// skip uuid
offset += 16;

const optLen = buf.readUInt8(offset);
offset += 1 + optLen;

const command = buf.readUInt8(offset);
offset += 1;

const port = buf.readUInt16BE(offset);
offset += 2;

const addressType = buf.readUInt8(offset);
offset += 1;

let host;
if (addressType === 1) { // IPV4
host = Array.from(buf.subarray(offset, offset + 4)).join('.');
offset += 4;
} else if (addressType === 2) { // DOMAIN
const len = buf.readUInt8(offset++);
host = buf.subarray(offset, offset + len).toString();
offset += len;
} else if (addressType === 3) { // IPV6
const segments = [];
for (let i = 0; i < 8; i++) {
segments.push(buf.readUInt16BE(offset).toString(16));
offset += 2;
}
host = segments.join(':');
} else {
throw new Error(`Unsupported address type: ${addressType}`);
}

return {version, command, host, port, offset};
}
const {parseHandshake, relayTraffic} = require('./vless-relay');

const wss = new WebSocketServer({server});
wss.on('connection', (ws, req) => {
Expand All @@ -167,22 +120,18 @@ wss.on('connection', (ws, req) => {

ws.once('message', msg => {
try {
const {version, host, port, offset} = parseHandshake(msg);
// console.log('version: ', version, 'host: ', host, 'port: ', port, 'offset: ', offset);

ws.send(Buffer.from([version, 0]));
const handshake = parseHandshake(msg);
// console.log('version: ', handshake.version, 'id: ', handshake.id, 'host: ', handshake.host, 'port: ', handshake.port, 'offset: ', handshake.offset);

const duplex = createWebSocketStream(ws);
const socket = net.connect({host, port}, () => {
socket.write(msg.slice(offset));
duplex.pipe(socket).pipe(duplex);
});
const uuidBuf = Buffer.from(UUID.replace(/-/g, ''), 'hex');
if (!handshake.id.equals(uuidBuf)) {
return ws.close();
}

duplex.on('error', () => {});
socket.on('error', () => {});
ws.send(Buffer.from([handshake.version, 0]));

socket.on('close', () => ws.terminate());
duplex.on('close', () => socket.destroy());
const duplex = createWebSocketStream(ws);
relayTraffic(duplex, handshake, msg.subarray(handshake.offset), () => ws.terminate());

} catch (err) {
// console.error('Handshake error: ', err);
Expand Down
70 changes: 6 additions & 64 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const fs = require('fs');
const path = require('path');
const net = require('net');
const crypto = require('crypto');
const {URL} = require('url');
const {exec} = require('child_process');
Expand Down Expand Up @@ -91,80 +90,23 @@ const server = createServer((req, res) => {
}
});

/**
* Refer to: https://xtls.github.io/development/protocols/vless.html
* Parse the client handshake message and extract the version, UUID, target host/port and message offset
* @param {Buffer} buf Handshake Message Buffer
* @returns {{version:number, id:Buffer, command:number, host:string, port:number, offset:number}}
*/
function parseHandshake(buf) {
let offset = 0;
const version = buf.readUInt8(offset);
offset += 1;

const id = buf.subarray(offset, offset + 16);
offset += 16;

const optLen = buf.readUInt8(offset);
offset += 1 + optLen;

const command = buf.readUInt8(offset);
offset += 1;

const port = buf.readUInt16BE(offset);
offset += 2;

const addressType = buf.readUInt8(offset);
offset += 1;

let host;
if (addressType === 1) { // IPV4
host = Array.from(buf.subarray(offset, offset + 4)).join('.');
offset += 4;
} else if (addressType === 2) { // DOMAIN
const len = buf.readUInt8(offset++);
host = buf.subarray(offset, offset + len).toString();
offset += len;
} else if (addressType === 3) { // IPV6
const segments = [];
for (let i = 0; i < 8; i++) {
segments.push(buf.readUInt16BE(offset).toString(16));
offset += 2;
}
host = segments.join(':');
} else {
throw new Error(`Unsupported address type: ${addressType}`);
}

return {version, id, command, host, port, offset};
}
const {parseHandshake, relayTraffic} = require('./vless-relay');

const uuid = Buffer.from(UUID.replace(/-/g, ''), 'hex');
const wss = new WebSocketServer({server});
wss.on('connection', ws => {
ws.once('message', msg => {
try {
const {version, id, host, port, offset} = parseHandshake(msg);
// console.log('version: ', version, 'id: ', id, 'host: ', host, 'port: ', port, 'offset: ', offset);
const handshake = parseHandshake(msg);
// console.log('version: ', handshake.version, 'id: ', handshake.id, 'host: ', handshake.host, 'port: ', handshake.port, 'offset: ', handshake.offset);

if (!id.equals(uuid)) {
if (!handshake.id.equals(uuid)) {
return ws.close();
}
ws.send(Buffer.from([version, 0]));
ws.send(Buffer.from([handshake.version, 0]));

const duplex = createWebSocketStream(ws);
const socket = net.connect({host, port}, () => {
socket.write(msg.slice(offset));
duplex.pipe(socket).pipe(duplex);
});

// duplex.on('error', err => console.error('Duplex error: ', err));
// socket.on('error', err => console.error('Socket error: ', err));
duplex.on('error', () => {});
socket.on('error', () => {});

socket.on('close', () => ws.terminate());
duplex.on('close', () => socket.destroy());
relayTraffic(duplex, handshake, msg.subarray(handshake.offset), () => ws.terminate());

} catch (err) {
// console.error('Handshake error: ', err);
Expand Down
70 changes: 5 additions & 65 deletions app.tls.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const fs = require('fs');
const path = require('path');
const net = require('net');
const crypto = require('crypto');
const {URL} = require('url');
const {exec} = require('child_process');
Expand Down Expand Up @@ -96,83 +95,24 @@ const server = createServer(options, (req, res) => {
}
});

/**
* Refer to: https://xtls.github.io/development/protocols/vless.html
* Parse the client handshake message and extract the version, UUID, target host/port and message offset
* @param {Buffer} buf Handshake Message Buffer
* @returns {{version:number, id:Buffer, command:number, host:string, port:number, offset:number}}
*/
function parseHandshake(buf) {
let offset = 0;
const version = buf.readUInt8(offset);
offset += 1;

const id = buf.subarray(offset, offset + 16);
offset += 16;

const optLen = buf.readUInt8(offset);
offset += 1 + optLen;

const command = buf.readUInt8(offset);
offset += 1;

const port = buf.readUInt16BE(offset);
offset += 2;

const addressType = buf.readUInt8(offset);
offset += 1;

let host;
if (addressType === 1) { // IPV4
host = Array.from(buf.subarray(offset, offset + 4)).join('.');
offset += 4;
} else if (addressType === 2) { // DOMAIN
const len = buf.readUInt8(offset++);
host = buf.subarray(offset, offset + len).toString();
offset += len;
} else if (addressType === 3) { // IPV6
const segments = [];
for (let i = 0; i < 8; i++) {
segments.push(buf.readUInt16BE(offset).toString(16));
offset += 2;
}
host = segments.join(':');
} else {
throw new Error(`Unsupported address type: ${addressType}`);
}

return {version, id, command, host, port, offset};
}
const {parseHandshake, relayTraffic} = require('./vless-relay');

const uuid = Buffer.from(UUID.replace(/-/g, ''), 'hex');
const wss = new WebSocketServer({server});
wss.on('connection', ws => {
ws.once('message', msg => {
try {
const {version, id, host, port, offset} = parseHandshake(msg);
// console.log('version: ', version, 'id: ', id, 'host: ', host, 'port: ', port, 'offset: ', offset);
const handshake = parseHandshake(msg);

if (!id.equals(uuid)) {
if (!handshake.id.equals(uuid)) {
return ws.close();
}
ws.send(Buffer.from([version, 0]));
ws.send(Buffer.from([handshake.version, 0]));

const duplex = createWebSocketStream(ws);
const socket = net.connect({host, port}, () => {
socket.write(msg.slice(offset));
duplex.pipe(socket).pipe(duplex);
});

// duplex.on('error', err => console.error('Duplex error: ', err));
// socket.on('error', err => console.error('Socket error: ', err));
duplex.on('error', () => {});
socket.on('error', () => {});

socket.on('close', () => ws.terminate());
duplex.on('close', () => socket.destroy());
relayTraffic(duplex, handshake, msg.subarray(handshake.offset), () => ws.terminate());

} catch (err) {
// console.error('Handshake error: ', err);
ws.close();
}
});
Expand Down
Loading