mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-03-19 05:19:47 +08:00
135 lines
4.4 KiB
Dart
135 lines
4.4 KiB
Dart
/*
|
|
* Copyright 2023 WangHongEn
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import 'dart:typed_data';
|
|
|
|
class TLS {
|
|
///从TLS Client Hello 获取支持的协议
|
|
static List<String>? supportProtocols(Uint8List data) {
|
|
try {
|
|
int sessionLength = data[43];
|
|
int pos = 44 + sessionLength;
|
|
if (data.length < pos + 2) return null;
|
|
|
|
int cipherSuitesLength = data.buffer.asByteData().getUint16(pos);
|
|
pos += 2 + cipherSuitesLength;
|
|
if (data.length < pos + 1) return null;
|
|
|
|
int compressionMethodsLength = data[pos];
|
|
pos += 1 + compressionMethodsLength;
|
|
if (data.length < pos + 2) return null;
|
|
|
|
int extensionsLength = data.buffer.asByteData().getUint16(pos);
|
|
pos += 2;
|
|
if (data.length < pos + extensionsLength) return null;
|
|
|
|
List<String> protocols = [];
|
|
|
|
int end = pos + extensionsLength;
|
|
while (pos + 4 <= end) {
|
|
int extensionType = data.buffer.asByteData().getUint16(pos);
|
|
int extensionLength = data.buffer.asByteData().getUint16(pos + 2);
|
|
pos += 4;
|
|
|
|
if (extensionType == 16 /* ALPN */) {
|
|
if (pos + 2 > end) return protocols;
|
|
int alpnExtensionLength = data.buffer.asByteData().getUint16(pos);
|
|
pos += 2;
|
|
if (pos + alpnExtensionLength > end) return protocols;
|
|
|
|
int alpnEnd = pos + alpnExtensionLength;
|
|
while (pos + 1 <= alpnEnd) {
|
|
int protocolLength = data[pos];
|
|
pos += 1;
|
|
if (pos + protocolLength > alpnEnd) return protocols;
|
|
|
|
String protocol = String.fromCharCodes(data.sublist(pos, pos + protocolLength));
|
|
protocols.add(protocol);
|
|
|
|
pos += protocolLength;
|
|
}
|
|
} else {
|
|
pos += extensionLength;
|
|
}
|
|
}
|
|
return protocols;
|
|
} catch (_) {
|
|
// Ignore errors, just return empty list
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
///判断是否是TLS Client Hello
|
|
static bool isTLSClientHello(Uint8List data) {
|
|
if (data.length < 43) return false;
|
|
if (data[0] != 0x16 /* handshake */) return false;
|
|
if (data[1] != 0x03 || data[2] < 0x00 || data[2] > 0x03) return false;
|
|
if (data[5] != 0x01 /* client_hello */) return false;
|
|
if (data[9] != 0x03 || data[10] < 0x00 || data[10] > 0x03) return false;
|
|
return true;
|
|
}
|
|
|
|
///从TLS Client Hello 解析域名
|
|
static String? getDomain(Uint8List data) {
|
|
try {
|
|
int sessionLength = data[43];
|
|
int pos = 44 + sessionLength;
|
|
if (data.length < pos + 2) return null;
|
|
|
|
int cipherSuitesLength = data.buffer.asByteData().getUint16(pos);
|
|
pos += 2 + cipherSuitesLength;
|
|
if (data.length < pos + 1) return null;
|
|
|
|
int compressionMethodsLength = data[pos];
|
|
pos += 1 + compressionMethodsLength;
|
|
if (data.length < pos + 2) return null;
|
|
|
|
int extensionsLength = data.buffer.asByteData().getUint16(pos);
|
|
pos += 2;
|
|
if (data.length < pos + extensionsLength) return null;
|
|
|
|
int end = pos + extensionsLength;
|
|
while (pos + 4 <= end) {
|
|
int extensionType = data.buffer.asByteData().getUint16(pos);
|
|
int extensionLength = data.buffer.asByteData().getUint16(pos + 2);
|
|
pos += 4;
|
|
|
|
if (extensionType == 0 /* server_name */) {
|
|
if (pos + 5 > end) return null;
|
|
int serverNameListLength = data.buffer.asByteData().getUint16(pos);
|
|
pos += 2;
|
|
if (pos + serverNameListLength > end) return null;
|
|
|
|
int serverNameType = data[pos];
|
|
int serverNameLength = data.buffer.asByteData().getUint16(pos + 1);
|
|
pos += 3;
|
|
if (serverNameType != 0 /* host_name */) return null;
|
|
if (pos + serverNameLength > end) return null;
|
|
|
|
return String.fromCharCodes(data.sublist(pos, pos + serverNameLength));
|
|
} else {
|
|
pos += extensionLength;
|
|
}
|
|
}
|
|
} catch (_) {
|
|
// Ignore errors, just return null
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|