Add full WebTransport support

This commit is contained in:
Travis Burtrum 2023-12-19 22:28:48 -05:00
parent 3bdb461142
commit 42aed6cbb2
Signed by: moparisthebest
GPG Key ID: 88C93BFE27BC8229
22 changed files with 1025 additions and 47 deletions

38
Cargo.lock generated
View File

@ -1920,6 +1920,43 @@ version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
[[package]]
name = "webtransport-generic"
version = "0.5.0"
source = "git+https://github.com/kixelated/webtransport-rs?rev=ba1a372a7a89e4ba9f9bc027733f82f87aa9a4fd#ba1a372a7a89e4ba9f9bc027733f82f87aa9a4fd"
dependencies = [
"tokio",
]
[[package]]
name = "webtransport-proto"
version = "0.6.0"
source = "git+https://github.com/kixelated/webtransport-rs?rev=ba1a372a7a89e4ba9f9bc027733f82f87aa9a4fd#ba1a372a7a89e4ba9f9bc027733f82f87aa9a4fd"
dependencies = [
"bytes",
"http",
"thiserror",
"url",
]
[[package]]
name = "webtransport-quinn"
version = "0.6.1"
source = "git+https://github.com/kixelated/webtransport-rs?rev=ba1a372a7a89e4ba9f9bc027733f82f87aa9a4fd#ba1a372a7a89e4ba9f9bc027733f82f87aa9a4fd"
dependencies = [
"bytes",
"futures",
"http",
"log",
"quinn",
"quinn-proto",
"thiserror",
"tokio",
"url",
"webtransport-generic",
"webtransport-proto",
]
[[package]]
name = "widestring"
version = "1.0.2"
@ -2088,4 +2125,5 @@ dependencies = [
"toml",
"trust-dns-resolver",
"webpki-roots",
"webtransport-quinn",
]

View File

@ -68,11 +68,14 @@ rustls-pemfile = { version = "1.0", optional = true }
tokio-tungstenite = { version = "0.19", optional = true, default-features = false, features = ["handshake"] }
futures-util = { version = "0.3", default-features = false, features = ["async-await", "sink", "std"], optional = true }
# webtransport deps
webtransport-quinn = { version = "0.6", optional = true }
# systemd dep
nix = { version = "0.26", optional = true, default-features = false, features = ["socket"]}
[features]
default = ["c2s-incoming", "c2s-outgoing", "s2s-incoming", "s2s-outgoing", "tls", "quic", "websocket", "logging", "tls-ca-roots-native", "systemd"]
default = ["c2s-incoming", "c2s-outgoing", "s2s-incoming", "s2s-outgoing", "tls", "quic", "websocket", "webtransport", "logging", "tls-ca-roots-native", "systemd"]
# you must pick one of these or the other, not both: todo: enable picking both and choosing at runtime
# don't need either of these if only doing c2s-incoming
@ -97,6 +100,7 @@ s2s-outgoing = ["outgoing", "s2s"]
tls = ["tokio-rustls", "webpki", "rustls"]
quic = ["quinn", "rustls"]
websocket = ["tokio-tungstenite", "futures-util", "tls"] # websocket+incoming also enables incoming TLS support as it's free
webtransport = ["webtransport-quinn", "quic"] # webtransport requires quic
logging = ["rand", "env_logger"]
systemd = ["nix"]
@ -106,3 +110,8 @@ net-test = []
[dev-dependencies]
serde_json = "1.0"
# need this until a release is made with this commit in it
[patch.crates-io]
webtransport-quinn = { git = "https://github.com/kixelated/webtransport-rs", rev = "ba1a372a7a89e4ba9f9bc027733f82f87aa9a4fd" }

View File

@ -11,11 +11,11 @@
[![Build Status](https://ci.moparisthe.best/job/moparisthebest/job/xmpp-proxy/job/master/badge/icon%3Fstyle=plastic)](https://ci.moparisthe.best/job/moparisthebest/job/xmpp-proxy/job/master/)
xmpp-proxy is a reverse proxy and outgoing proxy for XMPP servers and clients, providing [STARTTLS], [Direct TLS], [QUIC],
[WebSocket C2S], and [WebSocket S2S] connectivity to plain-text XMPP servers and clients and limiting stanza sizes without an XML parser.
[WebSocket C2S], [WebSocket S2S], and [WebTransport] connectivity to plain-text XMPP servers and clients and limiting stanza sizes without an XML parser.
xmpp-proxy in reverse proxy (incoming) mode will:
1. listen on any number of interfaces/ports
2. accept any STARTTLS, Direct TLS, QUIC, or WebSocket c2s or s2s connections from the internet
2. accept any STARTTLS, Direct TLS, QUIC, WebSocket, or WebTransport c2s or s2s connections from the internet
3. terminate TLS
4. for s2s require a client cert and validate it correctly (using CAs, host-meta, host-meta2, and POSH) for SASL EXTERNAL auth
5. connect them to a local real XMPP server over plain-text TCP
@ -25,8 +25,8 @@ xmpp-proxy in reverse proxy (incoming) mode will:
xmpp-proxy in outgoing mode will:
1. listen on any number of interfaces/ports
2. accept any plain-text TCP or WebSocket connection from a local XMPP server or client
3. look up the required SRV, [host-meta], host-meta2, and [POSH] records
4. connect to a real XMPP server across the internet over STARTTLS, Direct TLS, QUIC, or WebSocket
3. look up the required SRV, [host-meta], [host-meta2], and [POSH] records
4. connect to a real XMPP server across the internet over STARTTLS, Direct TLS, QUIC, WebSocket, or WebTransport
5. fallback to next SRV target or defaults as required to fully connect
6. perform all the proper required certificate validation logic
7. limit incoming stanza sizes as configured
@ -137,10 +137,11 @@ choose between 1-4 directions:
3. `s2s-incoming` - enables a server to accept incoming s2s connections
4. `s2s-outgoing` - enables a server to make outgoing s2s connections
choose between 1-3 transport protocols:
choose between 1-4 transport protocols:
1. `tls` - enables STARTTLS/TLS support
2. `quic` - enables QUIC support
3. `websocket` - enables WebSocket support, also enables TLS incoming support if the appropriate directions are enabled
4. `webtransport` - enables WebTransport support, also enables QUIC
choose exactly 1 of these methods to get trusted CA roots, not needed if only `c2s-incoming` is enabled:
1. `tls-ca-roots-native` - reads CA roots from operating system
@ -168,15 +169,18 @@ GNU/AGPLv3 - Check LICENSE.md for details
Thanks [rxml](https://github.com/horazont/rxml) for afl-fuzz seeds
#### Todo
1. write "host-meta2" XEP for QUIC and WebSocket S2S Discovery
2. optional [systemd](https://www.freedesktop.org/software/systemd/man/sd_listen_fds.html) [integration](https://mgdm.net/weblog/systemd/)
3. seamless Tor integration, connecting to and from .onion domains
1. seamless Tor integration, connecting to and from .onion domains
2. Write WebTransport XEP
3. Document systemd activation support
4. Document use-as-a-library support
[STARTTLS]: https://datatracker.ietf.org/doc/html/rfc6120#section-5
[Direct TLS]: https://xmpp.org/extensions/xep-0368.html
[QUIC]: https://xmpp.org/extensions/xep-0467.html
[WebSocket C2S]: https://datatracker.ietf.org/doc/html/rfc7395
[WebSocket S2S]: https://xmpp.org/extensions/xep-0468.html
[WebTransport]: https://www.w3.org/TR/webtransport/
[POSH]: https://datatracker.ietf.org/doc/html/rfc7711
[host-meta]: https://xmpp.org/extensions/xep-0156.html
[host-meta2]: https://xmpp.org/extensions/inbox/host-meta-2.html
[PROXY protocol]: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt

View File

@ -47,7 +47,7 @@ perms_optional() {
all_features() {
for optional in "" $(perms_optional logging systemd)
do
for proto in $(perms tls quic websocket)
for proto in $(perms tls quic websocket webtransport)
do
for direction in $(perms c2s-incoming c2s-outgoing s2s-incoming s2s-outgoing)
do
@ -61,7 +61,7 @@ all_features() {
for optional in "" $(perms_optional logging systemd)
do
for proto in $(perms tls quic websocket)
for proto in $(perms tls quic websocket webtransport)
do
echo c2s-incoming,$proto$optional
done

View File

@ -0,0 +1,23 @@
$TTL 300
; example.org
@ IN SOA ns1.example.org. postmaster.example.org. (
2018111111 ; Serial
28800 ; Refresh
1800 ; Retry
604800 ; Expire - 1 week
86400 ) ; Negative Cache TTL
IN NS ns1
ns1 IN A 192.5.0.10
server1 IN A 192.5.0.20
server2 IN A 192.5.0.30
xp1 IN A 192.5.0.40
xp2 IN A 192.5.0.50
xp3 IN A 192.5.0.60
web1 IN A 192.5.0.70
web2 IN A 192.5.0.80
one IN CNAME web1
two IN CNAME web2
scansion.one IN CNAME xp3
scansion.two IN CNAME xp3

View File

@ -0,0 +1,48 @@
daemon off;
worker_processes 1;
error_log stderr;
events {
worker_connections 32;
}
http {
access_log /dev/stdout;
server {
listen 443 ssl;
server_name one.example.org;
ssl_certificate /etc/prosody/certs/one.example.org.crt;
ssl_certificate_key /etc/prosody/certs/one.example.org.key;
location = /.well-known/host-meta.json {
default_type application/json;
return 200 '{
"links": [
{
"rel": "urn:xmpp:alt-connections:s2s-webtransport",
"href": "https://xp1.example.org/xmpp-webtransport",
"ips": [
"192.5.0.40"
],
"priority": 15,
"weight": 50,
"sni": "xp1.example.org"
},
{
"rel": "urn:xmpp:alt-connections:webtransport",
"href": "https://xp1.example.org/xmpp-webtransport",
"ips": [
"192.5.0.40"
],
"priority": 15,
"weight": 50,
"sni": "xp1.example.org"
}
]
}';
}
}
}

View File

@ -0,0 +1,48 @@
daemon off;
worker_processes 1;
error_log stderr;
events {
worker_connections 32;
}
http {
access_log /dev/stdout;
server {
listen 443 ssl;
server_name two.example.org;
ssl_certificate /etc/prosody/certs/two.example.org.crt;
ssl_certificate_key /etc/prosody/certs/two.example.org.key;
location = /.well-known/host-meta.json {
default_type application/json;
return 200 '{
"links": [
{
"rel": "urn:xmpp:alt-connections:s2s-webtransport",
"href": "https://xp2.example.org/xmpp-webtransport",
"ips": [
"192.5.0.50"
],
"priority": 15,
"weight": 50,
"sni": "xp2.example.org"
},
{
"rel": "urn:xmpp:alt-connections:webtransport",
"href": "https://xp2.example.org/xmpp-webtransport",
"ips": [
"192.5.0.50"
],
"priority": 15,
"weight": 50,
"sni": "xp2.example.org"
}
]
}';
}
}
}

View File

@ -0,0 +1,251 @@
--Important for systemd
-- daemonize is important for systemd. if you set this to false the systemd startup will freeze.
daemonize = false
run_as_root = true
pidfile = "/run/prosody/prosody.pid"
plugin_paths = { "/opt/xmpp-proxy/prosody-modules", "/opt/prosody-modules" }
-- Prosody Example Configuration File
--
-- Information on configuring Prosody can be found on our
-- website at https://prosody.im/doc/configure
--
-- Tip: You can check that the syntax of this file is correct
-- when you have finished by running this command:
-- prosodyctl check config
-- If there are any errors, it will let you know what and where
-- they are, otherwise it will keep quiet.
--
-- The only thing left to do is rename this file to remove the .dist ending, and fill in the
-- blanks. Good luck, and happy Jabbering!
---------- Server-wide settings ----------
-- Settings in this section apply to the whole server and are the default settings
-- for any virtual hosts
-- This is a (by default, empty) list of accounts that are admins
-- for the server. Note that you must create the accounts separately
-- (see https://prosody.im/doc/creating_accounts for info)
-- Example: admins = { "user1@example.com", "user2@example.net" }
admins = { }
-- Enable use of libevent for better performance under high load
-- For more information see: https://prosody.im/doc/libevent
--use_libevent = true
-- Prosody will always look in its source directory for modules, but
-- this option allows you to specify additional locations where Prosody
-- will look for modules first. For community modules, see https://modules.prosody.im/
--plugin_paths = {}
-- This is the list of modules Prosody will load on startup.
-- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too.
-- Documentation for bundled modules can be found at: https://prosody.im/doc/modules
modules_enabled = {
-- Generally required
"roster"; -- Allow users to have a roster. Recommended ;)
"saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
--"tls"; -- Add support for secure TLS on c2s/s2s connections
--"dialback"; -- s2s dialback support
"disco"; -- Service discovery
-- Not essential, but recommended
"carbons"; -- Keep multiple clients in sync
"pep"; -- Enables users to publish their avatar, mood, activity, playing music and more
"private"; -- Private XML storage (for room bookmarks, etc.)
"blocklist"; -- Allow users to block communications with other users
"vcard4"; -- User profiles (stored in PEP)
"vcard_legacy"; -- Conversion between legacy vCard and PEP Avatar, vcard
"limits"; -- Enable bandwidth limiting for XMPP connections
-- Nice to have
"version"; -- Replies to server version requests
"uptime"; -- Report how long server has been running
"time"; -- Let others know the time here on this server
"ping"; -- Replies to XMPP pings with pongs
"register"; -- Allow users to register on this server using a client and change passwords
--"mam"; -- Store messages in an archive and allow users to access it
--"csi_simple"; -- Simple Mobile optimizations
-- Admin interfaces
"admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
--"admin_telnet"; -- Opens telnet console interface on localhost port 5582
-- HTTP modules
--"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
--"websocket"; -- XMPP over WebSockets
--"http_files"; -- Serve static files from a directory over HTTP
-- Other specific functionality
--"groups"; -- Shared roster support
--"server_contact_info"; -- Publish contact information for this service
--"announce"; -- Send announcement to all online users
--"welcome"; -- Welcome users who register accounts
--"watchregistrations"; -- Alert admins of registrations
--"motd"; -- Send a message to users when they log in
--"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.
--"proxy65"; -- Enables a file transfer proxy service which clients behind NAT can use
"net_proxy";
"s2s_outgoing_proxy";
}
-- These modules are auto-loaded, but should you want
-- to disable them then uncomment them here:
modules_disabled = {
-- "offline"; -- Store offline messages
-- "c2s"; -- Handle client connections
-- "s2s"; -- Handle server-to-server connections
-- "posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
}
-- Disable account creation by default, for security
-- For more information see https://prosody.im/doc/creating_accounts
allow_registration = false
-- we don't need prosody doing any encryption, xmpp-proxy does this now
-- these are likely set to true somewhere in your file, find them, make them false
-- you can also remove all certificates from your config
s2s_require_encryption = false
s2s_secure_auth = false
-- xmpp-proxy outgoing is listening on this port, make all outgoing s2s connections directly to here
s2s_outgoing_proxy = { "192.5.0.40", 15270 }
-- handle PROXY protocol on these ports
proxy_port_mappings = {
[15222] = "c2s",
[15269] = "s2s"
}
--[[
Specifies a list of trusted hosts or networks which may use the PROXY protocol
If not specified, it will default to: 127.0.0.1, ::1 (local connections only)
An empty table ({}) can be configured to allow connections from any source.
Please read the module documentation about potential security impact.
]]--
proxy_trusted_proxies = {
"192.5.0.40"
}
-- don't listen on any normal c2s/s2s ports (xmpp-proxy listens on these now)
-- you might need to comment these out further down in your config file if you set them
c2s_ports = {}
legacy_ssl_ports = {}
-- you MUST have at least one s2s_ports defined if you want outgoing S2S to work, don't ask..
s2s_ports = {15268}
-- Force clients to use encrypted connections? This option will
-- prevent clients from authenticating unless they are using encryption.
c2s_require_encryption = false
allow_unencrypted_plain_auth = true
-- Some servers have invalid or self-signed certificates. You can list
-- remote domains here that will not be required to authenticate using
-- certificates. They will be authenticated using DNS instead, even
-- when s2s_secure_auth is enabled.
--s2s_insecure_domains = { "insecure.example" }
-- Even if you disable s2s_secure_auth, you can still require valid
-- certificates for some domains by specifying a list here.
--s2s_secure_domains = { "jabber.org" }
-- Enable rate limits for incoming client and server connections
limits = {
c2s = {
rate = "10kb/s";
};
s2sin = {
rate = "30kb/s";
};
}
-- Select the authentication backend to use. The 'internal' providers
-- use Prosody's configured data storage to store the authentication data.
authentication = "internal_hashed"
-- Select the storage backend to use. By default Prosody uses flat files
-- in its configured data directory, but it also supports more backends
-- through modules. An "sql" backend is included by default, but requires
-- additional dependencies. See https://prosody.im/doc/storage for more info.
--storage = "sql" -- Default is "internal"
-- For the "sql" backend, you can uncomment *one* of the below to configure:
--sql = { driver = "SQLite3", database = "prosody.sqlite" } -- Default. 'database' is the filename.
--sql = { driver = "MySQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
--sql = { driver = "PostgreSQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
-- Archiving configuration
-- If mod_mam is enabled, Prosody will store a copy of every message. This
-- is used to synchronize conversations between multiple clients, even if
-- they are offline. This setting controls how long Prosody will keep
-- messages in the archive before removing them.
archive_expires_after = "1w" -- Remove archived messages after 1 week
-- You can also configure messages to be stored in-memory only. For more
-- archiving options, see https://prosody.im/doc/modules/mod_mam
-- Logging configuration
-- For advanced logging see https://prosody.im/doc/logging
log = {
-- info = "prosody.log"; -- Change 'info' to 'debug' for verbose logging
-- error = "prosody.err";
--info = "*syslog"; -- Uncomment this for logging to syslog
debug = "*console"; -- Log to the console, useful for debugging with daemonize=false
}
-- Uncomment to enable statistics
-- For more info see https://prosody.im/doc/statistics
-- statistics = "internal"
-- Certificates
-- Every virtual host and component needs a certificate so that clients and
-- servers can securely verify its identity. Prosody will automatically load
-- certificates/keys from the directory specified here.
-- For more information, including how to use 'prosodyctl' to auto-import certificates
-- (from e.g. Let's Encrypt) see https://prosody.im/doc/certificates
-- Location of directory to find certificates in (relative to main config file):
certificates = "certs"
-- HTTPS currently only supports a single certificate, specify it here:
--https_certificate = "/etc/prosody/certs/localhost.crt"
----------- Virtual hosts -----------
-- You need to add a VirtualHost entry for each domain you wish Prosody to serve.
-- Settings under each VirtualHost entry apply *only* to that host.
VirtualHost "one.example.org"
--VirtualHost "example.com"
-- certificate = "/path/to/example.crt"
------ Components ------
-- You can specify components to add hosts that provide special services,
-- like multi-user conferences, and transports.
-- For more information on components, see https://prosody.im/doc/components
---Set up a MUC (multi-user chat) room server on conference.example.com:
--Component "conference.example.com" "muc"
--- Store MUC messages in an archive and allow users to access it
--modules_enabled = { "muc_mam" }
---Set up an external component (default component port is 5347)
--
-- External components allow adding various services, such as gateways/
-- transports to other networks like ICQ, MSN and Yahoo. For more info
-- see: https://prosody.im/doc/components#adding_an_external_component
--
--Component "gateway.example.com"
-- component_secret = "password"

View File

@ -0,0 +1,251 @@
--Important for systemd
-- daemonize is important for systemd. if you set this to false the systemd startup will freeze.
daemonize = false
run_as_root = true
pidfile = "/run/prosody/prosody.pid"
plugin_paths = { "/opt/xmpp-proxy/prosody-modules", "/opt/prosody-modules" }
-- Prosody Example Configuration File
--
-- Information on configuring Prosody can be found on our
-- website at https://prosody.im/doc/configure
--
-- Tip: You can check that the syntax of this file is correct
-- when you have finished by running this command:
-- prosodyctl check config
-- If there are any errors, it will let you know what and where
-- they are, otherwise it will keep quiet.
--
-- The only thing left to do is rename this file to remove the .dist ending, and fill in the
-- blanks. Good luck, and happy Jabbering!
---------- Server-wide settings ----------
-- Settings in this section apply to the whole server and are the default settings
-- for any virtual hosts
-- This is a (by default, empty) list of accounts that are admins
-- for the server. Note that you must create the accounts separately
-- (see https://prosody.im/doc/creating_accounts for info)
-- Example: admins = { "user1@example.com", "user2@example.net" }
admins = { }
-- Enable use of libevent for better performance under high load
-- For more information see: https://prosody.im/doc/libevent
--use_libevent = true
-- Prosody will always look in its source directory for modules, but
-- this option allows you to specify additional locations where Prosody
-- will look for modules first. For community modules, see https://modules.prosody.im/
--plugin_paths = {}
-- This is the list of modules Prosody will load on startup.
-- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too.
-- Documentation for bundled modules can be found at: https://prosody.im/doc/modules
modules_enabled = {
-- Generally required
"roster"; -- Allow users to have a roster. Recommended ;)
"saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
--"tls"; -- Add support for secure TLS on c2s/s2s connections
--"dialback"; -- s2s dialback support
"disco"; -- Service discovery
-- Not essential, but recommended
"carbons"; -- Keep multiple clients in sync
"pep"; -- Enables users to publish their avatar, mood, activity, playing music and more
"private"; -- Private XML storage (for room bookmarks, etc.)
"blocklist"; -- Allow users to block communications with other users
"vcard4"; -- User profiles (stored in PEP)
"vcard_legacy"; -- Conversion between legacy vCard and PEP Avatar, vcard
"limits"; -- Enable bandwidth limiting for XMPP connections
-- Nice to have
"version"; -- Replies to server version requests
"uptime"; -- Report how long server has been running
"time"; -- Let others know the time here on this server
"ping"; -- Replies to XMPP pings with pongs
"register"; -- Allow users to register on this server using a client and change passwords
--"mam"; -- Store messages in an archive and allow users to access it
--"csi_simple"; -- Simple Mobile optimizations
-- Admin interfaces
"admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
--"admin_telnet"; -- Opens telnet console interface on localhost port 5582
-- HTTP modules
--"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
--"websocket"; -- XMPP over WebSockets
--"http_files"; -- Serve static files from a directory over HTTP
-- Other specific functionality
--"groups"; -- Shared roster support
--"server_contact_info"; -- Publish contact information for this service
--"announce"; -- Send announcement to all online users
--"welcome"; -- Welcome users who register accounts
--"watchregistrations"; -- Alert admins of registrations
--"motd"; -- Send a message to users when they log in
--"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.
--"proxy65"; -- Enables a file transfer proxy service which clients behind NAT can use
"net_proxy";
"s2s_outgoing_proxy";
}
-- These modules are auto-loaded, but should you want
-- to disable them then uncomment them here:
modules_disabled = {
-- "offline"; -- Store offline messages
-- "c2s"; -- Handle client connections
-- "s2s"; -- Handle server-to-server connections
-- "posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
}
-- Disable account creation by default, for security
-- For more information see https://prosody.im/doc/creating_accounts
allow_registration = false
-- we don't need prosody doing any encryption, xmpp-proxy does this now
-- these are likely set to true somewhere in your file, find them, make them false
-- you can also remove all certificates from your config
s2s_require_encryption = false
s2s_secure_auth = false
-- xmpp-proxy outgoing is listening on this port, make all outgoing s2s connections directly to here
s2s_outgoing_proxy = { "192.5.0.50", 15270 }
-- handle PROXY protocol on these ports
proxy_port_mappings = {
[15222] = "c2s",
[15269] = "s2s"
}
--[[
Specifies a list of trusted hosts or networks which may use the PROXY protocol
If not specified, it will default to: 127.0.0.1, ::1 (local connections only)
An empty table ({}) can be configured to allow connections from any source.
Please read the module documentation about potential security impact.
]]--
proxy_trusted_proxies = {
"192.5.0.50"
}
-- don't listen on any normal c2s/s2s ports (xmpp-proxy listens on these now)
-- you might need to comment these out further down in your config file if you set them
c2s_ports = {}
legacy_ssl_ports = {}
-- you MUST have at least one s2s_ports defined if you want outgoing S2S to work, don't ask..
s2s_ports = {15268}
-- Force clients to use encrypted connections? This option will
-- prevent clients from authenticating unless they are using encryption.
c2s_require_encryption = false
allow_unencrypted_plain_auth = true
-- Some servers have invalid or self-signed certificates. You can list
-- remote domains here that will not be required to authenticate using
-- certificates. They will be authenticated using DNS instead, even
-- when s2s_secure_auth is enabled.
--s2s_insecure_domains = { "insecure.example" }
-- Even if you disable s2s_secure_auth, you can still require valid
-- certificates for some domains by specifying a list here.
--s2s_secure_domains = { "jabber.org" }
-- Enable rate limits for incoming client and server connections
limits = {
c2s = {
rate = "10kb/s";
};
s2sin = {
rate = "30kb/s";
};
}
-- Select the authentication backend to use. The 'internal' providers
-- use Prosody's configured data storage to store the authentication data.
authentication = "internal_hashed"
-- Select the storage backend to use. By default Prosody uses flat files
-- in its configured data directory, but it also supports more backends
-- through modules. An "sql" backend is included by default, but requires
-- additional dependencies. See https://prosody.im/doc/storage for more info.
--storage = "sql" -- Default is "internal"
-- For the "sql" backend, you can uncomment *one* of the below to configure:
--sql = { driver = "SQLite3", database = "prosody.sqlite" } -- Default. 'database' is the filename.
--sql = { driver = "MySQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
--sql = { driver = "PostgreSQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
-- Archiving configuration
-- If mod_mam is enabled, Prosody will store a copy of every message. This
-- is used to synchronize conversations between multiple clients, even if
-- they are offline. This setting controls how long Prosody will keep
-- messages in the archive before removing them.
archive_expires_after = "1w" -- Remove archived messages after 1 week
-- You can also configure messages to be stored in-memory only. For more
-- archiving options, see https://prosody.im/doc/modules/mod_mam
-- Logging configuration
-- For advanced logging see https://prosody.im/doc/logging
log = {
-- info = "prosody.log"; -- Change 'info' to 'debug' for verbose logging
-- error = "prosody.err";
--info = "*syslog"; -- Uncomment this for logging to syslog
debug = "*console"; -- Log to the console, useful for debugging with daemonize=false
}
-- Uncomment to enable statistics
-- For more info see https://prosody.im/doc/statistics
-- statistics = "internal"
-- Certificates
-- Every virtual host and component needs a certificate so that clients and
-- servers can securely verify its identity. Prosody will automatically load
-- certificates/keys from the directory specified here.
-- For more information, including how to use 'prosodyctl' to auto-import certificates
-- (from e.g. Let's Encrypt) see https://prosody.im/doc/certificates
-- Location of directory to find certificates in (relative to main config file):
certificates = "certs"
-- HTTPS currently only supports a single certificate, specify it here:
--https_certificate = "/etc/prosody/certs/localhost.crt"
----------- Virtual hosts -----------
-- You need to add a VirtualHost entry for each domain you wish Prosody to serve.
-- Settings under each VirtualHost entry apply *only* to that host.
VirtualHost "two.example.org"
--VirtualHost "example.com"
-- certificate = "/path/to/example.crt"
------ Components ------
-- You can specify components to add hosts that provide special services,
-- like multi-user conferences, and transports.
-- For more information on components, see https://prosody.im/doc/components
---Set up a MUC (multi-user chat) room server on conference.example.com:
--Component "conference.example.com" "muc"
--- Store MUC messages in an archive and allow users to access it
--modules_enabled = { "muc_mam" }
---Set up an external component (default component port is 5347)
--
-- External components allow adding various services, such as gateways/
-- transports to other networks like ICQ, MSN and Yahoo. For more info
-- see: https://prosody.im/doc/components#adding_an_external_component
--
--Component "gateway.example.com"
-- component_secret = "password"

View File

@ -0,0 +1,42 @@
# interfaces to listen for reverse proxy STARTTLS/Direct TLS XMPP connections on, should be open to the internet
# incoming_listen = [ "0.0.0.0:5281" ]
# interfaces to listen for reverse proxy QUIC XMPP connections on, should be open to the internet
quic_listen = [ "0.0.0.0:443" ]
# interfaces to listen for outgoing proxy TCP XMPP connections on, should be localhost
outgoing_listen = [ "0.0.0.0:15270" ]
# these ports shouldn't do any TLS, but should assume any connection from xmpp-proxy is secure
# prosody module: https://modules.prosody.im/mod_secure_interfaces.html
# c2s port backend XMPP server listens on
c2s_target = "192.5.0.20:15222"
# s2s port backend XMPP server listens on
s2s_target = "192.5.0.20:15269"
# send PROXYv1 header to backend XMPP server
# https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
# prosody module: https://modules.prosody.im/mod_net_proxy.html
# ejabberd config: https://docs.ejabberd.im/admin/configuration/listen-options/#use-proxy-protocol
proxy = true
# limit incoming stanzas to this many bytes, default to ejabberd's default
# https://github.com/processone/ejabberd/blob/master/ejabberd.yml.example#L32
# xmpp-proxy will use this many bytes + 16k per connection
max_stanza_size_bytes = 262_144
# TLS key/certificate valid for all your XMPP domains, PEM format
# included systemd unit can only read files from /etc/xmpp-proxy/ so put them in there
tls_key = "/etc/prosody/certs/xp1.example.org.key"
tls_cert = "/etc/prosody/certs/xp1.example.org.crt"
# configure logging, defaults are commented
# can also set env variables XMPP_PROXY_LOG_LEVEL and/or XMPP_PROXY_LOG_STYLE, but values in this file override them
# many options, trace is XML-console-level, refer to: https://docs.rs/env_logger/0.8.3/env_logger/#enabling-logging
#log_level = "info"
# for development/debugging:
log_level = "info,xmpp_proxy=trace"
# one of auto, always, never, refer to: https://docs.rs/env_logger/0.8.3/env_logger/#disabling-colors
#log_style = "never"

View File

@ -0,0 +1,42 @@
# interfaces to listen for reverse proxy STARTTLS/Direct TLS XMPP connections on, should be open to the internet
# incoming_listen = [ "0.0.0.0:5281" ]
# interfaces to listen for reverse proxy QUIC XMPP connections on, should be open to the internet
quic_listen = [ "0.0.0.0:443" ]
# interfaces to listen for outgoing proxy TCP XMPP connections on, should be localhost
outgoing_listen = [ "0.0.0.0:15270" ]
# these ports shouldn't do any TLS, but should assume any connection from xmpp-proxy is secure
# prosody module: https://modules.prosody.im/mod_secure_interfaces.html
# c2s port backend XMPP server listens on
c2s_target = "192.5.0.30:15222"
# s2s port backend XMPP server listens on
s2s_target = "192.5.0.30:15269"
# send PROXYv1 header to backend XMPP server
# https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
# prosody module: https://modules.prosody.im/mod_net_proxy.html
# ejabberd config: https://docs.ejabberd.im/admin/configuration/listen-options/#use-proxy-protocol
proxy = true
# limit incoming stanzas to this many bytes, default to ejabberd's default
# https://github.com/processone/ejabberd/blob/master/ejabberd.yml.example#L32
# xmpp-proxy will use this many bytes + 16k per connection
max_stanza_size_bytes = 262_144
# TLS key/certificate valid for all your XMPP domains, PEM format
# included systemd unit can only read files from /etc/xmpp-proxy/ so put them in there
tls_key = "/etc/prosody/certs/xp2.example.org.key"
tls_cert = "/etc/prosody/certs/xp2.example.org.crt"
# configure logging, defaults are commented
# can also set env variables XMPP_PROXY_LOG_LEVEL and/or XMPP_PROXY_LOG_STYLE, but values in this file override them
# many options, trace is XML-console-level, refer to: https://docs.rs/env_logger/0.8.3/env_logger/#enabling-logging
#log_level = "info"
# for development/debugging:
log_level = "info,xmpp_proxy=trace"
# one of auto, always, never, refer to: https://docs.rs/env_logger/0.8.3/env_logger/#disabling-colors
#log_style = "never"

View File

@ -0,0 +1,44 @@
# interfaces to listen for reverse proxy STARTTLS/Direct TLS XMPP connections on, should be open to the internet
incoming_listen = [ ]
# interfaces to listen for reverse proxy QUIC XMPP connections on, should be open to the internet
quic_listen = [ ]
# interfaces to listen for reverse proxy TLS WebSocket (wss) XMPP connections on, should be open to the internet
websocket_listen = [ ]
# interfaces to listen for outgoing proxy TCP XMPP connections on, should be localhost
outgoing_listen = [ "0.0.0.0:5222" ]
# these ports shouldn't do any TLS, but should assume any connection from xmpp-proxy is secure
# prosody module: https://modules.prosody.im/mod_secure_interfaces.html
# c2s port backend XMPP server listens on
c2s_target = "127.0.0.1:15222"
# s2s port backend XMPP server listens on
s2s_target = "127.0.0.1:15269"
# send PROXYv1 header to backend XMPP server
# https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
# prosody module: https://modules.prosody.im/mod_net_proxy.html
# ejabberd config: https://docs.ejabberd.im/admin/configuration/listen-options/#use-proxy-protocol
proxy = true
# limit incoming stanzas to this many bytes, default to ejabberd's default
# https://github.com/processone/ejabberd/blob/master/ejabberd.yml.example#L32
# xmpp-proxy will use this many bytes + 16k per connection
max_stanza_size_bytes = 262_144
# TLS key/certificate valid for all your XMPP domains, PEM format
# included systemd unit can only read files from /etc/xmpp-proxy/ so put them in there
tls_key = "/etc/certs/rsa/one.example.org.key"
tls_cert = "/etc/certs/rsa/one.example.org.crt"
# configure logging, defaults are commented
# can also set env variables XMPP_PROXY_LOG_LEVEL and/or XMPP_PROXY_LOG_STYLE, but values in this file override them
# many options, trace is XML-console-level, refer to: https://docs.rs/env_logger/0.8.3/env_logger/#enabling-logging
#log_level = "info"
# for development/debugging:
log_level = "info,xmpp_proxy=trace"
# one of auto, always, never, refer to: https://docs.rs/env_logger/0.8.3/env_logger/#disabling-colors
#log_style = "never"

View File

@ -39,24 +39,35 @@ pub fn server_config(certs_key: Arc<CertsKey>) -> Result<ServerConfig> {
Ok(config)
}
#[cfg(not(feature = "s2s-incoming"))]
#[cfg(not(any(feature = "s2s-incoming", feature = "webtransport")))]
pub type ServerCerts = ();
#[cfg(feature = "s2s-incoming")]
#[cfg(any(feature = "s2s-incoming", feature = "webtransport"))]
#[derive(Clone)]
pub enum ServerCerts {
Tls(&'static ServerConnection),
#[cfg(feature = "quic")]
Quic(Arc<quinn::Connection>),
Quic(Option<Vec<Certificate>>, Option<String>, Option<Vec<u8>>), // todo: wrap this in arc or something now
}
#[cfg(feature = "s2s-incoming")]
#[cfg(any(feature = "s2s-incoming", feature = "webtransport"))]
impl ServerCerts {
#[cfg(feature = "quic")]
pub fn quic(conn: &quinn::Connection) -> ServerCerts {
let certs = conn.peer_identity().and_then(|v| v.downcast::<Vec<Certificate>>().ok()).map(|v| v.to_vec());
let (sni, alpn) = conn
.handshake_data()
.and_then(|v| v.downcast::<quinn::crypto::rustls::HandshakeData>().ok())
.map(|h| (h.server_name, h.protocol))
.unwrap_or_default();
ServerCerts::Quic(certs, sni, alpn)
}
pub fn peer_certificates(&self) -> Option<Vec<Certificate>> {
match self {
ServerCerts::Tls(c) => c.peer_certificates().map(|c| c.to_vec()),
#[cfg(feature = "quic")]
ServerCerts::Quic(c) => c.peer_identity().and_then(|v| v.downcast::<Vec<Certificate>>().ok()).map(|v| v.to_vec()),
ServerCerts::Quic(certs, _, _) => certs.clone(),
}
}
@ -64,7 +75,7 @@ impl ServerCerts {
match self {
ServerCerts::Tls(c) => c.server_name().map(|s| s.to_string()),
#[cfg(feature = "quic")]
ServerCerts::Quic(c) => c.handshake_data().and_then(|v| v.downcast::<quinn::crypto::rustls::HandshakeData>().ok()).and_then(|h| h.server_name),
ServerCerts::Quic(_, sni, _) => sni.clone(),
}
}
@ -72,7 +83,7 @@ impl ServerCerts {
match self {
ServerCerts::Tls(c) => c.alpn_protocol().map(|s| s.to_vec()),
#[cfg(feature = "quic")]
ServerCerts::Quic(c) => c.handshake_data().and_then(|v| v.downcast::<quinn::crypto::rustls::HandshakeData>().ok()).and_then(|h| h.protocol),
ServerCerts::Quic(_, _, alpn) => alpn.clone(),
}
}
@ -80,7 +91,7 @@ impl ServerCerts {
match self {
ServerCerts::Tls(_) => true,
#[cfg(feature = "quic")]
ServerCerts::Quic(_) => false,
ServerCerts::Quic(_, _, _) => false,
}
}
}

View File

@ -19,6 +19,13 @@ impl OutgoingConfig {
_ => ClientConfig::builder().with_safe_defaults().with_custom_certificate_verifier(cert_verifier).with_no_client_auth(),
};
#[cfg(feature = "webtransport")]
let config_webtransport_alpn = {
let mut config = config.clone();
config.alpn_protocols.push(webtransport_quinn::ALPN.to_vec());
Arc::new(config)
};
let mut config_alpn = config.clone();
config_alpn.alpn_protocols.push(if is_c2s { ALPN_XMPP_CLIENT } else { ALPN_XMPP_SERVER }.to_vec());
@ -30,6 +37,8 @@ impl OutgoingConfig {
OutgoingVerifierConfig {
max_stanza_size_bytes: self.max_stanza_size_bytes,
#[cfg(feature = "webtransport")]
config_webtransport_alpn,
config_alpn,
connector_alpn,
connector,
@ -41,6 +50,9 @@ impl OutgoingConfig {
pub struct OutgoingVerifierConfig {
pub max_stanza_size_bytes: usize,
#[cfg(feature = "webtransport")]
pub config_webtransport_alpn: Arc<ClientConfig>,
pub config_alpn: Arc<ClientConfig>,
pub connector_alpn: TlsConnector,

View File

@ -17,6 +17,9 @@ pub mod srv;
#[cfg(feature = "websocket")]
pub mod websocket;
#[cfg(feature = "webtransport")]
pub mod webtransport;
#[cfg(any(feature = "s2s-incoming", feature = "outgoing"))]
pub mod verify;

View File

@ -36,26 +36,21 @@ fn internal_spawn_quic_listener(incoming: Endpoint, local_addr: SocketAddr, conf
tokio::spawn(async move {
if let Ok(new_conn) = incoming_conn.await {
let client_addr = Context::new("quic-in", new_conn.remote_address());
let new_conn = Arc::new(new_conn);
#[cfg(feature = "s2s-incoming")]
let server_certs = ServerCerts::Quic(new_conn.clone());
#[cfg(not(feature = "s2s-incoming"))]
let server_certs = ();
info!("{} connected new connection", client_addr.log_from());
while let Ok((wrt, rd)) = new_conn.accept_bi().await {
let config = config.clone();
let mut client_addr = client_addr.clone();
let server_certs = server_certs.clone();
info!("{} connected new stream", client_addr.log_from());
tokio::spawn(async move {
if let Err(e) = shuffle_rd_wr(StanzaRead::new(rd), StanzaWrite::new(wrt), config, server_certs, local_addr, &mut client_addr).await {
error!("{} {}", client_addr.log_from(), e);
}
});
}
#[cfg(any(feature = "s2s-incoming", feature = "webtransport"))]
let server_certs = {
let server_certs = ServerCerts::quic(&new_conn);
#[cfg(feature = "webtransport")]
if server_certs.alpn().map(|a| a == webtransport_quinn::ALPN).unwrap_or(false) {
return crate::webtransport::incoming::handle_webtransport_session(new_conn, config, server_certs, local_addr, client_addr).await;
}
server_certs
};
#[cfg(not(any(feature = "s2s-incoming", feature = "webtransport")))]
let server_certs = ();
handle_quic_session(new_conn, config, server_certs, local_addr, client_addr).await
}
});
}
@ -64,7 +59,23 @@ fn internal_spawn_quic_listener(incoming: Endpoint, local_addr: SocketAddr, conf
})
}
pub fn quic_server_config(server_config: rustls::ServerConfig) -> ServerConfig {
pub async fn handle_quic_session(conn: quinn::Connection, config: Arc<IncomingConfig>, server_certs: ServerCerts, local_addr: SocketAddr, client_addr: Context<'static>) {
while let Ok((wrt, rd)) = conn.accept_bi().await {
let config = config.clone();
let mut client_addr = client_addr.clone();
let server_certs = server_certs.clone();
info!("{} connected new stream", client_addr.log_from());
tokio::spawn(async move {
if let Err(e) = shuffle_rd_wr(StanzaRead::new(rd), StanzaWrite::new(wrt), config, server_certs, local_addr, &mut client_addr).await {
error!("{} {}", client_addr.log_from(), e);
}
});
}
}
pub fn quic_server_config(mut server_config: rustls::ServerConfig) -> ServerConfig {
#[cfg(feature = "webtransport")]
server_config.alpn_protocols.push(webtransport_quinn::ALPN.to_vec());
let transport_config = quinn::TransportConfig::default();
// todo: configure transport_config here if needed
let mut server_config = ServerConfig::with_crypto(Arc::new(server_config));

View File

@ -13,7 +13,7 @@ use crate::{
use anyhow::{bail, Result};
use data_encoding::BASE64;
use log::{debug, error, trace};
use reqwest::Client;
use reqwest::{Client, Url};
use ring::digest::{Algorithm, Context as DigestContext, SHA256, SHA512};
use serde::Deserialize;
use std::{
@ -60,7 +60,10 @@ enum XmppConnectionType {
#[cfg(feature = "quic")]
QUIC,
#[cfg(feature = "websocket")]
// uri, origin
WebSocket(Uri, String),
#[cfg(feature = "webtransport")]
WebTransport(Url),
}
impl XmppConnectionType {
@ -69,11 +72,13 @@ impl XmppConnectionType {
#[cfg(feature = "quic")]
XmppConnectionType::QUIC => 0,
#[cfg(feature = "tls")]
XmppConnectionType::DirectTLS => 1,
XmppConnectionType::DirectTLS => 2,
#[cfg(feature = "tls")]
XmppConnectionType::StartTLS => 2,
XmppConnectionType::StartTLS => 3,
#[cfg(feature = "websocket")]
XmppConnectionType::WebSocket(_, _) => 3,
XmppConnectionType::WebSocket(_, _) => 4,
#[cfg(feature = "webtransport")]
XmppConnectionType::WebTransport(_) => 1,
}
}
}
@ -84,7 +89,7 @@ impl Ord for XmppConnectionType {
if cmp != Ordering::Equal {
return cmp;
}
// so they are the same type, but WebSocket is a special case...
// so they are the same type, but WebSocket and WebTransport is a special case...
match (self, other) {
#[cfg(feature = "websocket")]
(XmppConnectionType::WebSocket(self_uri, self_origin), XmppConnectionType::WebSocket(other_uri, other_origin)) => {
@ -94,6 +99,8 @@ impl Ord for XmppConnectionType {
}
self_origin.cmp(other_origin)
}
#[cfg(feature = "webtransport")]
(XmppConnectionType::WebTransport(self_url), XmppConnectionType::WebTransport(other_url)) => self_url.cmp(other_url),
(_, _) => Ordering::Equal,
}
}
@ -237,6 +244,20 @@ impl XmppConnection {
}
}
},
#[cfg(feature = "webtransport")]
XmppConnectionType::WebTransport(ref url) => match crate::webtransport::outgoing::webtransport_connect(to_addr, domain, url, config).await {
Ok((wr, rd)) => return Ok((wr, rd, to_addr, "webtransport-out")),
Err(e) => {
if self.secure && self.target != orig_domain {
match crate::webtransport::outgoing::webtransport_connect(to_addr, orig_domain, url, config).await {
Ok((wr, rd)) => return Ok((wr, rd, to_addr, "webtransport-out")),
Err(e2) => error!("webtransport connection failed to IP {} from URL {}, error try 1: {}, error try 2: {}", to_addr, url, e, e2),
}
} else {
error!("websocket connection failed to IP {} from URL {}, error: {}", to_addr, url, e)
}
}
},
}
}
bail!("cannot connect to any IPs for SRV: {}", self.target)
@ -301,6 +322,21 @@ fn wss_to_srv(url: &str, secure: bool) -> Option<XmppConnection> {
})
}
#[cfg(feature = "webtransport")]
fn wt_to_srv(url: &str) -> Option<(XmppConnectionType, u16)> {
let url = match Url::parse(url) {
Ok(url) => url,
Err(e) => {
debug!("invalid URL record '{}': {}", url, e);
return None;
}
};
let port = if let Some(port) = url.port() { port } else { 443 };
Some((XmppConnectionType::WebTransport(url), port))
}
#[cfg(feature = "websocket")]
fn collect_txts(ret: &mut Vec<XmppConnection>, txt_records: std::result::Result<TxtLookup, ResolveError>, is_c2s: bool) {
if let Ok(txt_records) = txt_records {
@ -534,6 +570,12 @@ enum Link {
#[serde(flatten)]
link: Option<LinkCommon>,
},
#[serde(rename = "urn:xmpp:alt-connections:webtransport")]
WebTransport {
href: String,
#[serde(flatten)]
link: LinkCommon,
},
#[serde(rename = "urn:xmpp:alt-connections:tls")]
DirectTLS {
#[serde(flatten)]
@ -546,6 +588,12 @@ enum Link {
link: LinkCommon,
port: u16,
},
#[serde(rename = "urn:xmpp:alt-connections:s2s-webtransport")]
S2SWebTransport {
href: String,
#[serde(flatten)]
link: LinkCommon,
},
#[serde(rename = "urn:xmpp:alt-connections:s2s-websocket")]
S2SWebSocket {
href: String,
@ -630,6 +678,24 @@ impl Link {
None
};
}
#[cfg(feature = "webtransport")]
Link::WebTransport { href, link } => {
return if is_c2s {
let (conn_type, port) = wt_to_srv(&href)?;
link.into_xmpp_connection(conn_type, port)
} else {
None
};
}
#[cfg(feature = "webtransport")]
Link::S2SWebTransport { href, link } => {
return if !is_c2s {
let (conn_type, port) = wt_to_srv(&href)?;
link.into_xmpp_connection(conn_type, port)
} else {
None
};
}
_ => return None,
};

View File

@ -127,12 +127,12 @@ pub async fn handle_tls_connection<S: AsyncReadWritePeekSplit>(mut stream: S, cl
// where we read the first stanza, where we are guaranteed the handshake is complete, but I can't
// do that without ignoring the lifetime and just pulling a C programmer and pinky promising to be
// *very careful* that this reference doesn't outlive stream...
#[cfg(feature = "s2s-incoming")]
#[cfg(any(feature = "s2s-incoming", feature = "webtransport"))]
let server_certs = {
let server_connection: &'static ServerConnection = unsafe { std::mem::transmute(server_connection) };
ServerCerts::Tls(server_connection)
};
#[cfg(not(feature = "s2s-incoming"))]
#[cfg(not(any(feature = "s2s-incoming", feature = "webtransport")))]
let server_certs = ();
#[cfg(not(feature = "websocket"))]

View File

@ -0,0 +1,44 @@
use crate::{
common::incoming::{shuffle_rd_wr, IncomingConfig, ServerCerts},
context::Context,
in_out::{StanzaRead, StanzaWrite},
};
use log::{error, info};
use std::{net::SocketAddr, sync::Arc};
pub async fn handle_webtransport_session(conn: quinn::Connection, config: Arc<IncomingConfig>, server_certs: ServerCerts, local_addr: SocketAddr, mut client_addr: Context<'static>) {
client_addr.set_proto("webtransport-in");
// Perform the WebTransport handshake.
let request = match webtransport_quinn::accept(conn).await {
Ok(r) => r,
Err(e) => {
error!("{} {}", client_addr.log_from(), e);
return;
}
};
info!("{} received request URL: {}", client_addr.log_from(), request.url());
// Accept the session.
let session = match request.ok().await {
Ok(r) => r,
Err(e) => {
error!("{} {}", client_addr.log_from(), e);
return;
}
};
info!("{} connected new session", client_addr.log_from());
while let Ok((wrt, rd)) = session.accept_bi().await {
let config = config.clone();
let mut client_addr = client_addr.clone();
let server_certs = server_certs.clone();
info!("{} connected new stream", client_addr.log_from());
tokio::spawn(async move {
if let Err(e) = shuffle_rd_wr(StanzaRead::new(rd), StanzaWrite::new(wrt), config, server_certs, local_addr, &mut client_addr).await {
error!("{} {}", client_addr.log_from(), e);
}
});
}
}

5
src/webtransport/mod.rs Normal file
View File

@ -0,0 +1,5 @@
#[cfg(feature = "incoming")]
pub mod incoming;
#[cfg(feature = "outgoing")]
pub mod outgoing;

View File

@ -0,0 +1,26 @@
use std::net::SocketAddr;
use crate::{
common::outgoing::OutgoingVerifierConfig,
in_out::{StanzaRead, StanzaWrite},
};
use anyhow::Result;
use log::trace;
use reqwest::Url;
pub async fn webtransport_connect(target: SocketAddr, server_name: &str, url: &Url, config: &OutgoingVerifierConfig) -> Result<(StanzaWrite, StanzaRead)> {
let bind_addr = "0.0.0.0:0".parse().unwrap();
let mut endpoint = quinn::Endpoint::client(bind_addr)?;
endpoint.set_default_client_config(quinn::ClientConfig::new(config.config_webtransport_alpn.clone()));
// connect to server
let connection = endpoint.connect(target, server_name)?.await?;
trace!("quic pre-wt connected: addr={}", connection.remote_address());
let connection = webtransport_quinn::connect_with(connection, url).await?;
trace!("webtransport connected: addr={}", connection.remote_address());
let (wrt, rd) = connection.open_bi().await?;
Ok((StanzaWrite::new(wrt), StanzaRead::new(rd)))
}

View File

@ -1,7 +1,7 @@
# interfaces to listen for reverse proxy STARTTLS/Direct TLS/TLS WebSocket (wss) XMPP connections on, should be open to the internet
incoming_listen = [ "0.0.0.0:5222", "0.0.0.0:5269", "0.0.0.0:443" ]
# interfaces to listen for reverse proxy QUIC XMPP connections on, should be open to the internet
# interfaces to listen for reverse proxy QUIC/WebTransport XMPP connections on, should be open to the internet
quic_listen = [ "0.0.0.0:443" ]
# interfaces to listen for outgoing proxy TCP or WebSocket XMPP connections on, should be localhost or a path for a unix socket
outgoing_listen = [ "127.0.0.1:15270" ]
@ -39,4 +39,4 @@ tls_cert = "/etc/xmpp-proxy/fullchain.cer"
#log_level = "info,xmpp_proxy=trace"
# one of auto, always, never, refer to: https://docs.rs/env_logger/0.8.3/env_logger/#disabling-colors
#log_style = "never"
#log_style = "never"