fix: 修复代理问题

This commit is contained in:
丹尼尔
2026-03-15 17:16:05 +08:00
parent 8b62c445fc
commit 15c9e1772a
100 changed files with 6157 additions and 69 deletions

View File

@@ -0,0 +1,21 @@
from .._abc import SyncSocketStream, AsyncSocketStream
class SyncConnector:
def connect(
self,
stream: SyncSocketStream,
host: str,
port: int,
):
raise NotImplementedError
class AsyncConnector:
async def connect(
self,
stream: AsyncSocketStream,
host: str,
port: int,
):
raise NotImplementedError

View File

@@ -0,0 +1,40 @@
from typing import Optional
from .._abc import AsyncResolver
from .._types import ProxyType
from .abc import AsyncConnector
from .socks5_async import Socks5AsyncConnector
from .socks4_async import Socks4AsyncConnector
from .http_async import HttpAsyncConnector
def create_connector(
proxy_type: ProxyType,
username: Optional[str],
password: Optional[str],
rdns: Optional[bool],
resolver: AsyncResolver,
) -> AsyncConnector:
if proxy_type == ProxyType.SOCKS4:
return Socks4AsyncConnector(
user_id=username,
rdns=rdns,
resolver=resolver,
)
if proxy_type == ProxyType.SOCKS5:
return Socks5AsyncConnector(
username=username,
password=password,
rdns=rdns,
resolver=resolver,
)
if proxy_type == ProxyType.HTTP:
return HttpAsyncConnector(
username=username,
password=password,
resolver=resolver,
)
raise ValueError(f'Invalid proxy type: {proxy_type}')

View File

@@ -0,0 +1,40 @@
from typing import Optional
from .._abc import SyncResolver
from .._types import ProxyType
from .abc import SyncConnector
from .socks5_sync import Socks5SyncConnector
from .socks4_sync import Socks4SyncConnector
from .http_sync import HttpSyncConnector
def create_connector(
proxy_type: ProxyType,
username: Optional[str],
password: Optional[str],
rdns: Optional[bool],
resolver: SyncResolver,
) -> SyncConnector:
if proxy_type == ProxyType.SOCKS4:
return Socks4SyncConnector(
user_id=username,
rdns=rdns,
resolver=resolver,
)
if proxy_type == ProxyType.SOCKS5:
return Socks5SyncConnector(
username=username,
password=password,
rdns=rdns,
resolver=resolver,
)
if proxy_type == ProxyType.HTTP:
return HttpSyncConnector(
username=username,
password=password,
resolver=resolver,
)
raise ValueError(f'Invalid proxy type: {proxy_type}')

View File

@@ -0,0 +1,38 @@
from typing import Optional
from .._abc import AsyncSocketStream, AsyncResolver
from .abc import AsyncConnector
from .._protocols import http
class HttpAsyncConnector(AsyncConnector):
def __init__(
self,
username: Optional[str],
password: Optional[str],
resolver: AsyncResolver,
):
self._username = username
self._password = password
self._resolver = resolver
async def connect(
self,
stream: AsyncSocketStream,
host: str,
port: int,
) -> http.ConnectReply:
conn = http.Connection()
request = http.ConnectRequest(
host=host,
port=port,
username=self._username,
password=self._password,
)
data = conn.send(request)
await stream.write_all(data)
data = await stream.read()
reply: http.ConnectReply = conn.receive(data)
return reply

View File

@@ -0,0 +1,38 @@
from typing import Optional
from .._abc import SyncSocketStream, SyncResolver
from .abc import SyncConnector
from .._protocols import http
class HttpSyncConnector(SyncConnector):
def __init__(
self,
username: Optional[str],
password: Optional[str],
resolver: SyncResolver,
):
self._username = username
self._password = password
self._resolver = resolver
def connect(
self,
stream: SyncSocketStream,
host: str,
port: int,
) -> http.ConnectReply:
conn = http.Connection()
request = http.ConnectRequest(
host=host,
port=port,
username=self._username,
password=self._password,
)
data = conn.send(request)
stream.write_all(data)
data = stream.read()
reply: http.ConnectReply = conn.receive(data)
return reply

View File

@@ -0,0 +1,45 @@
import socket
from typing import Optional
from .._abc import AsyncSocketStream, AsyncResolver
from .abc import AsyncConnector
from .._protocols import socks4
from .._helpers import is_ip_address
class Socks4AsyncConnector(AsyncConnector):
def __init__(
self,
user_id: Optional[str],
rdns: Optional[bool],
resolver: AsyncResolver,
):
if rdns is None:
rdns = False
self._user_id = user_id
self._rdns = rdns
self._resolver = resolver
async def connect(
self,
stream: AsyncSocketStream,
host: str,
port: int,
) -> socks4.ConnectReply:
conn = socks4.Connection()
if not is_ip_address(host) and not self._rdns:
_, host = await self._resolver.resolve(
host,
family=socket.AF_INET,
)
request = socks4.ConnectRequest(host=host, port=port, user_id=self._user_id)
data = conn.send(request)
await stream.write_all(data)
data = await stream.read_exact(socks4.ConnectReply.SIZE)
reply: socks4.ConnectReply = conn.receive(data)
return reply

View File

@@ -0,0 +1,45 @@
import socket
from typing import Optional
from .._abc import SyncSocketStream, SyncResolver
from .abc import SyncConnector
from .._protocols import socks4
from .._helpers import is_ip_address
class Socks4SyncConnector(SyncConnector):
def __init__(
self,
user_id: Optional[str],
rdns: Optional[bool],
resolver: SyncResolver,
):
if rdns is None:
rdns = False
self._user_id = user_id
self._rdns = rdns
self._resolver = resolver
def connect(
self,
stream: SyncSocketStream,
host: str,
port: int,
) -> socks4.ConnectReply:
conn = socks4.Connection()
if not is_ip_address(host) and not self._rdns:
_, host = self._resolver.resolve(
host,
family=socket.AF_INET,
)
request = socks4.ConnectRequest(host=host, port=port, user_id=self._user_id)
data = conn.send(request)
stream.write_all(data)
data = stream.read_exact(socks4.ConnectReply.SIZE)
reply: socks4.ConnectReply = conn.receive(data)
return reply

View File

@@ -0,0 +1,95 @@
import socket
from typing import Optional
from .._abc import AsyncSocketStream, AsyncResolver
from .abc import AsyncConnector
from .._protocols import socks5
from .._helpers import is_ip_address
class Socks5AsyncConnector(AsyncConnector):
def __init__(
self,
username: Optional[str],
password: Optional[str],
rdns: Optional[bool],
resolver: AsyncResolver,
):
if rdns is None:
rdns = True
self._username = username
self._password = password
self._rdns = rdns
self._resolver = resolver
async def connect(
self,
stream: AsyncSocketStream,
host: str,
port: int,
) -> socks5.ConnectReply:
conn = socks5.Connection()
# Auth methods
request = socks5.AuthMethodsRequest(
username=self._username,
password=self._password,
)
data = conn.send(request)
await stream.write_all(data)
data = await stream.read_exact(socks5.AuthMethodReply.SIZE)
reply: socks5.AuthMethodReply = conn.receive(data)
# Authenticate
if reply.method == socks5.AuthMethod.USERNAME_PASSWORD:
request = socks5.AuthRequest(
username=self._username,
password=self._password,
)
data = conn.send(request)
await stream.write_all(data)
data = await stream.read_exact(socks5.AuthReply.SIZE)
_: socks5.AuthReply = conn.receive(data)
# Connect
if not is_ip_address(host) and not self._rdns:
_, host = await self._resolver.resolve(
host,
family=socket.AF_UNSPEC,
)
request = socks5.ConnectRequest(host=host, port=port)
data = conn.send(request)
await stream.write_all(data)
data = await self._read_reply(stream)
reply: socks5.ConnectReply = conn.receive(data)
return reply
# noinspection PyMethodMayBeStatic
async def _read_reply(self, stream: AsyncSocketStream) -> bytes:
data = await stream.read_exact(3)
if data[0] != socks5.SOCKS_VER:
return data
if data[1] != socks5.ReplyCode.SUCCEEDED:
return data
if data[2] != socks5.RSV:
return data
data += await stream.read_exact(1)
addr_type = data[3]
if addr_type == socks5.AddressType.IPV4:
data += await stream.read_exact(6)
elif addr_type == socks5.AddressType.IPV6:
data += await stream.read_exact(18)
elif addr_type == socks5.AddressType.DOMAIN:
data += await stream.read_exact(1)
host_len = data[-1]
data += await stream.read_exact(host_len + 2)
return data

View File

@@ -0,0 +1,86 @@
import socket
from typing import Optional
from .._abc import SyncSocketStream, SyncResolver
from .abc import SyncConnector
from .._protocols import socks5
from .._helpers import is_ip_address
class Socks5SyncConnector(SyncConnector):
def __init__(
self,
username: Optional[str],
password: Optional[str],
rdns: Optional[bool],
resolver: SyncResolver,
):
if rdns is None:
rdns = True
self._username = username
self._password = password
self._rdns = rdns
self._resolver = resolver
def connect(
self,
stream: SyncSocketStream,
host: str,
port: int,
) -> socks5.ConnectReply:
conn = socks5.Connection()
# Auth methods
request = socks5.AuthMethodsRequest(username=self._username, password=self._password)
data = conn.send(request)
stream.write_all(data)
data = stream.read_exact(socks5.AuthMethodReply.SIZE)
reply: socks5.AuthMethodReply = conn.receive(data)
# Authenticate
if reply.method == socks5.AuthMethod.USERNAME_PASSWORD:
request = socks5.AuthRequest(username=self._username, password=self._password)
data = conn.send(request)
stream.write_all(data)
data = stream.read_exact(socks5.AuthReply.SIZE)
_: socks5.AuthReply = conn.receive(data)
# Connect
if not is_ip_address(host) and not self._rdns:
_, host = self._resolver.resolve(host, family=socket.AF_UNSPEC)
request = socks5.ConnectRequest(host=host, port=port)
data = conn.send(request)
stream.write_all(data)
data = self._read_reply(stream)
reply: socks5.ConnectReply = conn.receive(data)
return reply
# noinspection PyMethodMayBeStatic
def _read_reply(self, stream: SyncSocketStream) -> bytes:
data = stream.read_exact(3)
if data[0] != socks5.SOCKS_VER:
return data
if data[1] != socks5.ReplyCode.SUCCEEDED:
return data
if data[2] != socks5.RSV:
return data
data += stream.read_exact(1)
addr_type = data[3]
if addr_type == socks5.AddressType.IPV4:
data += stream.read_exact(6)
elif addr_type == socks5.AddressType.IPV6:
data += stream.read_exact(18)
elif addr_type == socks5.AddressType.DOMAIN:
data += stream.read_exact(1)
host_len = data[-1]
data += stream.read_exact(host_len + 2)
return data