sni/sni_spoofing/bypass/fragment.py
2026-05-28 02:18:25 +04:30

90 lines
2.8 KiB
Python

"""Fragment-based DPI bypass strategy.
Splits the real TLS ClientHello into fragments so that DPI systems
that only inspect the first packet or don't reassemble TCP streams
cannot read the SNI.
"""
import asyncio
import socket
import time
from typing import Optional
from .base import BypassStrategy
from ..tls.fragment import fragment_client_hello
class FragmentBypass(BypassStrategy):
"""Bypass DPI by fragmenting the TLS ClientHello.
This is the most compatible cross-platform bypass method.
It works by splitting the ClientHello into multiple TCP segments,
with the split point strategically placed in the middle of the
SNI extension value.
DPI systems that don't reassemble TCP streams will see an incomplete
SNI in the first packet and won't be able to filter it.
"""
name = "fragment"
def __init__(
self,
strategy: str = "sni_split",
fragment_delay: float = 0.1,
tcp_nodelay: bool = True,
):
"""Initialize fragment bypass.
Args:
strategy: Fragmentation strategy (sni_split, half, multi, tls_record_frag)
fragment_delay: Delay between fragments in seconds
tcp_nodelay: Enable TCP_NODELAY to send fragments immediately
"""
self.strategy = strategy
self.fragment_delay = fragment_delay
self.tcp_nodelay = tcp_nodelay
async def apply(
self,
client_sock: socket.socket,
server_sock: socket.socket,
fake_sni: str,
first_data: bytes,
loop=None,
) -> bool:
"""Apply fragmentation to the first TLS record.
The first_data from the client (usually a TLS ClientHello) is
fragmented and sent to the server in multiple TCP segments.
"""
if loop is None:
loop = asyncio.get_running_loop()
try:
# Enable TCP_NODELAY so each send() becomes its own segment
if self.tcp_nodelay:
server_sock.setsockopt(
socket.IPPROTO_TCP, socket.TCP_NODELAY, 1
)
# Fragment the ClientHello
fragments = fragment_client_hello(first_data, self.strategy)
# Send each fragment as a separate TCP segment
for i, fragment in enumerate(fragments):
await loop.sock_sendall(server_sock, fragment)
if i < len(fragments) - 1 and self.fragment_delay > 0:
await asyncio.sleep(self.fragment_delay)
# Disable TCP_NODELAY after fragments are sent (optional)
if self.tcp_nodelay:
server_sock.setsockopt(
socket.IPPROTO_TCP, socket.TCP_NODELAY, 0
)
return True
except Exception:
return False