отправка файлов, анимации с реанимации
This commit is contained in:
@@ -13,8 +13,8 @@ enum SocketState { disconnected, connecting, connected }
|
||||
/// Обёртка над TCP + TLS сокетом.
|
||||
/// Отдаёт сырые байты через [dataStream], сборкой пакетов занимается [PacketReceiver].
|
||||
class Connection {
|
||||
RawSecureSocket? _socket;
|
||||
StreamSubscription<RawSocketEvent>? _subscription;
|
||||
SecureSocket? _socket;
|
||||
StreamSubscription<Uint8List>? _subscription;
|
||||
SocketState _state = SocketState.disconnected;
|
||||
|
||||
final _dataController = StreamController<Uint8List>.broadcast();
|
||||
@@ -63,18 +63,7 @@ class Connection {
|
||||
logger.i('Подключено к $host:$port');
|
||||
|
||||
_subscription = _socket!.listen(
|
||||
(event) {
|
||||
if (event == RawSocketEvent.read) {
|
||||
final data = _socket?.read();
|
||||
if (data != null) {
|
||||
_dataController.add(data);
|
||||
}
|
||||
} else if (event == RawSocketEvent.readClosed ||
|
||||
event == RawSocketEvent.closed) {
|
||||
logger.w('Сокет закрыт сервером');
|
||||
disconnect();
|
||||
}
|
||||
},
|
||||
(data) => _dataController.add(data),
|
||||
onError: (Object error) {
|
||||
logger.e('Ошибка сокета: $error');
|
||||
disconnect();
|
||||
@@ -91,41 +80,41 @@ class Connection {
|
||||
}
|
||||
}
|
||||
|
||||
Future<RawSecureSocket> _openSecureSocket(
|
||||
Future<SecureSocket> _openSecureSocket(
|
||||
String host,
|
||||
int port,
|
||||
ProxySettings proxySettings, {
|
||||
Duration? timeout,
|
||||
}) async {
|
||||
RawSocket rawSocket;
|
||||
Socket socket;
|
||||
if (proxySettings.isEnabled) {
|
||||
final connector = ProxyConnector(proxySettings);
|
||||
rawSocket = await connector.connect(host, port);
|
||||
socket = await connector.connect(host, port);
|
||||
logger.i('Подключено через прокси ${proxySettings.type.name}');
|
||||
} else {
|
||||
rawSocket = timeout == null
|
||||
? await RawSocket.connect(host, port)
|
||||
: await RawSocket.connect(host, port, timeout: timeout);
|
||||
socket = timeout == null
|
||||
? await Socket.connect(host, port)
|
||||
: await Socket.connect(host, port, timeout: timeout);
|
||||
}
|
||||
final allowInsecure = await TlsConfig.isInsecureAllowed();
|
||||
if (allowInsecure) {
|
||||
logger.w(
|
||||
'TLS: проверка сертификата отключена (дебаг) — соединение уязвимо к MitM',
|
||||
);
|
||||
return RawSecureSocket.secure(
|
||||
rawSocket,
|
||||
return SecureSocket.secure(
|
||||
socket,
|
||||
host: host,
|
||||
onBadCertificate: (_) => true,
|
||||
);
|
||||
}
|
||||
return RawSecureSocket.secure(rawSocket, host: host);
|
||||
return SecureSocket.secure(socket, host: host);
|
||||
}
|
||||
|
||||
void write(Uint8List data) {
|
||||
if (_socket == null || !isConnected) {
|
||||
throw StateError('Нельзя писать: сокет не подключён');
|
||||
}
|
||||
_socket!.write(data);
|
||||
_socket!.add(data);
|
||||
}
|
||||
|
||||
Future<void> disconnect() async {
|
||||
@@ -136,7 +125,7 @@ class Connection {
|
||||
|
||||
if (socket != null) {
|
||||
try {
|
||||
socket.close();
|
||||
await socket.close();
|
||||
} catch (e) {
|
||||
logger.w('Ошибка при закрытии сокета: $e');
|
||||
}
|
||||
|
||||
@@ -6,28 +6,25 @@ import 'dart:typed_data';
|
||||
import '../config/proxy_config.dart';
|
||||
import '../utils/logger.dart';
|
||||
|
||||
/// Устанавливает TCP-соединение через SOCKS5 или HTTP CONNECT прокси.
|
||||
/// Возвращает [RawSocket], который никогда не слушался —
|
||||
/// его можно передать в [RawSecureSocket.secure].
|
||||
class ProxyConnector {
|
||||
final ProxySettings settings;
|
||||
|
||||
ProxyConnector(this.settings);
|
||||
|
||||
Future<RawSocket> connect(String targetHost, int targetPort) async {
|
||||
Future<Socket> connect(String targetHost, int targetPort) async {
|
||||
switch (settings.type) {
|
||||
case ProxyType.socks5:
|
||||
return _connectSocks5(targetHost, targetPort);
|
||||
case ProxyType.httpConnect:
|
||||
return _connectHttpConnect(targetHost, targetPort);
|
||||
case ProxyType.none:
|
||||
return RawSocket.connect(targetHost, targetPort);
|
||||
return Socket.connect(targetHost, targetPort);
|
||||
}
|
||||
}
|
||||
|
||||
// ── SOCKS5 (RFC 1928) ──────────────────────────────────────────────────
|
||||
|
||||
Future<RawSocket> _connectSocks5(String targetHost, int targetPort) async {
|
||||
Future<Socket> _connectSocks5(String targetHost, int targetPort) async {
|
||||
final proxySocket = await RawSocket.connect(settings.host, settings.port);
|
||||
logger.i('SOCKS5: подключено к прокси ${settings.host}:${settings.port}');
|
||||
|
||||
@@ -128,7 +125,7 @@ class ProxyConnector {
|
||||
|
||||
// ── HTTP CONNECT ────────────────────────────────────────────────────────
|
||||
|
||||
Future<RawSocket> _connectHttpConnect(
|
||||
Future<Socket> _connectHttpConnect(
|
||||
String targetHost,
|
||||
int targetPort,
|
||||
) async {
|
||||
@@ -194,19 +191,13 @@ class ProxyConnector {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Мост: создаём свежий сокет и проксируем через loopback ─────────────
|
||||
|
||||
/// После handshake proxy-сокет уже прослушан (single-subscription).
|
||||
/// Создаём пару локальных сокетов через loopback и проксируем данные
|
||||
/// между прокси-сокетом и одним концом. Второй конец возвращаем —
|
||||
/// он «свежий» и его можно передать в [RawSecureSocket.secure].
|
||||
Future<RawSocket> _bridgeToFreshSocket(
|
||||
Future<Socket> _bridgeToFreshSocket(
|
||||
RawSocket proxySocket,
|
||||
_RawSocketIO io,
|
||||
) async {
|
||||
RawServerSocket? server;
|
||||
ServerSocket? server;
|
||||
try {
|
||||
server = await RawServerSocket.bind(
|
||||
server = await ServerSocket.bind(
|
||||
InternetAddress.loopbackIPv4,
|
||||
0,
|
||||
);
|
||||
@@ -215,31 +206,36 @@ class ProxyConnector {
|
||||
proxySocket.close();
|
||||
rethrow;
|
||||
}
|
||||
final clientSide = await RawSocket.connect(
|
||||
final clientFuture = Socket.connect(
|
||||
InternetAddress.loopbackIPv4,
|
||||
server.port,
|
||||
);
|
||||
final serverSide = await server.first;
|
||||
final clientSide = await clientFuture;
|
||||
await server.close();
|
||||
|
||||
// proxy → local (через уже имеющуюся подписку _RawSocketIO)
|
||||
io.onData = (data) {
|
||||
serverSide.write(data);
|
||||
serverSide.add(data);
|
||||
};
|
||||
io.onClosed = () {
|
||||
serverSide.shutdown(SocketDirection.send);
|
||||
serverSide.close();
|
||||
};
|
||||
|
||||
// local → proxy
|
||||
serverSide.listen((event) {
|
||||
if (event == RawSocketEvent.read) {
|
||||
final data = serverSide.read();
|
||||
if (data != null) proxySocket.write(data);
|
||||
} else if (event == RawSocketEvent.readClosed ||
|
||||
event == RawSocketEvent.closed) {
|
||||
serverSide.listen(
|
||||
(data) {
|
||||
unawaited(io.write(data).catchError((Object _) {
|
||||
try {
|
||||
serverSide.destroy();
|
||||
} catch (_) {}
|
||||
}));
|
||||
},
|
||||
onError: (Object _) {
|
||||
proxySocket.shutdown(SocketDirection.send);
|
||||
}
|
||||
});
|
||||
},
|
||||
onDone: () {
|
||||
proxySocket.shutdown(SocketDirection.send);
|
||||
},
|
||||
);
|
||||
|
||||
// Сливаем данные, буферизованные во время handshake
|
||||
io.flushBuffered();
|
||||
|
||||
Reference in New Issue
Block a user