summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2026-04-30 13:20:30 -0300
committerEuAndreh <eu@euandre.org>2026-04-30 13:20:30 -0300
commitef8c102fe9f7df498626ded1f641ee455d52cd65 (patch)
tree3ebe3485409f1fa3036bbee35a77383a9e39600b /tests
parentImplement ADMIN, STATS, LINKS, TRACE, USERS, REHASH, CONNECT (diff)
downloadpapod-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.clj72
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*]