* Parse the VLESS header from the first chunk. Returns {ok, host, port, payload}. * VLESS v1 layout (simplified): * [0] version (1 byte) * [1-16] uuid (16 bytes) * [17] addons length N (1 byte) * [18..18+N] addons * [+1] command (1=TCP, 2=UDP) * [+2] port (big-endia
(buf, expectedUUID)
| 138 | * [..] payload |
| 139 | */ |
| 140 | function parseVLESSHeader(buf, expectedUUID) { |
| 141 | try { |
| 142 | if (buf.length < 18) return { ok: false, err: "too short" }; |
| 143 | const version = buf[0]; |
| 144 | const uuidBytes = buf.slice(1, 17); |
| 145 | const got = uuidToStr(uuidBytes); |
| 146 | if (got !== expectedUUID) return { ok: false, err: "uuid mismatch" }; |
| 147 | const addonsLen = buf[17]; |
| 148 | let i = 18 + addonsLen; |
| 149 | if (buf.length < i + 4) return { ok: false, err: "truncated" }; |
| 150 | const cmd = buf[i++]; |
| 151 | if (cmd !== 1) return { ok: false, err: "unsupported command (UDP/Mux not handled)" }; |
| 152 | const port = (buf[i] << 8) | buf[i + 1]; |
| 153 | i += 2; |
| 154 | const addrType = buf[i++]; |
| 155 | let host; |
| 156 | if (addrType === 1) { |
| 157 | // IPv4: 4 bytes. |
| 158 | if (buf.length < i + 4) return { ok: false, err: "truncated ipv4" }; |
| 159 | host = `${buf[i]}.${buf[i + 1]}.${buf[i + 2]}.${buf[i + 3]}`; |
| 160 | i += 4; |
| 161 | } else if (addrType === 2) { |
| 162 | // Domain: 1-byte length prefix + name. |
| 163 | if (buf.length < i + 1) return { ok: false, err: "truncated domain len" }; |
| 164 | const len = buf[i++]; |
| 165 | if (buf.length < i + len) return { ok: false, err: "truncated domain" }; |
| 166 | host = new TextDecoder().decode(buf.slice(i, i + len)); |
| 167 | i += len; |
| 168 | } else if (addrType === 3) { |
| 169 | // IPv6: 16 bytes. |
| 170 | if (buf.length < i + 16) return { ok: false, err: "truncated ipv6" }; |
| 171 | host = ipv6ToStr(buf.slice(i, i + 16)); |
| 172 | i += 16; |
| 173 | } else { |
| 174 | return { ok: false, err: `unknown addr type ${addrType}` }; |
| 175 | } |
| 176 | return { ok: true, version, host, port, payload: buf.slice(i) }; |
| 177 | } catch (e) { |
| 178 | return { ok: false, err: e.message }; |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | // Cloudflare-specific outbound TCP via the `connect` API. |
| 183 | async function cfConnect(host, port) { |