diff options
| author | EuAndreh <eu@euandre.org> | 2026-04-30 13:20:30 -0300 |
|---|---|---|
| committer | EuAndreh <eu@euandre.org> | 2026-04-30 13:20:30 -0300 |
| commit | ef8c102fe9f7df498626ded1f641ee455d52cd65 (patch) | |
| tree | 3ebe3485409f1fa3036bbee35a77383a9e39600b /tests | |
| parent | Implement ADMIN, STATS, LINKS, TRACE, USERS, REHASH, CONNECT (diff) | |
| download | papod-ef8c102fe9f7df498626ded1f641ee455d52cd65.tar.gz papod-ef8c102fe9f7df498626ded1f641ee455d52cd65.tar.xz | |
Identify networks via PROXY v2 AUTHORITY; drop default network
papod no longer pre-creates a "default network" at boot. Instead,
each accepted connection is expected to begin with a PROXY protocol
v2 header (RFC: haproxy.org proxy-protocol.txt) carrying the SNI in
a PP2_TYPE_AUTHORITY (0x02) TLV — exactly what untls now injects.
The authority is used to look up (or create) a network by name, and
the resulting network-id is bound to the client at connect time
rather than at registration time. Connections that arrive without
a header and without the PAPOD_NETWORK_NAME fallback set are refused
with "ERROR :Closing link: (No network)".
PAPOD_NETWORK_NAME exists for environments where untls is not in
the path (notably the integration test harness, where binder/wscat
speak raw bytes); production deployments should leave it unset and
let untls supply the SNI.
Unit tests cover the parser: a valid header returns the AUTHORITY
and leaves trailing bytes intact; non-PROXY input returns nil and
preserves the stream; PROXY without an AUTHORITY TLV returns "".
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/unit.clj | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/tests/unit.clj b/tests/unit.clj index 21833e5..9eda963 100644 --- a/tests/unit.clj +++ b/tests/unit.clj @@ -1943,6 +1943,78 @@ (is (empty? (filter #(re-find #" 475 " %) replies))))))) +(def read-proxy-v2-authority! @#'papod/read-proxy-v2-authority!) + +(defn- bis-of + ^java.io.BufferedInputStream [^bytes b] + (java.io.BufferedInputStream. + (java.io.ByteArrayInputStream. b) + 4096)) + +(defn- proxy-v2-bytes + "Construct a PROXY v2 LOCAL/AF_UNSPEC header carrying a single + AUTHORITY TLV. Mirrors what untls writes to the upstream socket." + ^bytes [^String authority] + (let [auth (.getBytes authority "UTF-8") + sig (byte-array + [(unchecked-byte 0x0D) (unchecked-byte 0x0A) + (unchecked-byte 0x0D) (unchecked-byte 0x0A) + (unchecked-byte 0x00) (unchecked-byte 0x0D) + (unchecked-byte 0x0A) (unchecked-byte 0x51) + (unchecked-byte 0x55) (unchecked-byte 0x49) + (unchecked-byte 0x54) (unchecked-byte 0x0A)]) + tlv-len (+ 3 (alength auth)) + out (java.io.ByteArrayOutputStream.)] + (.write out sig) + (.write out 0x21) + (.write out 0x00) + (.write out (bit-and (bit-shift-right tlv-len 8) 0xFF)) + (.write out (bit-and tlv-len 0xFF)) + (.write out 0x02) + (.write out (bit-and (bit-shift-right (alength auth) 8) 0xFF)) + (.write out (bit-and (alength auth) 0xFF)) + (.write out auth) + (.toByteArray out))) + +(deftest test_proxy-v2-parser + (testing "valid PROXY v2 with AUTHORITY returns the SNI" + (let [hdr (proxy-v2-bytes "papo.example.com") + tail (.getBytes "NICK alice\r\n" "UTF-8") + buf (byte-array (+ (alength hdr) (alength tail)))] + (System/arraycopy hdr 0 buf 0 (alength hdr)) + (System/arraycopy tail 0 buf (alength hdr) (alength tail)) + (let [bis (bis-of buf) + authority (read-proxy-v2-authority! bis)] + (is (= authority "papo.example.com")) + ;; The trailing IRC bytes should remain readable. + (let [out (byte-array (alength tail)) + _ (.read bis out)] + (is (= (String. out "UTF-8") "NICK alice\r\n")))))) + + (testing "no PROXY signature returns nil and preserves bytes" + (let [bis (bis-of (.getBytes "NICK alice\r\n" "UTF-8"))] + (is (nil? (read-proxy-v2-authority! bis))) + (let [out (byte-array 12) + _ (.read bis out)] + (is (= (String. out "UTF-8") "NICK alice\r\n"))))) + + (testing "PROXY v2 without AUTHORITY returns empty string" + (let [sig (byte-array + [(unchecked-byte 0x0D) (unchecked-byte 0x0A) + (unchecked-byte 0x0D) (unchecked-byte 0x0A) + (unchecked-byte 0x00) (unchecked-byte 0x0D) + (unchecked-byte 0x0A) (unchecked-byte 0x51) + (unchecked-byte 0x55) (unchecked-byte 0x49) + (unchecked-byte 0x54) (unchecked-byte 0x0A)]) + out (java.io.ByteArrayOutputStream.)] + (.write out sig) + (.write out 0x21) + (.write out 0x00) + (.write out 0x00) + (.write out 0x00) + (let [bis (bis-of (.toByteArray out))] + (is (= (read-proxy-v2-authority! bis) "")))))) + (defn -main [& _args] (binding [*out* *err*] |
