V2rayN is a Windows client for V2Ray. It is a graphical user interface (GUI) for V2Ray, a tool for bypassing internet censorship. It provides users with an easy-to-use interface to access and manage their V2Ray servers. V2rayN is available for free and is open source.
V2rayN provides a few features that make it easier to use V2Ray. It allows users to quickly switch between different servers, set up custom routing rules, and monitor their connection status. It also provides a few additional features such as automatic connection and the ability to export and import configuration files.
Note: Starting from Nov 23 2024 (Based on my testing, most likely earlier, but not earlier than Nov 16), Cloudflare is checking the code in the new deployment for this kind of workers or pages. The existing ones have not got issue, but new deployment will get an error 1101 “Worker threw exception”.
- https://github.com/vfarid/v2ray-worker
- https://github.com/Vauth/vless-cf
- https://github.com/51sec/ss_generator
V2rayN Features
Supports V2Ray, Shadowsocks, Trojan and VMess protocols. Offers support for multiple servers and custom routing rules. Configuration file import and export support. Supports many different obfuscation methods like HTTP/2, QUIC and WebSocket.
Vless protocol:
How to Download and Which one to Download
1 Go to release page
Release Page: Github release page V2rayN
Or other download page:
v2fly/xray Clients
-
v2fly/xray Windows Client v2rayN
-
xray Android App v2rayNG
-
v2fly Android App v2flyNG
clash 客户端
-
clash Windows客户端 clashN
2 You can choose pre-release version or latest release version
Click version number, here we are using version 7.0.6 as an example
3 Select the right file to download.
I am using Windows OS, and usually go with
4 Download and extract the file. Double click v2rayN.exe file to start this V2ray client.
Get free web service from sshOcean
Creae a server in V2rayN
Since we have copied our v2ray VLESS link, we can choose import shared links from Clipboard this option:
Switch Enable Tun to on, and choose System proxy to set system proxy
Configuration successful
[VLESS] sshocean-netsec.cagmail.com-ws(hu2***site:443)
Microsoft Windows NT 10.0.19045.0 – 64
Start service (2024/11/03 14:15:06)…
Xray 24.10.31 (Xray, Penetrates Everything.) 4ec5c78 (go1.23.2 windows/amd64)
A unified platform for anti-censorship.
2024/11/03 14:15:06 Using default config: C:\Users\User\Downloads\v2rayN-windows-64-SelfContained-With-Core\v2rayN-windows-64-SelfContained-With-Core\guiConfigs\config.json
2024/11/03 14:15:06 [Info] infra/conf/serial: Reading config: &{Name:C:\Users\User\Downloads\v2rayN-windows-64-SelfContained-With-Core\v2rayN-windows-64-SelfContained-With-Core\guiConfigs\config.json Format:json}
2024/11/03 14:15:07 [Warning] core: Xray 24.10.31 started
2024/11/03 14:15:07 from 127.0.0.1:4018 accepted //www.google.com:443 [http -> proxy]
2024/11/03 14:15:09 The ping of current service: 112 ms
Configuration successful
[VLESS] sshocean-netsec.cagmail.com-ws(hu2***site:443)
Microsoft Windows NT 10.0.19045.0 – 64
Start service (2024/11/03 14:15:15)…
Xray 24.10.31 (Xray, Penetrates Everything.) 4ec5c78 (go1.23.2 windows/amd64)
A unified platform for anti-censorship.
2024/11/03 14:15:15 Using default config: C:\Users\User\Downloads\v2rayN-windows-64-SelfContained-With-Core\v2rayN-windows-64-SelfContained-With-Core\guiConfigs\config.json
2024/11/03 14:15:15 [Info] infra/conf/serial: Reading config: &{Name:C:\Users\User\Downloads\v2rayN-windows-64-SelfContained-With-Core\v2rayN-windows-64-SelfContained-With-Core\guiConfigs\config.json Format:json}
2024/11/03 14:15:16 [Warning] core: Xray 24.10.31 started
2024/11/03 14:15:17 from 127.0.0.1:4037 accepted //www.google.com:443 [http -> proxy]
2024/11/03 14:15:18 from udp:127.0.0.1:54581 accepted udp:8.8.8.8:53 [socks -> proxy]
2024/11/03 14:15:19 The ping of current service: 245 ms
2024/11/03 14:15:19 System proxy setting is changed – ForcedChange
2024/11/03 14:15:19 from 127.0.0.1:9615 accepted //mtalk.google.com:5228 [http -> proxy]
2024/11/03 14:15:20 from 127.0.0.1:9634 accepted //teams.events.data.microsoft.com:443 [http -> proxy]
After done, you can set back the settings of “System Proxy” to Clear system proxy.
Use Google Colab to Generate SS Nodes
sshocean can generate multiple types of secure proxy / vpn servers. But each time you only can get one server. Here is a way you can generate 12 different SS (Shadowsocks) servers at one time.
Shadowsocks is a secure split proxy loosely based on SOCKS5. The Shadowsocks local component (ss-local) acts like a traditional SOCKS5 server and provides proxy service to clients. It encrypts and forwards data streams and packets from the client to the Shadowsocks remote component (ss-remote), which decrypts and forwards to the target. Replies from target are similarly encrypted and relayed by ss-remote back to ss-local, which decrypts and eventually returns to the original client.
- Github: https://github.com/51sec/ss_generator
import base64
import json
import pyaes
import binascii
from datetime import datetime
a = ‘http://api.skrapp.net/api/serverlist’
b = {
‘accept’: ‘/’,
‘accept-language’: ‘zh-Hans-CN;q=1, en-CN;q=0.9’,
‘appversion’: ‘1.3.1’,
‘user-agent’: ‘SkrKK/1.3.1 (iPhone; iOS 13.5; Scale/2.00)’,
‘content-type’: ‘application/x-www-form-urlencoded’,
‘Cookie’: ‘PHPSESSID=fnffo1ivhvt0ouo6ebqn86a0d4’
}
c = {‘data’: ‘4265a9c353cd8624fd2bc7b5d75d2f18b1b5e66ccd37e2dfa628bcb8f73db2f14ba98bc6a1d8d0d1c7ff1ef0823b11264d0addaba2bd6a30bdefe06f4ba994ed’}
d = b’65151f8d966bf596′
e = b’88ca0f0ea1ecf975′
def f(g, d, e):
h = pyaes.AESModeOfOperationCBC(d, iv=e)
i = b”.join(h.decrypt(g[j:j+16]) for j in range(0, len(g), 16))
return i[:-i[-1]]
j = requests.post(a, headers=b, data=c)
if j.status_code == 200:
k = j.text.strip()
l = binascii.unhexlify(k)
m = f(l, d, e)
n = json.loads(m)
print(“youtube.com/netsec”)
for o in n[‘data’]:
p = f”aes-256-cfb:{o[‘password’]}@{o[‘ip’]}:{o[‘port’]}”
q = base64.b64encode(p.encode(‘utf-8’)).decode(‘utf-8’)
r = f”ss://{q}#{o[‘title’]}”
print(r)
Use Replit.com to Generate Server List
Log into Replit.com
- Github: https://github.com/51sec/ss_generator
import base64
import json
import pyaes
import binascii
from datetime import datetime
a = ‘http://api.skrapp.net/api/serverlist’
b = {
‘accept’: ‘/’,
‘accept-language’: ‘zh-Hans-CN;q=1, en-CN;q=0.9’,
‘appversion’: ‘1.3.1’,
‘user-agent’: ‘SkrKK/1.3.1 (iPhone; iOS 13.5; Scale/2.00)’,
‘content-type’: ‘application/x-www-form-urlencoded’,
‘Cookie’: ‘PHPSESSID=fnffo1ivhvt0ouo6ebqn86a0d4’
}
c = {‘data’: ‘4265a9c353cd8624fd2bc7b5d75d2f18b1b5e66ccd37e2dfa628bcb8f73db2f14ba98bc6a1d8d0d1c7ff1ef0823b11264d0addaba2bd6a30bdefe06f4ba994ed’}
d = b’65151f8d966bf596′
e = b’88ca0f0ea1ecf975′
def f(g, d, e):
h = pyaes.AESModeOfOperationCBC(d, iv=e)
i = b”.join(h.decrypt(g[j:j+16]) for j in range(0, len(g), 16))
return i[:-i[-1]]
j = requests.post(a, headers=b, data=c)
if j.status_code == 200:
k = j.text.strip()
l = binascii.unhexlify(k)
m = f(l, d, e)
n = json.loads(m)
print(“youtube.com/netsec”)
for o in n[‘data’]:
p = f”aes-256-cfb:{o[‘password’]}@{o[‘ip’]}:{o[‘port’]}”
q = base64.b64encode(p.encode(‘utf-8’)).decode(‘utf-8’)
r = f”ss://{q}#{o[‘title’]}”
print(r)
Use Cloudflare to Generate vless nodes
Github project:
- https://github.com/51sec/ss_generator/blob/main/worker2vless.js
- Original one: https://github.com/zizifn/edgetunnel/tree/main/src
// <!–GAMFC–>version base on commit 5a112a0a0994b8bb834427ac84133501407f6413, time is 2024-10-11 04:49:19 UTC<!–GAMFC-END–>.
// @ts-ignore
import { connect } from ‘cloudflare:sockets’;
// How to generate your own UUID:
// [Windows] Press “Win + R”, input cmd and run: Powershell -NoExit -Command “[guid]::NewGuid()”
let userID = ‘d342d11e-d424-4583-b36e-524ab1f0afa4’;
let proxyIP = ”;
if (!isValidUUID(userID)) {
throw new Error(‘uuid is not valid’);
}
export default {
/**
* @param {import(“@cloudflare/workers-types”).Request} request
* @param {{UUID: string, PROXYIP: string}} env
* @param {import(“@cloudflare/workers-types”).ExecutionContext} ctx
* @returns {Promise<Response>}
*/
async fetch(request, env, ctx) {
try {
userID = env.UUID || userID;
proxyIP = env.PROXYIP || proxyIP;
const upgradeHeader = request.headers.get(‘Upgrade’);
if (!upgradeHeader || upgradeHeader !== ‘websocket’) {
const url = new URL(request.url);
switch (url.pathname) {
case ‘/’:
return new Response(JSON.stringify(request.cf), { status: 200 });
case `/${userID}`: {
const vlessConfig = getVLESSConfig(userID, request.headers.get(‘Host’));
return new Response(`${vlessConfig}`, {
status: 200,
headers: {
“Content-Type”: “text/plain;charset=utf-8”,
}
});
}
default:
return new Response(‘Not found’, { status: 404 });
}
} else {
return await vlessOverWSHandler(request);
}
} catch (err) {
/** @type {Error} */ let e = err;
return new Response(e.toString());
}
},
};
/**
*
* @param {import(“@cloudflare/workers-types”).Request} request
*/
async function vlessOverWSHandler(request) {
/** @type {import(“@cloudflare/workers-types”).WebSocket[]} */
// @ts-ignore
const webSocketPair = new WebSocketPair();
const [client, webSocket] = Object.values(webSocketPair);
webSocket.accept();
let address = ”;
let portWithRandomLog = ”;
const log = (/** @type {string} */ info, /** @type {string | undefined} */ event) => {
console.log(`[${address}:${portWithRandomLog}] ${info}`, event || ”);
};
const earlyDataHeader = request.headers.get(‘sec-websocket-protocol’) || ”;
const readableWebSocketStream = makeReadableWebSocketStream(webSocket, earlyDataHeader, log);
/** @type {{ value: import(“@cloudflare/workers-types”).Socket | null}}*/
let remoteSocketWapper = {
value: null,
};
let udpStreamWrite = null;
let isDns = false;
// ws –> remote
readableWebSocketStream.pipeTo(new WritableStream({
async write(chunk, controller) {
if (isDns && udpStreamWrite) {
return udpStreamWrite(chunk);
}
if (remoteSocketWapper.value) {
const writer = remoteSocketWapper.value.writable.getWriter()
await writer.write(chunk);
writer.releaseLock();
return;
}
const {
hasError,
message,
portRemote = 443,
addressRemote = ”,
rawDataIndex,
vlessVersion = new Uint8Array([0, 0]),
isUDP,
} = processVlessHeader(chunk, userID);
address = addressRemote;
portWithRandomLog = `${portRemote}–${Math.random()} ${isUDP ? ‘udp ‘ : ‘tcp ‘
} `;
if (hasError) {
// controller.error(message);
throw new Error(message); // cf seems has bug, controller.error will not end stream
// webSocket.close(1000, message);
return;
}
// if UDP but port not DNS port, close it
if (isUDP) {
if (portRemote === 53) {
isDns = true;
} else {
// controller.error(‘UDP proxy only enable for DNS which is port 53’);
throw new Error(‘UDP proxy only enable for DNS which is port 53’); // cf seems has bug, controller.error will not end stream
return;
}
}
// [“version”, “附加信息长度 N”]
const vlessResponseHeader = new Uint8Array([vlessVersion[0], 0]);
const rawClientData = chunk.slice(rawDataIndex);
// TODO: support udp here when cf runtime has udp support
if (isDns) {
const { write } = await handleUDPOutBound(webSocket, vlessResponseHeader, log);
udpStreamWrite = write;
udpStreamWrite(rawClientData);
return;
}
handleTCPOutBound(remoteSocketWapper, addressRemote, portRemote, rawClientData, webSocket, vlessResponseHeader, log);
},
close() {
log(`readableWebSocketStream is close`);
},
abort(reason) {
log(`readableWebSocketStream is abort`, JSON.stringify(reason));
},
})).catch((err) => {
log(‘readableWebSocketStream pipeTo error’, err);
});
return new Response(null, {
status: 101,
// @ts-ignore
webSocket: client,
});
}
/**
* Handles outbound TCP connections.
*
* @param {any} remoteSocket
* @param {string} addressRemote The remote address to connect to.
* @param {number} portRemote The remote port to connect to.
* @param {Uint8Array} rawClientData The raw client data to write.
* @param {import(“@cloudflare/workers-types”).WebSocket} webSocket The WebSocket to pass the remote socket to.
* @param {Uint8Array} vlessResponseHeader The VLESS response header.
* @param {function} log The logging function.
* @returns {Promise<void>} The remote socket.
*/
async function handleTCPOutBound(remoteSocket, addressRemote, portRemote, rawClientData, webSocket, vlessResponseHeader, log,) {
async function connectAndWrite(address, port) {
/** @type {import(“@cloudflare/workers-types”).Socket} */
const tcpSocket = connect({
hostname: address,
port: port,
});
remoteSocket.value = tcpSocket;
log(`connected to ${address}:${port}`);
const writer = tcpSocket.writable.getWriter();
await writer.write(rawClientData); // first write, nomal is tls client hello
writer.releaseLock();
return tcpSocket;
}
// if the cf connect tcp socket have no incoming data, we retry to redirect ip
async function retry() {
const tcpSocket = await connectAndWrite(proxyIP || addressRemote, portRemote)
// no matter retry success or not, close websocket
tcpSocket.closed.catch(error => {
console.log(‘retry tcpSocket closed error’, error);
}).finally(() => {
safeCloseWebSocket(webSocket);
})
remoteSocketToWS(tcpSocket, webSocket, vlessResponseHeader, null, log);
}
const tcpSocket = await connectAndWrite(addressRemote, portRemote);
// when remoteSocket is ready, pass to websocket
// remote–> ws
remoteSocketToWS(tcpSocket, webSocket, vlessResponseHeader, retry, log);
}
/**
*
* @param {import(“@cloudflare/workers-types”).WebSocket} webSocketServer
* @param {string} earlyDataHeader for ws 0rtt
* @param {(info: string)=> void} log for ws 0rtt
*/
function makeReadableWebSocketStream(webSocketServer, earlyDataHeader, log) {
let readableStreamCancel = false;
const stream = new ReadableStream({
start(controller) {
webSocketServer.addEventListener(‘message’, (event) => {
if (readableStreamCancel) {
return;
}
const message = event.data;
controller.enqueue(message);
});
// The event means that the client closed the client -> server stream.
// However, the server -> client stream is still open until you call close() on the server side.
// The WebSocket protocol says that a separate close message must be sent in each direction to fully close the socket.
webSocketServer.addEventListener(‘close’, () => {
// client send close, need close server
// if stream is cancel, skip controller.close
safeCloseWebSocket(webSocketServer);
if (readableStreamCancel) {
return;
}
controller.close();
}
);
webSocketServer.addEventListener(‘error’, (err) => {
log(‘webSocketServer has error’);
controller.error(err);
}
);
// for ws 0rtt
const { earlyData, error } = base64ToArrayBuffer(earlyDataHeader);
if (error) {
controller.error(error);
} else if (earlyData) {
controller.enqueue(earlyData);
}
},
pull(controller) {
// if ws can stop read if stream is full, we can implement backpressure
// https://streams.spec.whatwg.org/#example-rs-push-backpressure
},
cancel(reason) {
// 1. pipe WritableStream has error, this cancel will called, so ws handle server close into here
// 2. if readableStream is cancel, all controller.close/enqueue need skip,
// 3. but from testing controller.error still work even if readableStream is cancel
if (readableStreamCancel) {
return;
}
log(`ReadableStream was canceled, due to ${reason}`)
readableStreamCancel = true;
safeCloseWebSocket(webSocketServer);
}
});
return stream;
}
// https://xtls.github.io/development/protocols/vless.html
// https://github.com/zizifn/excalidraw-backup/blob/main/v2ray-protocol.excalidraw
/**
*
* @param { ArrayBuffer} vlessBuffer
* @param {string} userID
* @returns
*/
function processVlessHeader(
vlessBuffer,
userID
) {
if (vlessBuffer.byteLength < 24) {
return {
hasError: true,
message: ‘invalid data’,
};
}
const version = new Uint8Array(vlessBuffer.slice(0, 1));
let isValidUser = false;
let isUDP = false;
if (stringify(new Uint8Array(vlessBuffer.slice(1, 17))) === userID) {
isValidUser = true;
}
if (!isValidUser) {
return {
hasError: true,
message: ‘invalid user’,
};
}
const optLength = new Uint8Array(vlessBuffer.slice(17, 18))[0];
//skip opt for now
const command = new Uint8Array(
vlessBuffer.slice(18 + optLength, 18 + optLength + 1)
)[0];
// 0x01 TCP
// 0x02 UDP
// 0x03 MUX
if (command === 1) {
} else if (command === 2) {
isUDP = true;
} else {
return {
hasError: true,
message: `command ${command} is not support, command 01-tcp,02-udp,03-mux`,
};
}
const portIndex = 18 + optLength + 1;
const portBuffer = vlessBuffer.slice(portIndex, portIndex + 2);
// port is big-Endian in raw data etc 80 == 0x005d
const portRemote = new DataView(portBuffer).getUint16(0);
let addressIndex = portIndex + 2;
const addressBuffer = new Uint8Array(
vlessBuffer.slice(addressIndex, addressIndex + 1)
);
// 1–> ipv4 addressLength =4
// 2–> domain name addressLength=addressBuffer[1]
// 3–> ipv6 addressLength =16
const addressType = addressBuffer[0];
let addressLength = 0;
let addressValueIndex = addressIndex + 1;
let addressValue = ”;
switch (addressType) {
case 1:
addressLength = 4;
addressValue = new Uint8Array(
vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength)
).join(‘.’);
break;
case 2:
addressLength = new Uint8Array(
vlessBuffer.slice(addressValueIndex, addressValueIndex + 1)
)[0];
addressValueIndex += 1;
addressValue = new TextDecoder().decode(
vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength)
);
break;
case 3:
addressLength = 16;
const dataView = new DataView(
vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength)
);
// 2001:0db8:85a3:0000:0000:8a2e:0370:7334
const ipv6 = [];
for (let i = 0; i < 8; i++) {
ipv6.push(dataView.getUint16(i * 2).toString(16));
}
addressValue = ipv6.join(‘:’);
// seems no need add [] for ipv6
break;
default:
return {
hasError: true,
message: `invild addressType is ${addressType}`,
};
}
if (!addressValue) {
return {
hasError: true,
message: `addressValue is empty, addressType is ${addressType}`,
};
}
return {
hasError: false,
addressRemote: addressValue,
addressType,
portRemote,
rawDataIndex: addressValueIndex + addressLength,
vlessVersion: version,
isUDP,
};
}
/**
*
* @param {import(“@cloudflare/workers-types”).Socket} remoteSocket
* @param {import(“@cloudflare/workers-types”).WebSocket} webSocket
* @param {ArrayBuffer} vlessResponseHeader
* @param {(() => Promise<void>) | null} retry
* @param {*} log
*/
async function remoteSocketToWS(remoteSocket, webSocket, vlessResponseHeader, retry, log) {
// remote–> ws
let remoteChunkCount = 0;
let chunks = [];
/** @type {ArrayBuffer | null} */
let vlessHeader = vlessResponseHeader;
let hasIncomingData = false; // check if remoteSocket has incoming data
await remoteSocket.readable
.pipeTo(
new WritableStream({
start() {
},
/**
*
* @param {Uint8Array} chunk
* @param {*} controller
*/
async write(chunk, controller) {
hasIncomingData = true;
// remoteChunkCount++;
if (webSocket.readyState !== WS_READY_STATE_OPEN) {
controller.error(
‘webSocket.readyState is not open, maybe close’
);
}
if (vlessHeader) {
webSocket.send(await new Blob([vlessHeader, chunk]).arrayBuffer());
vlessHeader = null;
} else {
// seems no need rate limit this, CF seems fix this??..
// if (remoteChunkCount > 20000) {
// // cf one package is 4096 byte(4kb), 4096 * 20000 = 80M
// await delay(1);
// }
webSocket.send(chunk);
}
},
close() {
log(`remoteConnection!.readable is close with hasIncomingData is ${hasIncomingData}`);
// safeCloseWebSocket(webSocket); // no need server close websocket frist for some case will casue HTTP ERR_CONTENT_LENGTH_MISMATCH issue, client will send close event anyway.
},
abort(reason) {
console.error(`remoteConnection!.readable abort`, reason);
},
})
)
.catch((error) => {
console.error(
`remoteSocketToWS has exception `,
error.stack || error
);
safeCloseWebSocket(webSocket);
});
// seems is cf connect socket have error,
// 1. Socket.closed will have error
// 2. Socket.readable will be close without any data coming
if (hasIncomingData === false && retry) {
log(`retry`)
retry();
}
}
/**
*
* @param {string} base64Str
* @returns
*/
function base64ToArrayBuffer(base64Str) {
if (!base64Str) {
return { error: null };
}
try {
// go use modified Base64 for URL rfc4648 which js atob not support
base64Str = base64Str.replace(/-/g, ‘+’).replace(/_/g, ‘/’);
const decode = atob(base64Str);
const arryBuffer = Uint8Array.from(decode, (c) => c.charCodeAt(0));
return { earlyData: arryBuffer.buffer, error: null };
} catch (error) {
return { error };
}
}
/**
* This is not real UUID validation
* @param {string} uuid
*/
function isValidUUID(uuid) {
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
return uuidRegex.test(uuid);
}
const WS_READY_STATE_OPEN = 1;
const WS_READY_STATE_CLOSING = 2;
/**
* Normally, WebSocket will not has exceptions when close.
* @param {import(“@cloudflare/workers-types”).WebSocket} socket
*/
function safeCloseWebSocket(socket) {
try {
if (socket.readyState === WS_READY_STATE_OPEN || socket.readyState === WS_READY_STATE_CLOSING) {
socket.close();
}
} catch (error) {
console.error(‘safeCloseWebSocket error’, error);
}
}
const byteToHex = [];
for (let i = 0; i < 256; ++i) {
byteToHex.push((i + 256).toString(16).slice(1));
}
function unsafeStringify(arr, offset = 0) {
return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + “-” + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + “-” + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + “-” + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + “-” + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
}
function stringify(arr, offset = 0) {
const uuid = unsafeStringify(arr, offset);
if (!isValidUUID(uuid)) {
throw TypeError(“Stringified UUID is invalid”);
}
return uuid;
}
/**
*
* @param {import(“@cloudflare/workers-types”).WebSocket} webSocket
* @param {ArrayBuffer} vlessResponseHeader
* @param {(string)=> void} log
*/
async function handleUDPOutBound(webSocket, vlessResponseHeader, log) {
let isVlessHeaderSent = false;
const transformStream = new TransformStream({
start(controller) {
},
transform(chunk, controller) {
// udp message 2 byte is the the length of udp data
// TODO: this should have bug, beacsue maybe udp chunk can be in two websocket message
for (let index = 0; index < chunk.byteLength;) {
const lengthBuffer = chunk.slice(index, index + 2);
const udpPakcetLength = new DataView(lengthBuffer).getUint16(0);
const udpData = new Uint8Array(
chunk.slice(index + 2, index + 2 + udpPakcetLength)
);
index = index + 2 + udpPakcetLength;
controller.enqueue(udpData);
}
},
flush(controller) {
}
});
// only handle dns udp for now
transformStream.readable.pipeTo(new WritableStream({
async write(chunk) {
const resp = await fetch(‘https://1.1.1.1/dns-query’,
{
method: ‘POST’,
headers: {
‘content-type’: ‘application/dns-message’,
},
body: chunk,
})
const dnsQueryResult = await resp.arrayBuffer();
const udpSize = dnsQueryResult.byteLength;
// console.log([…new Uint8Array(dnsQueryResult)].map((x) => x.toString(16)));
const udpSizeBuffer = new Uint8Array([(udpSize >> 8) & 0xff, udpSize & 0xff]);
if (webSocket.readyState === WS_READY_STATE_OPEN) {
log(`doh success and dns message length is ${udpSize}`);
if (isVlessHeaderSent) {
webSocket.send(await new Blob([udpSizeBuffer, dnsQueryResult]).arrayBuffer());
} else {
webSocket.send(await new Blob([vlessResponseHeader, udpSizeBuffer, dnsQueryResult]).arrayBuffer());
isVlessHeaderSent = true;
}
}
}
})).catch((error) => {
log(‘dns udp has error’ + error)
});
const writer = transformStream.writable.getWriter();
return {
/**
*
* @param {Uint8Array} chunk
*/
write(chunk) {
writer.write(chunk);
}
};
}
/**
*
* @param {string} userID
* @param {string | null} hostName
* @returns {string}
*/
function getVLESSConfig(userID, hostName) {
const vlessMain = `vless://${userID}\u0040${hostName}:443?encryption=none&security=tls&sni=${hostName}&fp=randomized&type=ws&host=${hostName}&path=%2F%3Fed%3D2048#${hostName}`
return `
################################################################
v2ray
—————————————————————
${vlessMain}
—————————————————————
################################################################
clash-meta
—————————————————————
– type: vless
name: ${hostName}
server: ${hostName}
port: 443
uuid: ${userID}
network: ws
tls: true
udp: false
sni: ${hostName}
client-fingerprint: chrome
ws-opts:
path: “/?ed=2048”
headers:
host: ${hostName}
—————————————————————
################################################################
`;
}
- const proxyIPs = ['cdn.xn--b6gac.eu.org', 'cdn-all.xn--b6gac.eu.org', 'workers.cloudflare.cyou'];
-
- // if you want to use ipv6 or single proxyIP, please add comment at this line and remove comment at the next line
- let proxyIP = proxyIPs[Math.floor(Math.random() * proxyIPs.length)];
- // use single proxyIP instead of random
- // let proxyIP = 'cdn.xn--b6gac.eu.org';
- // ipv6 proxyIP example remove comment to use
- // let proxyIP = "[2a01:4f8:c2c:123f:64:5:6810:c55a]"
Two things you might want to change:
1. UUID
2. Proxyip
For proxy ip, in the original github code, it is empty, but we can choose one from following proxyIP list maintained by someone else on Internet.
- aliyun.classelivre.eu.org 天城大佬
- cm.godns.onflashdrive.app 天城大佬
- 3k.fxxk.dedyn.io 3K大佬
- vmess.fxxk.dedyn.io CM大佬
- vless.fxxk.dedyn.io CM大佬
- in my example, it is same as 优选IP (Address)
- It can be different and some other hosts on internet
- such as th.amazinglinyy.workers.dev
- in my example, it is same as 伪装域名 (host)
- But it can be different.
- such as www.visa.com:8880 to replace my v.51sec.workers.dev:443
CTRL+A to select all, then CTRL+C. CTRL+V paste into v2rayN.
将复制的 V2ray 配置链接粘贴到 V2rayN 客户端,地址填入优选 IP ,即可成功科学上网。优选 IP 可通过 https://stock.hostmonit.com/CloudFlareYes.
-
decade00-0000-4000-a000-000000000000
v2ray
---------------------------------------------------------------
vless://[email protected]:443?encryption=none&security=tls&sni=v.51sec.workers.dev&fp=randomized&type=ws&host=v.51sec.workers.dev&path=%2F%3Fed%3D2048#v.51sec.workers.dev
---------------------------------------------------------------
################################################################
clash-meta
---------------------------------------------------------------
- type: vless
name: v.51sec.workers.dev
server: v.51sec.workers.dev
port: 443
uuid: decade00-0000-4000-a000-000000000000
network: ws
tls: true
udp: false
sni: v.51sec.workers.dev
client-fingerprint: chrome
ws-opts:
path: "/?ed=2048"
headers:
host: v.51sec.workers.dev
---------------------------------------------------------------
################################################################
Performance Check
Alternatively, you can check the latency via HTTPS request from your browser (different than ping) and verify if your ISP is not blocked or if you will be bypassing the firewall following the URL:
ping falkenstein-lb-1.vs.secologist.com
Videos
References
- How to Set up V2rayN on Windows
- 免费vpn 最新技术 简单部署 轻松秒开油管4K 只要复制粘贴 谷歌colab
- 神秘代码一键生成免费上网节点,速度惊人! 解锁流媒体 4K秒开|支持sing-box,nekobox,v2rayN等
- https://replit.com/
- https://colab.research.google.com/
- v2ray-worker – https://github.com/vfarid/v2ray-worker
- https://github.com/zizifn/edgetunnel
- Built-in trojan config generator is based on ca110us/epeius
- Proxy IPs source: https://rentry.co/CF-proxyIP
- https://ygkkk.blogspot.com/2023/07/cfworkers-vless.html
- https://github.com/yonggekkk/Cloudflare_vless_trojan/tree/main