HTTP/3 および SOCKS5 経由の UDP
Updated:
レジデンシャルプロキシは、SOCKS5経由でUDPトラフィックを転送します。これを使用して、レジデンシャルIPからHTTP/3(QUIC)経由でウェブサイトを開いたり、DNS、WebRTC、VoIP、オンラインゲームなど、その他のUDPプロトコルをルーティングしたりできます。
既存のすべてのリストで動作します — ホスト、ポート、ログインID、パスワードはすべて同じです。有効化や注文の手順は一切必要ありません。
仕組み
HTTP/3はQUIC上で動作し、QUICはUDP上で動作します。HTTPプロキシプロトコルはTCPのみを扱うため、UDPトラフィックはSOCKS5を経由します。
トラフィック | HTTPプロキシ | SOCKS5 |
|---|---|---|
TCP:HTTP/1.1、HTTP/2、その他のTCPプロトコル | ✅ | ✅ |
UDP:HTTP/3 (QUIC)、DNS、WebRTC、VoIP、ゲーム | — | ✅ |
最初のデータグラムがあなたのマシンから送信される前に、3つのやり取りが行われます:
接続と認証
アプリはゲートウェイに対してSOCKS5接続を確立し、リストに登録されたログインIDとパスワードを使用してログインします。
UDPリレーをリクエストする
それは
UDP ASSOCIATEリクエスト(RFC 1928)。ゲートウェイは、中継アドレス(ゲートウェイのIPアドレスと30000~39999の範囲のポート番号)を応答として返します。データグラムを送信する
このアプリは、そのリレーにUDPデータグラムを送信します。各データグラムは、宛先を指定する短いSOCKS5ヘッダーで始まります。トラフィックは住宅用出口IPから送信され、応答も同じ経路で戻ってきます。
リレーは、SOCKS5 TCP接続が開いている間のみ稼働します。制御接続を閉じると、リレーも同時に閉じられます。
接続設定
設定 | 価値 |
|---|---|
手順 | ユーザー名/パスワード認証付きのSOCKS5 |
ホスト |
|
移植 | リストにある任意のポート(10000~10999) |
ログインとパスワード | リストに記載されているログインIDとパスワード |
UDPリレー | ゲートウェイのIPアドレス、ポート30000~39999 |
HTTPとSOCKS5は、ホスト、ポート、ログイン名、パスワードを共有しています。ゲートウェイはプロトコルを自動的に判別します。接続文字列をSOCKS5に変更するには、スキームのみを変更し、それ以外は変更しないでください:
http://LOGIN:[email protected]:10000
socks5://LOGIN:[email protected]:10000ゲートウェイは単なるエントリポイントに過ぎません。どのゲートウェイを使用する場合でも、出口IPとその場所はリスト設定から取得されます。そのため、サーバーからの遅延が最も少ないゲートウェイを選択してください。
ネットワークで送信トラフィックが制限されている場合は、ポート30000~39999でのゲートウェイへのUDP通信を許可してください。以下のUDPチェックでタイムアウトが発生する主な原因は、この範囲がブロックされていることです。
ご自身で確認してください
接続を確認してください
SOCKS5クライアントであればどれでも構いません —
curlこれで十分です:curl -x socks5h://LOGIN:[email protected]:10000 https://api.ipify.org応答はプロキシの出口IPです。
socks5h://、ドメイン名の解決は、お使いのマシンではなくプロキシによって行われます。UDPを確認してください
このスクリプトはPythonの標準ライブラリのみを使用しています。SOCKS5経由でログインし、UDPリレーを開いて、それを通じて1つのDNSクエリを送信します。OpenDNSリゾルバーが応答します。
myip.opendns.comクエリの発信元アドレスを特定するため、このスクリプトはUDPトラフィックの送信元IPを出力します。以下の名前で保存してください
socks5_udp.py、ログインIDとパスワードを設定し、実行してくださいpython socks5_udp.py."""UDP through a residential proxy: SOCKS5 login, UDP ASSOCIATE and one DNS query. The OpenDNS resolver answers "myip.opendns.com" with the IP the query came from, so the script prints the exit IP your UDP traffic leaves from. """ import os import socket import struct PROXY_HOST = "res.proxy-seller.com" PROXY_PORT = 10000 LOGIN = "LOGIN" PASSWORD = "PASSWORD" def recv_exact(sock, n): data = b"" while len(data) < n: chunk = sock.recv(n - len(data)) if not chunk: raise ConnectionError("proxy closed the connection") data += chunk return data def udp_associate(host, port, login, password): """SOCKS5 login (RFC 1929) and UDP ASSOCIATE (RFC 1928). Returns the TCP control connection and the UDP relay address. The relay works while the TCP connection stays open. """ tcp = socket.create_connection((host, port), timeout=15) tcp.sendall(b"\x05\x01\x02") # SOCKS5, one auth method: username/password if recv_exact(tcp, 2)[1] != 0x02: raise RuntimeError("username/password authentication is not available") tcp.sendall(b"\x01" + bytes([len(login)]) + login.encode() + bytes([len(password)]) + password.encode()) if recv_exact(tcp, 2)[1] != 0x00: raise RuntimeError("wrong login or password") tcp.sendall(b"\x05\x03\x00\x01" + bytes(6)) # UDP ASSOCIATE, client address 0.0.0.0:0 _, reply, _, atyp = recv_exact(tcp, 4) if reply != 0x00: raise RuntimeError(f"UDP ASSOCIATE failed, reply code {reply}") if atyp == 0x01: relay_ip = socket.inet_ntoa(recv_exact(tcp, 4)) elif atyp == 0x03: relay_ip = recv_exact(tcp, recv_exact(tcp, 1)[0]).decode() else: raise RuntimeError("IPv6 relay address is not handled in this example") relay_port = struct.unpack("!H", recv_exact(tcp, 2))[0] if relay_ip == "0.0.0.0": relay_ip = tcp.getpeername()[0] return tcp, (relay_ip, relay_port) def strip_udp_header(datagram): """Remove the SOCKS5 UDP header from a datagram received from the relay.""" atyp = datagram[3] if atyp == 0x01: # IPv4 return datagram[10:] if atyp == 0x04: # IPv6 return datagram[22:] return datagram[7 + datagram[4]:] # domain name if __name__ == "__main__": tcp, relay = udp_associate(PROXY_HOST, PROXY_PORT, LOGIN, PASSWORD) print("UDP relay:", f"{relay[0]}:{relay[1]}") # DNS query "myip.opendns.com, type A" without EDNS: the answer ends with the IPv4 address query = (os.urandom(2) + b"\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00" + b"\x04myip\x07opendns\x03com\x00" + b"\x00\x01\x00\x01") # every datagram to the relay starts with a SOCKS5 UDP header: RSV, FRAG, ATYP=IPv4, destination header = b"\x00\x00\x00\x01" + socket.inet_aton("208.67.222.222") + struct.pack("!H", 53) udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) udp.settimeout(10) udp.sendto(header + query, relay) answer = strip_udp_header(udp.recv(4096)) print("Exit IP over UDP:", socket.inet_ntoa(answer[-4:])) udp.close() tcp.close()出力例:
UDP relay: 103.152.136.28:39631 Exit IP over UDP: 203.0.113.45HTTP/3リクエストを送信する
HTTP/3の場合、リクエストのQUICパケットは同じUDPリレーを経由します。この例では、aioquic ライブラリ(Python 3.11 以降)。これはhttps://cloudflare-quic.com/cdn-cgi/trace、プロトコルとクライアントのIPアドレスを表示するプレーンテキストのページ。
依存関係をインストールし、スクリプトを
http3_via_socks5.pyの横にsocks5_udp.py、そして実行してください:pip install aioquic certifi python http3_via_socks5.py"""HTTP/3 (QUIC) request to a website through a residential proxy. QUIC packets go to the SOCKS5 UDP relay; the website sees an HTTP/3 connection from the exit IP. Requires: pip install aioquic certifi, and socks5_udp.py from the previous example in the same folder. """ import socket import struct import time import certifi from aioquic.h3.connection import H3_ALPN, H3Connection from aioquic.h3.events import DataReceived, HeadersReceived from aioquic.quic.configuration import QuicConfiguration from aioquic.quic.connection import QuicConnection from aioquic.quic.events import ConnectionTerminated, HandshakeCompleted from socks5_udp import LOGIN, PASSWORD, PROXY_HOST, PROXY_PORT, strip_udp_header, udp_associate TARGET_HOST = "cloudflare-quic.com" TARGET_PATH = "/cdn-cgi/trace" # plain text: shows the protocol (http=http/3) and the client IP tcp, relay = udp_associate(PROXY_HOST, PROXY_PORT, LOGIN, PASSWORD) udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # the destination goes into the SOCKS5 UDP header as a domain name (ATYP=3): the proxy resolves it header = b"\x00\x00\x00\x03" + bytes([len(TARGET_HOST)]) + TARGET_HOST.encode() + struct.pack("!H", 443) config = QuicConfiguration(is_client=True, alpn_protocols=H3_ALPN, server_name=TARGET_HOST) config.load_verify_locations(certifi.where()) quic = QuicConnection(configuration=config) peer = ("0.0.0.0", 443) # placeholder: only the proxy knows the real address quic.connect(peer, now=time.time()) h3 = H3Connection(quic) def send_pending(): for datagram, _ in quic.datagrams_to_send(now=time.time()): udp.sendto(header + datagram, relay) send_pending() stream_id, body, done = None, b"", False deadline = time.time() + 20 while not done and time.time() < deadline: timer = quic.get_timer() udp.settimeout(0.5 if timer is None else min(0.5, max(0.01, timer - time.time()))) try: quic.receive_datagram(strip_udp_header(udp.recv(65535)), peer, now=time.time()) except socket.timeout: if timer is not None and time.time() >= timer: quic.handle_timer(now=time.time()) event = quic.next_event() while event is not None: if isinstance(event, HandshakeCompleted): stream_id = quic.get_next_available_stream_id() h3.send_headers(stream_id, [ (b":method", b"GET"), (b":scheme", b"https"), (b":authority", TARGET_HOST.encode()), (b":path", TARGET_PATH.encode()), ], end_stream=True) elif isinstance(event, ConnectionTerminated): done = True for h3_event in h3.handle_event(event): if isinstance(h3_event, (HeadersReceived, DataReceived)) and h3_event.stream_id == stream_id: if isinstance(h3_event, DataReceived): body += h3_event.data done = done or h3_event.stream_ended event = quic.next_event() send_pending() print(body.decode() or "No HTTP/3 response within 20 seconds") quic.close() send_pending() udp.close() tcp.close()出力(短縮版):
ip=203.0.113.45 http=http/3 loc=GB tls=TLSv1.3http=http/3は、そのウェブサイトがHTTP/3経由でリクエストを受信したことを意味します。ipおよびlocは、プロキシの出口IPとその国です。
ブラウザやその他のアプリにおけるHTTP/3
アプリがプロキシ経由でUDPを送信できるのは、SOCKS5経由のUDPに対応している場合に限られます(UDP ASSOCIATE)。
ChromeおよびFirefoxは、自身の設定で設定されたSOCKS5プロキシ経由でUDPを送信しません。その結果、ウェブサイトはHTTP/3ではなく、HTTP/2またはHTTP/1.1で開かれます。
ブラウザやその他のアプリでHTTP/3を使用するには、SOCKS5経由でUDPを転送するツールを介してトラフィックをルーティングします。アプリはプロキシを認識しないため、通常通りHTTP/3を使用し、ツールがゲートウェイを介してQUICパケットを送信します。
Windows、macOS、Linux。以下を追加してください。socks リストの詳細とtun アプリへのトラフィックをキャプチャするためのインバウンド接続。UDPはsocks デフォルトではアウトバウンド:
{
"type": "socks",
"tag": "residential",
"server": "res.proxy-seller.com",
"server_port": 10000,
"version": "5",
"username": "LOGIN",
"password": "PASSWORD"
}Windows。選択したアプリのトラフィック(UDPおよびQUICを含む)をSOCKS5経由でルーティングします。プロキシ経由で通信させるアプリを選択し、プロファイルでホスト、ポート、ログイン名、パスワードを指定してください。
制限事項
UDPはSOCKS5でのみ動作します。HTTPプロキシ接続ではTCPが使用されます。
HTTPS(TLS)またはHTTP/3を介してプロキシ自体に接続することはサポートされていません。代わりに、
http://またはsocks5://.UDPセッションでは、同じポート上のTCP接続とは異なる送信元IPアドレスが割り当てられる場合があります。
よくある質問(FAQ)
いいえ。UDPおよびHTTP/3は、すべてのリストで動作し、すでに持っているログインIDとパスワードで利用できます。
いいえ。TCP 上の HTTP および SOCKS5 は、これまでと全く同じように動作し続けます。
サーバーからレイテンシが最も低いもの。ゲートウェイは出口IPやその場所を変更しません。これらはリスト設定から取得されます。
ファイアウォールやホスティングプロバイダが、ポート30000~39999におけるゲートウェイへのUDP送信をブロックしていないことを確認してください。また、UDPを送信している間はSOCKS5のTCP接続を開いたままにしておいてください。この接続が閉じられると、中継も同時に切断されます。
新しいリストが有効になるまで1~2分かかります。それまでは、プロキシからエラー407が返される場合があります。しばらく待ってから、もう一度お試しください。