Zum Hauptinhalt springen

3. Tunneln von UDP über HTTP

  1. Tunneln von UDP über HTTP

Um die Aushandlung eines Tunnels für UDP über HTTP zu ermöglichen, definiert dieses Dokument das "connect-udp" HTTP-Upgrade-Token. Die resultierenden UDP-Tunnel verwenden das Capsule-Protokoll (siehe Abschnitt 3.2 von [HTTP-DGRAM]) mit HTTP-Datagrammen im in Abschnitt 5 definierten Format.

Um einen UDP-Tunnel zu initiieren, der einem einzelnen HTTP-Stream zugeordnet ist, gibt ein Client eine Anfrage aus, die das "connect-udp"-Upgrade-Token enthält. Das Ziel des Tunnels wird dem UDP-Proxy vom Client über die Variablen "target_host" und "target_port" des URI-Templates angezeigt; siehe Abschnitt 2.

"target_host" unterstützt die Verwendung von DNS-Namen, IPv6-Literalen und IPv4-Literalen. Beachten Sie, dass IPv6-Scope-Adresszonen-Identifikatoren nicht unterstützt werden. Unter Verwendung der Begriffe IPv6address, IPv4address, reg-name und port aus [URI] MÜSSEN die Variablen "target_host" und "target_port" dem Format in Abbildung 2 entsprechen, unter Verwendung der Notation aus [ABNF]. Zusätzlich:

  • sowohl die "target_host"- als auch die "target_port"-Variablen DÜRFEN NICHT leer sein.

  • wenn "target_host" ein IPv6-Literal enthält, MÜSSEN die Doppelpunkte (":") prozentkodiert werden. Wenn der Zielhost beispielsweise "2001:db8::42" ist, wird er im URI als "2001%3Adb8%3A%3A42" kodiert.

  • "target_port" MUSS eine Ganzzahl zwischen 1 und 65535 einschließlich darstellen.

target_host = IPv6address / IPv4address / reg-name target_port = port

               Abbildung 2: URI-Template-Variablenformat

Beim Senden seiner UDP-Proxying-Anfrage FÜHRT der Client eine URI-Template-Erweiterung durch, um den Pfad und die Query seiner Anfrage zu bestimmen.

Wenn die Anfrage erfolgreich ist, verpflichtet sich der UDP-Proxy, empfangene HTTP-Datagramme in UDP-Pakete umzuwandeln und umgekehrt, bis der Tunnel geschlossen wird.

Aufgrund der Definition des Capsule-Protokolls (siehe Abschnitt 3.2 von [HTTP-DGRAM]) tragen UDP-Proxying-Anfragen keinen Nachrichteninhalt. Ebenso tragen erfolgreiche UDP-Proxying-Antworten keinen Nachrichteninhalt.

3.1. UDP-Proxy-Handhabung

Bei Empfang einer UDP-Proxying-Anfrage:

  • wenn der Empfänger so konfiguriert ist, dass er einen anderen HTTP-Proxy verwendet, fungiert er als Zwischenhändler, indem er die Anfrage an einen anderen HTTP-Server weiterleitet. Beachten Sie, dass solche Zwischenhändler die Anfrage möglicherweise neu kodieren müssen, wenn sie sie unter Verwendung einer anderen HTTP-Version weiterleiten als der, mit der sie sie empfangen haben, da sich die Anfragencodierung je nach Version unterscheidet (siehe unten).

  • andernfalls fungiert der Empfänger als UDP-Proxy. Er extrahiert die Variablen "target_host" und "target_port" aus dem URI, den er aus den Anfrage-Headern rekonstruiert hat, dekodiert ihre Prozentkodierung und baut einen Tunnel auf, indem er direkt einen UDP-Socket zum angeforderten Ziel öffnet.

Im Gegensatz zu TCP ist UDP verbindungslos. Der UDP-Proxy, der den UDP-Socket öffnet, hat keine Möglichkeit zu wissen, ob das Ziel erreichbar ist. Daher muss er auf die Anfrage antworten, ohne auf ein Paket vom Ziel zu warten. Wenn jedoch "target_host" ein DNS-Name ist, MUSS der UDP-Proxy eine DNS-Auflösung durchführen, bevor er auf die HTTP-Anfrage antwortet. Wenn während dieses Prozesses Fehler auftreten, MUSS der UDP-Proxy die Anfrage ablehnen und SOLLTE Details unter Verwendung eines geeigneten Proxy-Status-Header-Felds [PROXY-STATUS] senden. Wenn beispielsweise die DNS-Auflösung einen Fehler zurückgibt, kann der Proxy den dns_error Proxy-Fehlertyp aus Abschnitt 2.3.2 von [PROXY-STATUS] verwenden.

UDP-Proxys können verbundene UDP-Sockets verwenden, wenn ihr Betriebssystem diese unterstützt, da dies dem UDP-Proxy ermöglicht, sich darauf zu verlassen, dass der Kernel ihm nur UDP-Pakete sendet, die dem korrekten 5-Tupel entsprechen. Wenn der UDP-Proxy einen nicht verbundenen Socket verwendet, MUSS er die IP-Quelladresse und den UDP-Quellport bei empfangenen Paketen validieren, um sicherzustellen, dass sie der Anfrage des Clients entsprechen. Pakete, die nicht übereinstimmen, MÜSSEN vom UDP-Proxy verworfen werden.

Die Lebensdauer des Sockets ist an den Anfragestream gebunden. Der UDP-Proxy MUSS den Socket offen halten, solange der Anfragestream offen ist. Wenn ein UDP-Proxy von seinem Betriebssystem benachrichtigt wird, dass sein Socket nicht mehr verwendbar ist, MUSS er den Anfragestream schließen. Dies kann beispielsweise passieren, wenn eine ICMP Destination Unreachable-Nachricht empfangen wird; siehe Abschnitt 3.1 von [ICMP6]. UDP-Proxys KÖNNEN Sockets aufgrund einer Zeit der Inaktivität schließen, aber sie MÜSSEN den Anfragestream schließen, wenn sie den Socket schließen. UDP-Proxys, die Sockets nach einer Zeit der Inaktivität schließen, SOLLTEN KEINE Zeitspanne von weniger als zwei Minuten verwenden; siehe Abschnitt 4.3 von [BEHAVE].

Eine erfolgreiche Antwort (wie in den Abschnitten 3.3 und 3.5 definiert) zeigt an, dass der UDP-Proxy einen Socket zum angeforderten Ziel geöffnet hat und bereit ist, UDP-Nutzlasten zu proxygen. Jede Antwort außer einer erfolgreichen Antwort zeigt an, dass die Anfrage fehlgeschlagen ist; daher MUSS der Client die Anfrage abbrechen.

UDP-Proxys DÜRFEN KEINE Fragmentierung auf der IP-Schicht einführen, wenn sie HTTP-Datagramme auf einen UDP-Socket weiterleiten; übermäßig große Datagramme werden stillschweigend verworfen. In IPv4 MUSS das Don't Fragment (DF)-Bit gesetzt werden, wenn möglich, um Fragmentierung auf dem Pfad zu verhindern. Zukünftige Erweiterungen KÖNNEN diese Anforderungen entfernen.

Implementierer von UDP-Proxys profitieren vom Lesen der Anleitung in [UDP-USAGE].

3.2. HTTP/1.1-Anfrage

Bei Verwendung von HTTP/1.1 [HTTP/1.1] erfüllt eine UDP-Proxying-Anfrage die folgenden Anforderungen:

  • die Methode IST "GET".

  • die Anfrage ENTHÄLT ein einzelnes Host-Header-Feld, das den Ursprung des UDP-Proxys enthält.

  • die Anfrage ENTHÄLT ein Connection-Header-Feld mit dem Wert "Upgrade" (beachten Sie, dass diese Anforderung gemäß Abschnitt 7.6.1 von [HTTP] nicht zwischen Groß- und Kleinschreibung unterscheidet).

  • die Anfrage ENTHÄLT ein Upgrade-Header-Feld mit dem Wert "connect-udp".

Eine UDP-Proxying-Anfrage, die diesen Einschränkungen nicht entspricht, ist fehlerhaft. Der Empfänger einer solchen fehlerhaften Anfrage MUSS mit einem Fehler antworten und SOLLTE den Statuscode 400 (Bad Request) verwenden.

Wenn der Client beispielsweise mit dem URI-Template "https://example.org/.well-known/masque/udp/\{target_host\}/\{target_port\}/" konfiguriert ist und einen UDP-Proxying-Tunnel zum Ziel 192.0.2.6:443 öffnen möchte, könnte er die folgende Anfrage senden:

GET https://example.org/.well-known/masque/udp/192.0.2.6/443/ HTTP/1.1 Host: example.org Connection: Upgrade Upgrade: connect-udp Capsule-Protocol: ?1

                Abbildung 3: Beispielhafte HTTP/1.1-Anfrage

In HTTP/1.1 verwendet dieses Protokoll die GET-Methode, um das Design des WebSocket-Protokolls [WEBSOCKET] nachzuahmen.

3.3. HTTP/1.1-Antwort

Der UDP-Proxy ZEIGT eine erfolgreiche Antwort an, indem er mit den folgenden Anforderungen antwortet:

  • der HTTP-Statuscode in der Antwort IST 101 (Switching Protocols).

  • die Antwort ENTHÄLT ein Connection-Header-Feld mit dem Wert "Upgrade" (beachten Sie, dass diese Anforderung gemäß Abschnitt 7.6.1 von [HTTP] nicht zwischen Groß- und Kleinschreibung unterscheidet).

  • die Antwort ENTHÄLT ein einzelnes Upgrade-Header-Feld mit dem Wert "connect-udp".

  • die Antwort ERFÜLLT die Anforderungen von HTTP-Antworten, die das Capsule-Protokoll starten; siehe Abschnitt 3.2 von [HTTP-DGRAM].

Wenn eine dieser Anforderungen nicht erfüllt ist, MUSS der Client diesen Proxying-Versuch als fehlgeschlagen betrachten und die Verbindung abbrechen.

Beispielsweise könnte der UDP-Proxy antworten mit:

HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: connect-udp Capsule-Protocol: ?1

                Abbildung 4: Beispielhafte HTTP/1.1-Antwort

3.4. HTTP/2- und HTTP/3-Anfragen

Bei Verwendung von HTTP/2 [HTTP/2] oder HTTP/3 [HTTP/3] verwenden UDP-Proxying-Anfragen HTTP Extended CONNECT. Dies erfordert, dass Server ein HTTP-Setting senden, wie in [EXT-CONNECT2] und [EXT-CONNECT3] spezifiziert, und dass Anfragen HTTP-Pseudo-Header-Felder mit den folgenden Anforderungen verwenden:

  • Das :method Pseudo-Header-Feld IST "CONNECT".

  • Das :protocol Pseudo-Header-Feld IST "connect-udp".

  • Das :authority Pseudo-Header-Feld ENTHÄLT die Authority des UDP-Proxys.

  • Die :path und :scheme Pseudo-Header-Felder DÜRFEN NICHT leer sein. Ihre Werte ENTHALTEN das Schema und den Pfad aus dem URI-Template, nachdem der URI-Template-Erweiterungsprozess abgeschlossen wurde.

Eine UDP-Proxying-Anfrage, die diesen Einschränkungen nicht entspricht, ist fehlerhaft (siehe Abschnitt 8.1.1 von [HTTP/2] und Abschnitt 4.1.2 von [HTTP/3]).

Wenn der Client beispielsweise mit dem URI-Template "https://example.org/.well-known/masque/udp/\{target_host\}/\{target_port\}/" konfiguriert ist und einen UDP-Proxying-Tunnel zum Ziel 192.0.2.6:443 öffnen möchte, könnte er die folgende Anfrage senden:

HEADERS :method = CONNECT :protocol = connect-udp :scheme = https :path = /.well-known/masque/udp/192.0.2.6/443/ :authority = example.org capsule-protocol = ?1

                  Abbildung 5: Beispielhafte HTTP/2-Anfrage

3.5. HTTP/2- und HTTP/3-Antworten

Der UDP-Proxy ZEIGT eine erfolgreiche Antwort an, indem er mit den folgenden Anforderungen antwortet:

  • der HTTP-Statuscode in der Antwort IST im Bereich 2xx (Erfolgreich).

  • die Antwort ERFÜLLT die Anforderungen von HTTP-Antworten, die das Capsule-Protokoll starten; siehe Abschnitt 3.2 von [HTTP-DGRAM].

Wenn eine dieser Anforderungen nicht erfüllt ist, MUSS der Client diesen Proxying-Versuch als fehlgeschlagen betrachten und die Anfrage abbrechen.

Beispielsweise könnte der UDP-Proxy antworten mit:

HEADERS :status = 200 capsule-protocol = ?1

                 Abbildung 6: Beispielhafte HTTP/2-Antwort