Implement future host-meta.json proposal
This commit is contained in:
parent
5f05332e37
commit
4f5938e0ce
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1645,5 +1645,6 @@ dependencies = [
|
||||
"tokio-tungstenite",
|
||||
"toml",
|
||||
"trust-dns-resolver",
|
||||
"untrusted",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
@ -29,6 +29,7 @@ anyhow = "1.0"
|
||||
tokio = { version = "1.9", features = ["net", "rt", "rt-multi-thread", "macros", "io-util", "signal"] }
|
||||
ring = "0.16"
|
||||
data-encoding = "2.3"
|
||||
untrusted = "0.7"
|
||||
|
||||
# logging deps
|
||||
log = "0.4"
|
||||
|
@ -33,9 +33,6 @@
|
||||
"priority": 10,
|
||||
"weight": 50,
|
||||
"sni": "example.org",
|
||||
"alpn": [
|
||||
"xmpp-client"
|
||||
],
|
||||
"ech": "eG1wcC1jbGllbnQ="
|
||||
},
|
||||
{
|
||||
@ -48,9 +45,6 @@
|
||||
"priority": 5,
|
||||
"weight": 50,
|
||||
"sni": "example.org",
|
||||
"alpn": [
|
||||
"xmpp-client"
|
||||
],
|
||||
"ech": "eG1wcC1jbGllbnQ="
|
||||
},
|
||||
{
|
||||
@ -63,11 +57,6 @@
|
||||
"priority": 15,
|
||||
"weight": 50,
|
||||
"sni": "example.org",
|
||||
"alpn": [
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"h3"
|
||||
],
|
||||
"ech": "eG1wcC1jbGllbnQ="
|
||||
},
|
||||
{
|
||||
@ -80,9 +69,6 @@
|
||||
"priority": 10,
|
||||
"weight": 50,
|
||||
"sni": "example.org",
|
||||
"alpn": [
|
||||
"xmpp-server"
|
||||
],
|
||||
"ech": "eG1wcC1jbGllbnQ="
|
||||
},
|
||||
{
|
||||
@ -95,9 +81,6 @@
|
||||
"priority": 5,
|
||||
"weight": 50,
|
||||
"sni": "example.org",
|
||||
"alpn": [
|
||||
"xmpp-server"
|
||||
],
|
||||
"ech": "eG1wcC1jbGllbnQ="
|
||||
}
|
||||
]
|
||||
|
@ -70,9 +70,6 @@
|
||||
"priority": 10,
|
||||
"weight": 50,
|
||||
"sni": "example.org",
|
||||
"alpn": [
|
||||
"xmpp-client"
|
||||
],
|
||||
"ech": "eG1wcC1jbGllbnQ="
|
||||
},
|
||||
{
|
||||
@ -85,9 +82,6 @@
|
||||
"priority": 5,
|
||||
"weight": 50,
|
||||
"sni": "example.org",
|
||||
"alpn": [
|
||||
"xmpp-client"
|
||||
],
|
||||
"ech": "eG1wcC1jbGllbnQ="
|
||||
},
|
||||
{
|
||||
@ -117,9 +111,6 @@
|
||||
"priority": 10,
|
||||
"weight": 50,
|
||||
"sni": "example.org",
|
||||
"alpn": [
|
||||
"xmpp-server"
|
||||
],
|
||||
"ech": "eG1wcC1jbGllbnQ="
|
||||
},
|
||||
{
|
||||
@ -132,9 +123,6 @@
|
||||
"priority": 5,
|
||||
"weight": 50,
|
||||
"sni": "example.org",
|
||||
"alpn": [
|
||||
"xmpp-server"
|
||||
],
|
||||
"ech": "eG1wcC1jbGllbnQ="
|
||||
},
|
||||
{
|
||||
|
@ -1,23 +0,0 @@
|
||||
$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
|
@ -1,25 +0,0 @@
|
||||
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 {
|
||||
default_type application/xrd+xml;
|
||||
return 200 '<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="urn:xmpp:alt-connections:s2s-websocket" href="wss://xp1.example.org:5281/xmpp-websocket"/><Link rel="urn:xmpp:alt-connections:websocket" href="wss://xp1.example.org:5281/xmpp-websocket"/></XRD>';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
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 {
|
||||
default_type application/xrd+xml;
|
||||
return 200 '<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="urn:xmpp:alt-connections:s2s-websocket" href="wss://xp2.example.org:5281/xmpp-websocket"/><Link rel="urn:xmpp:alt-connections:websocket" href="wss://xp2.example.org:5281/xmpp-websocket"/></XRD>';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,251 +0,0 @@
|
||||
--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"
|
@ -1,251 +0,0 @@
|
||||
--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"
|
@ -1,42 +0,0 @@
|
||||
|
||||
# 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 = [ ]
|
||||
# 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"
|
@ -1,42 +0,0 @@
|
||||
|
||||
# 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 = [ ]
|
||||
# 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"
|
@ -1,44 +0,0 @@
|
||||
|
||||
# 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"
|
@ -18,7 +18,24 @@ http {
|
||||
|
||||
location = /.well-known/host-meta.json {
|
||||
default_type application/json;
|
||||
return 200 '{"links":[{"rel":"urn:xmpp:alt-connections:s2s-websocket","href":"wss://xp1.example.org:5281/xmpp-websocket"}, {"rel":"urn:xmpp:alt-connections:websocket","href":"wss://xp1.example.org:5281/xmpp-websocket"}]}';
|
||||
return 200 '{
|
||||
"links": [
|
||||
{
|
||||
"rel": "urn:xmpp:alt-connections:s2s-websocket",
|
||||
"href": "wss://xp1.example.org:5281/xmpp-websocket",
|
||||
"ips": [
|
||||
"192.5.0.40"
|
||||
],
|
||||
"priority": 15,
|
||||
"weight": 50,
|
||||
"sni": "xp1.example.org"
|
||||
},
|
||||
{
|
||||
"rel": "urn:xmpp:alt-connections:websocket",
|
||||
"href": "wss://xp1.example.org:5281/xmpp-websocket"
|
||||
}
|
||||
]
|
||||
}';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,24 @@ http {
|
||||
|
||||
location = /.well-known/host-meta.json {
|
||||
default_type application/json;
|
||||
return 200 '{"links":[{"rel":"urn:xmpp:alt-connections:s2s-websocket","href":"wss://xp2.example.org:5281/xmpp-websocket"}, {"rel":"urn:xmpp:alt-connections:websocket","href":"wss://xp2.example.org:5281/xmpp-websocket"}]}';
|
||||
return 200 '{
|
||||
"links": [
|
||||
{
|
||||
"rel": "urn:xmpp:alt-connections:s2s-websocket",
|
||||
"href": "wss://xp2.example.org:5281/xmpp-websocket",
|
||||
"ips": [
|
||||
"192.5.0.50"
|
||||
],
|
||||
"priority": 15,
|
||||
"weight": 50,
|
||||
"sni": "xp2.example.org"
|
||||
},
|
||||
{
|
||||
"rel": "urn:xmpp:alt-connections:websocket",
|
||||
"href": "wss://xp2.example.org:5281/xmpp-websocket"
|
||||
}
|
||||
]
|
||||
}';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,19 +39,19 @@ RUN pacman -S --noconfirm --disable-download-timeout --needed bind nginx prosody
|
||||
pacman -U --noconfirm --needed /tmp/*.pkg.tar* && rm -f /tmp/*.pkg.tar* && \
|
||||
mkdir -p /opt/xmpp-proxy/prosody-modules/ /opt/prosody-modules/ /scansion && mkcert -install && \
|
||||
mkdir -p /etc/certs/ecdsa && cd /etc/certs/ecdsa && \
|
||||
mkcert -ecdsa -cert-file one.example.org.crt -key-file one.example.org.key one.example.org && \
|
||||
mkcert -ecdsa -cert-file two.example.org.crt -key-file two.example.org.key two.example.org && \
|
||||
mkcert -ecdsa -cert-file xp1.example.org.crt -key-file xp1.example.org.key xp1.example.org && \
|
||||
mkcert -ecdsa -cert-file xp2.example.org.crt -key-file xp2.example.org.key xp2.example.org && \
|
||||
mkcert -ecdsa -cert-file wildcard.crt -key-file wildcard.key '*.example.org' && \
|
||||
mkcert -ecdsa -client -cert-file one.example.org.crt -key-file one.example.org.key one.example.org && \
|
||||
mkcert -ecdsa -client -cert-file two.example.org.crt -key-file two.example.org.key two.example.org && \
|
||||
mkcert -ecdsa -client -cert-file xp1.example.org.crt -key-file xp1.example.org.key xp1.example.org && \
|
||||
mkcert -ecdsa -client -cert-file xp2.example.org.crt -key-file xp2.example.org.key xp2.example.org && \
|
||||
mkcert -ecdsa -client -cert-file wildcard.crt -key-file wildcard.key '*.example.org' && \
|
||||
cp wildcard.crt legacy_ssl.crt && cp wildcard.key legacy_ssl.key && \
|
||||
cp wildcard.crt https.crt && cp wildcard.key https.key && \
|
||||
mkdir -p /etc/certs/rsa && cd /etc/certs/rsa && \
|
||||
mkcert -cert-file one.example.org.crt -key-file one.example.org.key one.example.org && \
|
||||
mkcert -cert-file two.example.org.crt -key-file two.example.org.key two.example.org && \
|
||||
mkcert -cert-file xp1.example.org.crt -key-file xp1.example.org.key xp1.example.org && \
|
||||
mkcert -cert-file xp2.example.org.crt -key-file xp2.example.org.key xp2.example.org && \
|
||||
mkcert -cert-file wildcard.crt -key-file wildcard.key '*.example.org' && \
|
||||
mkcert -client -cert-file one.example.org.crt -key-file one.example.org.key one.example.org && \
|
||||
mkcert -client -cert-file two.example.org.crt -key-file two.example.org.key two.example.org && \
|
||||
mkcert -client -cert-file xp1.example.org.crt -key-file xp1.example.org.key xp1.example.org && \
|
||||
mkcert -client -cert-file xp2.example.org.crt -key-file xp2.example.org.key xp2.example.org && \
|
||||
mkcert -client -cert-file wildcard.crt -key-file wildcard.key '*.example.org' && \
|
||||
cp wildcard.crt legacy_ssl.crt && cp wildcard.key legacy_ssl.key && \
|
||||
cp wildcard.crt https.crt && cp wildcard.key https.key && \
|
||||
chmod -R 777 /etc/certs/ && rm -rf /etc/prosody/certs && ln -sf /etc/certs/rsa /etc/prosody/certs
|
||||
|
538
src/srv.rs
538
src/srv.rs
@ -1,7 +1,8 @@
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::convert::TryFrom;
|
||||
use std::net::SocketAddr;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
|
||||
use data_encoding::BASE64;
|
||||
use ring::digest::{Algorithm, Context as DigestContext, SHA256, SHA512};
|
||||
@ -27,24 +28,128 @@ fn make_resolver() -> TokioAsyncResolver {
|
||||
TokioAsyncResolver::tokio(config, options).unwrap()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum XmppConnectionType {
|
||||
StartTLS,
|
||||
DirectTLS,
|
||||
#[cfg(feature = "quic")]
|
||||
QUIC,
|
||||
#[cfg(feature = "websocket")]
|
||||
WebSocket(Uri, String, bool),
|
||||
WebSocket(Uri, String),
|
||||
}
|
||||
|
||||
impl XmppConnectionType {
|
||||
fn idx(&self) -> u8 {
|
||||
match self {
|
||||
XmppConnectionType::QUIC => 0,
|
||||
XmppConnectionType::DirectTLS => 1,
|
||||
XmppConnectionType::StartTLS => 2,
|
||||
XmppConnectionType::WebSocket(_, _) => 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for XmppConnectionType {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
let cmp = self.idx().cmp(&other.idx());
|
||||
if cmp != Ordering::Equal {
|
||||
return cmp;
|
||||
}
|
||||
// so they are the same type, but WebSocket is a special case...
|
||||
match (self, other) {
|
||||
(XmppConnectionType::WebSocket(self_uri, self_origin), XmppConnectionType::WebSocket(other_uri, other_origin)) => {
|
||||
let cmp = self_uri.to_string().cmp(&other_uri.to_string());
|
||||
if cmp != Ordering::Equal {
|
||||
return cmp;
|
||||
}
|
||||
self_origin.cmp(other_origin)
|
||||
}
|
||||
(_, _) => Ordering::Equal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for XmppConnectionType {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct XmppConnection {
|
||||
conn_type: XmppConnectionType,
|
||||
priority: u16,
|
||||
#[allow(dead_code)]
|
||||
weight: u16, // todo: use weight
|
||||
port: u16,
|
||||
target: String,
|
||||
secure: bool,
|
||||
ips: Vec<IpAddr>,
|
||||
#[allow(dead_code)]
|
||||
ech: Option<String>,
|
||||
}
|
||||
|
||||
impl PartialEq for XmppConnection {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.conn_type == other.conn_type && self.port == other.port && self.target == other.target
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for XmppConnection {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
// this should put equal things next to each other, but things we want to keep further to the left
|
||||
let cmp = self.conn_type.cmp(&other.conn_type);
|
||||
if cmp != Ordering::Equal {
|
||||
return cmp;
|
||||
}
|
||||
let cmp = self.port.cmp(&other.port);
|
||||
if cmp != Ordering::Equal {
|
||||
return cmp;
|
||||
}
|
||||
let cmp = self.target.cmp(&other.target);
|
||||
if cmp != Ordering::Equal {
|
||||
return cmp;
|
||||
}
|
||||
// end of equality checks, now preferences:
|
||||
// backwards on purpose, so secure is earlier in the list
|
||||
let cmp = other.secure.cmp(&self.secure);
|
||||
if cmp != Ordering::Equal {
|
||||
return cmp;
|
||||
}
|
||||
// lowest priority preferred
|
||||
let cmp = self.priority.cmp(&other.priority);
|
||||
if cmp != Ordering::Equal {
|
||||
return cmp;
|
||||
}
|
||||
// highest weight preferred
|
||||
other.weight.cmp(&self.priority)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for XmppConnection {}
|
||||
|
||||
impl PartialOrd for XmppConnection {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
fn sort_dedup(ret: &mut Vec<XmppConnection>) {
|
||||
ret.sort();
|
||||
ret.dedup();
|
||||
// now sort by priority
|
||||
ret.sort_by(|a, b| {
|
||||
let cmp = a.priority.cmp(&b.priority);
|
||||
if cmp != Ordering::Equal {
|
||||
return cmp;
|
||||
}
|
||||
// prioritize "better" protocols todo: we *could* prioritize these first before priority...
|
||||
let cmp = a.conn_type.idx().cmp(&b.conn_type.idx());
|
||||
if cmp != Ordering::Equal {
|
||||
return cmp;
|
||||
}
|
||||
// higher weight first todo: still not ideal
|
||||
b.weight.cmp(&a.weight)
|
||||
});
|
||||
}
|
||||
|
||||
impl XmppConnection {
|
||||
@ -57,12 +162,17 @@ impl XmppConnection {
|
||||
config: OutgoingVerifierConfig,
|
||||
) -> Result<(StanzaWrite, StanzaRead, SocketAddr, &'static str)> {
|
||||
debug!("{} attempting connection to SRV: {:?}", client_addr.log_from(), self);
|
||||
// todo: need to set options to Ipv4AndIpv6
|
||||
let ips = RESOLVER.lookup_ip(self.target.clone()).await?;
|
||||
// todo: for DNSSEC we need to optionally allow target in addition to domain, but what for SNI
|
||||
let domain = if self.secure { &self.target } else { domain };
|
||||
//let ips = RESOLVER.lookup_ip(self.target.clone()).await?;
|
||||
let ips = if self.ips.is_empty() {
|
||||
RESOLVER.lookup_ip(self.target.clone()).await?.iter().collect()
|
||||
} else {
|
||||
self.ips.clone() // todo: avoid clone?
|
||||
};
|
||||
for ip in ips.iter() {
|
||||
let to_addr = SocketAddr::new(ip, self.port);
|
||||
let to_addr = SocketAddr::new(*ip, self.port);
|
||||
debug!("{} trying ip {}", client_addr.log_from(), to_addr);
|
||||
// todo: for DNSSEC we need to optionally allow target in addition to domain, but what for SNI
|
||||
match self.conn_type {
|
||||
XmppConnectionType::StartTLS => match crate::starttls_connect(to_addr, domain, stream_open, in_filter, config.clone()).await {
|
||||
Ok((wr, rd)) => return Ok((wr, rd, to_addr, "starttls-out")),
|
||||
@ -79,12 +189,10 @@ impl XmppConnection {
|
||||
},
|
||||
#[cfg(feature = "websocket")]
|
||||
// todo: when websocket is found via DNS, we need to validate cert against domain, *not* target, this is a security problem with XEP-0156, we are doing it the secure but likely unexpected way here for now
|
||||
XmppConnectionType::WebSocket(ref url, ref origin, ref secure) => {
|
||||
match crate::websocket_connect(to_addr, if *secure { &self.target } else { domain }, url, origin, config.clone()).await {
|
||||
Ok((wr, rd)) => return Ok((wr, rd, to_addr, "websocket-out")),
|
||||
Err(e) => error!("websocket connection failed to IP {} from TXT {}, error: {}", to_addr, url, e),
|
||||
}
|
||||
}
|
||||
XmppConnectionType::WebSocket(ref url, ref origin) => match crate::websocket_connect(to_addr, domain, url, origin, config.clone()).await {
|
||||
Ok((wr, rd)) => return Ok((wr, rd, to_addr, "websocket-out")),
|
||||
Err(e) => error!("websocket connection failed to IP {} from TXT {}, error: {}", to_addr, url, e),
|
||||
},
|
||||
}
|
||||
}
|
||||
bail!("cannot connect to any IPs for SRV: {}", self.target)
|
||||
@ -101,6 +209,9 @@ fn collect_srvs(ret: &mut Vec<XmppConnection>, srv_records: std::result::Result<
|
||||
weight: srv.weight(),
|
||||
port: srv.port(),
|
||||
target: srv.target().to_ascii(),
|
||||
secure: false, // todo: support dnssec here, and if true, look up TLSA
|
||||
ips: Vec::new(),
|
||||
ech: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -135,16 +246,19 @@ fn wss_to_srv(url: &str, secure: bool) -> Option<XmppConnection> {
|
||||
443
|
||||
};
|
||||
Some(XmppConnection {
|
||||
conn_type: XmppConnectionType::WebSocket(url, origin, secure),
|
||||
conn_type: XmppConnectionType::WebSocket(url, origin),
|
||||
priority: u16::MAX,
|
||||
weight: 0,
|
||||
port,
|
||||
target,
|
||||
secure,
|
||||
ips: Vec::new(),
|
||||
ech: None,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "websocket")]
|
||||
fn collect_txts(ret: &mut Vec<XmppConnection>, secure_urls: Vec<String>, txt_records: std::result::Result<TxtLookup, ResolveError>, is_c2s: bool) {
|
||||
fn collect_txts(ret: &mut Vec<XmppConnection>, txt_records: std::result::Result<TxtLookup, ResolveError>, is_c2s: bool) {
|
||||
if let Ok(txt_records) = txt_records {
|
||||
for txt in txt_records.iter() {
|
||||
for txt in txt.iter() {
|
||||
@ -152,8 +266,8 @@ fn collect_txts(ret: &mut Vec<XmppConnection>, secure_urls: Vec<String>, txt_rec
|
||||
if txt.starts_with(if is_c2s { b"_xmpp-client-websocket=wss://" } else { b"_xmpp-server-websocket=wss://" }) {
|
||||
// 23 is the length of "_xmpp-client-websocket=" and "_xmpp-server-websocket="
|
||||
if let Ok(url) = String::from_utf8(txt[23..].to_vec()) {
|
||||
if !secure_urls.contains(&url) {
|
||||
if let Some(srv) = wss_to_srv(&url, false) {
|
||||
if let Some(srv) = wss_to_srv(&url, false) {
|
||||
if !ret.contains(&srv) {
|
||||
ret.push(srv);
|
||||
}
|
||||
}
|
||||
@ -168,16 +282,11 @@ fn collect_txts(ret: &mut Vec<XmppConnection>, secure_urls: Vec<String>, txt_rec
|
||||
|
||||
pub async fn get_xmpp_connections(domain: &str, is_c2s: bool) -> Result<(Vec<XmppConnection>, XmppServerCertVerifier)> {
|
||||
let mut valid_tls_cert_server_names: Vec<DnsName> = vec![DnsNameRef::try_from_ascii_str(domain)?.to_owned()];
|
||||
let (starttls, direct_tls, quic, websocket_txt, websocket_rel) = if is_c2s {
|
||||
("_xmpp-client._tcp", "_xmpps-client._tcp", "_xmppq-client._udp", "_xmppconnect", "urn:xmpp:alt-connections:websocket")
|
||||
let mut sha256_pinnedpubkeys = Vec::new();
|
||||
let (starttls, direct_tls, quic, websocket_txt) = if is_c2s {
|
||||
("_xmpp-client._tcp", "_xmpps-client._tcp", "_xmppq-client._udp", "_xmppconnect")
|
||||
} else {
|
||||
(
|
||||
"_xmpp-server._tcp",
|
||||
"_xmpps-server._tcp",
|
||||
"_xmppq-server._udp",
|
||||
"_xmppconnect-server",
|
||||
"urn:xmpp:alt-connections:s2s-websocket",
|
||||
)
|
||||
("_xmpp-server._tcp", "_xmpps-server._tcp", "_xmppq-server._udp", "_xmppconnect-server")
|
||||
};
|
||||
|
||||
let starttls = format!("{}.{}.", starttls, domain).into_name()?;
|
||||
@ -187,6 +296,8 @@ pub async fn get_xmpp_connections(domain: &str, is_c2s: bool) -> Result<(Vec<Xmp
|
||||
#[cfg(feature = "websocket")]
|
||||
let websocket_txt = format!("{}.{}.", websocket_txt, domain).into_name()?;
|
||||
|
||||
let mut ret = Vec::new();
|
||||
|
||||
// this lets them run concurrently but not in parallel, could spawn parallel tasks but... worth it ?
|
||||
// todo: don't look up websocket or quic records when they are disabled
|
||||
let (
|
||||
@ -205,46 +316,34 @@ pub async fn get_xmpp_connections(domain: &str, is_c2s: bool) -> Result<(Vec<Xmp
|
||||
RESOLVER.srv_lookup(quic),
|
||||
//#[cfg(feature = "websocket")]
|
||||
RESOLVER.txt_lookup(websocket_txt),
|
||||
collect_host_meta(domain, websocket_rel),
|
||||
collect_host_meta(&mut ret, &mut sha256_pinnedpubkeys, domain, is_c2s),
|
||||
collect_posh(domain),
|
||||
);
|
||||
|
||||
let mut ret = Vec::new();
|
||||
collect_srvs(&mut ret, starttls, XmppConnectionType::StartTLS);
|
||||
collect_srvs(&mut ret, direct_tls, XmppConnectionType::DirectTLS);
|
||||
#[cfg(feature = "quic")]
|
||||
collect_srvs(&mut ret, quic, XmppConnectionType::QUIC);
|
||||
#[cfg(feature = "websocket")]
|
||||
{
|
||||
let urls = websocket_host.unwrap_or_default();
|
||||
for url in &urls {
|
||||
if let Some(url) = wss_to_srv(url, true) {
|
||||
ret.push(url);
|
||||
}
|
||||
}
|
||||
collect_txts(&mut ret, urls, websocket_txt, is_c2s);
|
||||
if let Ok(Some(_ttl)) = websocket_host {
|
||||
// todo: cache for ttl
|
||||
} else {
|
||||
// ignore everything else if new host-meta format
|
||||
#[cfg(feature = "websocket")]
|
||||
collect_txts(&mut ret, websocket_txt, is_c2s);
|
||||
collect_srvs(&mut ret, starttls, XmppConnectionType::StartTLS);
|
||||
collect_srvs(&mut ret, direct_tls, XmppConnectionType::DirectTLS);
|
||||
#[cfg(feature = "quic")]
|
||||
collect_srvs(&mut ret, quic, XmppConnectionType::QUIC);
|
||||
}
|
||||
ret.sort_by(|a, b| a.priority.cmp(&b.priority));
|
||||
// todo: do something with weight
|
||||
|
||||
#[allow(clippy::single_match)]
|
||||
sort_dedup(&mut ret);
|
||||
|
||||
for srv in &ret {
|
||||
match srv.conn_type {
|
||||
#[cfg(feature = "websocket")]
|
||||
XmppConnectionType::WebSocket(_, _, ref secure) => {
|
||||
if *secure {
|
||||
if let Ok(target) = DnsNameRef::try_from_ascii_str(srv.target.as_str()) {
|
||||
let target = target.to_owned();
|
||||
if !valid_tls_cert_server_names.contains(&target) {
|
||||
valid_tls_cert_server_names.push(target);
|
||||
}
|
||||
}
|
||||
if srv.secure {
|
||||
if let Ok(target) = DnsNameRef::try_from_ascii_str(srv.target.as_str()) {
|
||||
let target = target.to_owned();
|
||||
if !valid_tls_cert_server_names.contains(&target) {
|
||||
valid_tls_cert_server_names.push(target);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let cert_verifier = XmppServerCertVerifier::new(valid_tls_cert_server_names, posh.ok());
|
||||
let cert_verifier = XmppServerCertVerifier::new(valid_tls_cert_server_names, posh.ok(), sha256_pinnedpubkeys);
|
||||
|
||||
if ret.is_empty() {
|
||||
// default starttls ports
|
||||
@ -254,6 +353,9 @@ pub async fn get_xmpp_connections(domain: &str, is_c2s: bool) -> Result<(Vec<Xmp
|
||||
target: domain.to_string(),
|
||||
conn_type: XmppConnectionType::StartTLS,
|
||||
port: if is_c2s { 5222 } else { 5269 },
|
||||
secure: false,
|
||||
ips: Vec::new(),
|
||||
ech: None,
|
||||
});
|
||||
// by spec there are no default direct/quic ports, but we are going 443
|
||||
ret.push(XmppConnection {
|
||||
@ -262,6 +364,9 @@ pub async fn get_xmpp_connections(domain: &str, is_c2s: bool) -> Result<(Vec<Xmp
|
||||
target: domain.to_string(),
|
||||
conn_type: XmppConnectionType::DirectTLS,
|
||||
port: 443,
|
||||
secure: false,
|
||||
ips: Vec::new(),
|
||||
ech: None,
|
||||
});
|
||||
#[cfg(feature = "quic")]
|
||||
ret.push(XmppConnection {
|
||||
@ -270,6 +375,9 @@ pub async fn get_xmpp_connections(domain: &str, is_c2s: bool) -> Result<(Vec<Xmp
|
||||
target: domain.to_string(),
|
||||
conn_type: XmppConnectionType::QUIC,
|
||||
port: 443,
|
||||
secure: false,
|
||||
ips: Vec::new(),
|
||||
ech: None,
|
||||
});
|
||||
}
|
||||
|
||||
@ -328,40 +436,171 @@ pub async fn srv_connect(
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "websocket"))]
|
||||
async fn collect_host_meta(domain: &str, rel: &str) -> Result<Vec<String>> {
|
||||
bail!("websocket disabled")
|
||||
async fn collect_host_meta(ret: &mut Vec<XmppConnection>, sha256_pinnedpubkeys: &mut Vec<String>, domain: &str, is_c2s: bool) -> Result<Option<u16>> {
|
||||
collect_host_meta_json(ret, sha256_pinnedpubkeys, domain, is_c2s)
|
||||
}
|
||||
|
||||
#[cfg(feature = "websocket")]
|
||||
async fn collect_host_meta(domain: &str, rel: &str) -> Result<Vec<String>> {
|
||||
match tokio::join!(collect_host_meta_xml(domain, rel), collect_host_meta_json(domain, rel)) {
|
||||
(Ok(mut xml), Ok(json)) => {
|
||||
combine_uniq(&mut xml, json);
|
||||
Ok(xml)
|
||||
async fn collect_host_meta(ret: &mut Vec<XmppConnection>, sha256_pinnedpubkeys: &mut Vec<String>, domain: &str, is_c2s: bool) -> Result<Option<u16>> {
|
||||
let mut xml = Vec::new();
|
||||
match tokio::join!(collect_host_meta_json(ret, sha256_pinnedpubkeys, domain, is_c2s), collect_host_meta_xml(&mut xml, domain, is_c2s)) {
|
||||
(Ok(Some(ttl)), _) => Ok(Some(ttl)), // if ttl is returned, ignore host-meta.xml
|
||||
(_, Ok(_)) => {
|
||||
ret.append(&mut xml);
|
||||
Ok(None)
|
||||
}
|
||||
(_, Ok(json)) => Ok(json),
|
||||
(xml, _) => xml,
|
||||
(json, _) => json,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "websocket")]
|
||||
async fn collect_host_meta_json(domain: &str, rel: &str) -> Result<Vec<String>> {
|
||||
#[derive(Deserialize)]
|
||||
struct HostMeta {
|
||||
links: Vec<Link>,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
struct Link {
|
||||
rel: String,
|
||||
href: String,
|
||||
}
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
struct HostMeta {
|
||||
xmpp: Option<HostMetaXmpp>,
|
||||
links: Vec<Link>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
struct HostMetaXmpp {
|
||||
ttl: u16,
|
||||
#[serde(default)]
|
||||
public_key_pins_sha_256: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(tag = "rel", rename_all = "kebab-case")]
|
||||
enum Link {
|
||||
#[serde(rename = "urn:xmpp:alt-connections:websocket")]
|
||||
WebSocket {
|
||||
href: String,
|
||||
#[serde(flatten)]
|
||||
link: Option<LinkCommon>,
|
||||
},
|
||||
#[serde(rename = "urn:xmpp:alt-connections:tls")]
|
||||
DirectTLS {
|
||||
#[serde(flatten)]
|
||||
link: LinkCommon,
|
||||
port: u16,
|
||||
},
|
||||
#[serde(rename = "urn:xmpp:alt-connections:quic")]
|
||||
Quic {
|
||||
#[serde(flatten)]
|
||||
link: LinkCommon,
|
||||
port: u16,
|
||||
},
|
||||
#[serde(rename = "urn:xmpp:alt-connections:s2s-websocket")]
|
||||
S2SWebSocket {
|
||||
href: String,
|
||||
#[serde(flatten)]
|
||||
link: LinkCommon,
|
||||
},
|
||||
#[serde(rename = "urn:xmpp:alt-connections:s2s-tls")]
|
||||
S2SDirectTLS {
|
||||
#[serde(flatten)]
|
||||
link: LinkCommon,
|
||||
port: u16,
|
||||
},
|
||||
#[serde(rename = "urn:xmpp:alt-connections:s2s-quic")]
|
||||
S2SQuic {
|
||||
#[serde(flatten)]
|
||||
link: LinkCommon,
|
||||
port: u16,
|
||||
},
|
||||
#[serde(other)]
|
||||
Unknown,
|
||||
}
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
struct LinkCommon {
|
||||
ips: Vec<IpAddr>,
|
||||
priority: u16,
|
||||
weight: u16,
|
||||
sni: String,
|
||||
ech: Option<String>,
|
||||
}
|
||||
|
||||
impl LinkCommon {
|
||||
pub fn into_xmpp_connection(self, conn_type: XmppConnectionType, port: u16) -> Option<XmppConnection> {
|
||||
if self.ips.is_empty() {
|
||||
error!("invalid empty ips");
|
||||
return None;
|
||||
}
|
||||
Some(XmppConnection {
|
||||
conn_type,
|
||||
port,
|
||||
priority: self.priority,
|
||||
weight: self.weight,
|
||||
target: self.sni,
|
||||
ips: self.ips,
|
||||
ech: self.ech,
|
||||
secure: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Link {
|
||||
pub fn into_xmpp_connection(self, is_c2s: bool) -> Option<XmppConnection> {
|
||||
use XmppConnectionType::*;
|
||||
let (srv_is_c2s, port, link, conn_type) = match self {
|
||||
Link::DirectTLS { port, link } => (true, port, link, DirectTLS),
|
||||
Link::Quic { port, link } => (true, port, link, QUIC),
|
||||
Link::S2SDirectTLS { port, link } => (false, port, link, DirectTLS),
|
||||
Link::S2SQuic { port, link } => (false, port, link, QUIC),
|
||||
Link::WebSocket { href, link } => {
|
||||
return if is_c2s {
|
||||
let srv = wss_to_srv(&href, true)?;
|
||||
if let Some(link) = link {
|
||||
link.into_xmpp_connection(srv.conn_type, srv.port)
|
||||
} else {
|
||||
Some(srv)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
Link::S2SWebSocket { href, link } => {
|
||||
return if !is_c2s {
|
||||
let srv = wss_to_srv(&href, true)?;
|
||||
link.into_xmpp_connection(srv.conn_type, srv.port)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
|
||||
Link::Unknown => return None,
|
||||
};
|
||||
|
||||
if srv_is_c2s == is_c2s {
|
||||
link.into_xmpp_connection(conn_type, port)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HostMeta {
|
||||
pub fn collect(self, ret: &mut Vec<XmppConnection>, sha256_pinnedpubkeys: &mut Vec<String>, is_c2s: bool) -> Option<u16> {
|
||||
for link in self.links {
|
||||
if let Some(srv) = link.into_xmpp_connection(is_c2s) {
|
||||
ret.push(srv);
|
||||
}
|
||||
}
|
||||
if let Some(xmpp) = self.xmpp {
|
||||
sha256_pinnedpubkeys.extend(xmpp.public_key_pins_sha_256);
|
||||
Some(xmpp.ttl)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn collect_host_meta_json(ret: &mut Vec<XmppConnection>, sha256_pinnedpubkeys: &mut Vec<String>, domain: &str, is_c2s: bool) -> Result<Option<u16>> {
|
||||
let url = format!("https://{}/.well-known/host-meta.json", domain);
|
||||
let resp = https_get(&url).await?;
|
||||
if resp.status().is_success() {
|
||||
let resp = resp.json::<HostMeta>().await?;
|
||||
// we will only support wss:// (TLS) not ws:// (plain text)
|
||||
Ok(resp.links.iter().filter(|l| l.rel == rel && l.href.starts_with("wss://")).map(|l| l.href.clone()).collect())
|
||||
Ok(resp.collect(ret, sha256_pinnedpubkeys, is_c2s))
|
||||
} else {
|
||||
bail!("failed with status code {} for url {}", resp.status(), url)
|
||||
}
|
||||
@ -396,11 +635,21 @@ async fn parse_host_meta_xml(rel: &str, bytes: &[u8]) -> Result<Vec<String>> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "websocket")]
|
||||
async fn collect_host_meta_xml(domain: &str, rel: &str) -> Result<Vec<String>> {
|
||||
async fn collect_host_meta_xml(ret: &mut Vec<XmppConnection>, domain: &str, is_c2s: bool) -> Result<()> {
|
||||
if !is_c2s {
|
||||
bail!("host-meta XML unsupported for S2s");
|
||||
}
|
||||
let url = format!("https://{}/.well-known/host-meta", domain);
|
||||
let resp = https_get(&url).await?;
|
||||
if resp.status().is_success() {
|
||||
parse_host_meta_xml(rel, resp.bytes().await?.as_ref()).await
|
||||
let rel = "urn:xmpp:alt-connections:websocket";
|
||||
let hosts = parse_host_meta_xml(rel, resp.bytes().await?.as_ref()).await?;
|
||||
for host in hosts {
|
||||
if let Some(srv) = wss_to_srv(&host, true) {
|
||||
ret.push(srv);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
bail!("failed with status code {} for url {}", resp.status(), url)
|
||||
}
|
||||
@ -515,7 +764,7 @@ impl Posh {
|
||||
}
|
||||
}
|
||||
|
||||
fn digest(algorithm: &'static Algorithm, buf: &[u8]) -> String {
|
||||
pub fn digest(algorithm: &'static Algorithm, buf: &[u8]) -> String {
|
||||
let mut context = DigestContext::new(algorithm);
|
||||
context.update(buf);
|
||||
let digest = context.finish();
|
||||
@ -525,6 +774,7 @@ fn digest(algorithm: &'static Algorithm, buf: &[u8]) -> String {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::srv::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn valid_posh(posh: &[u8], cert: &[u8]) -> bool {
|
||||
let posh: PoshJson = serde_json::from_slice(posh).unwrap();
|
||||
@ -539,6 +789,17 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn read_file(file: &str) -> Result<Vec<u8>> {
|
||||
let mut f = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
f.push(file);
|
||||
let mut file = File::open(f)?;
|
||||
|
||||
let mut data = Vec::new();
|
||||
file.read_to_end(&mut data)?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn posh_deserialize() {
|
||||
assert!(valid_posh(
|
||||
@ -611,13 +872,14 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "websocket")]
|
||||
//#[tokio::test]
|
||||
async fn http() -> Result<()> {
|
||||
let hosts = collect_host_meta_json("burtrum.org", "urn:xmpp:alt-connections:websocket").await?;
|
||||
println!("{:?}", hosts);
|
||||
let hosts = collect_host_meta_xml("burtrum.org", "urn:xmpp:alt-connections:websocket").await?;
|
||||
println!("{:?}", hosts);
|
||||
let mut hosts = Vec::new();
|
||||
let mut sha256_pinnedpubkeys = Vec::new();
|
||||
let res = collect_host_meta(&mut hosts, &mut sha256_pinnedpubkeys, "burtrum.org", true).await;
|
||||
println!("burtrum.org res: {:?}", res);
|
||||
println!("burtrum.org hosts: {:?}", hosts);
|
||||
println!("burtrum.org hosts: {:?}", sha256_pinnedpubkeys);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -642,6 +904,106 @@ mod tests {
|
||||
let xrd = br#"<xrd xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><link rel="urn:xmpp:alt-connections:xbosh" href="https://burtrum.org/http-bind"/><link rel="urn:xmpp:alt-connections:websocket" href="wss://burtrum.org/xmpp-websocket"/><link rel="urn:xmpp:alt-connections:s2s-websocket" href="wss://burtrum.org/xmpp-websocket-s2s"/></xrd>"#;
|
||||
assert_eq!(parse_host_meta_xml("urn:xmpp:alt-connections:websocket", xrd).await?, vec!["wss://burtrum.org/xmpp-websocket"]);
|
||||
|
||||
let xrd = read_file("contrib/host-meta/xep-0156-current.xml")?;
|
||||
assert_eq!(parse_host_meta_xml("urn:xmpp:alt-connections:websocket", &xrd).await?, vec!["wss://example.org/xmpp-websocket"]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "websocket")]
|
||||
#[tokio::test]
|
||||
async fn test_parse_host_meta_json() -> Result<()> {
|
||||
let xrd = read_file("contrib/host-meta/xep-0156-minimal.json")?;
|
||||
let host_meta: HostMeta = serde_json::from_slice(&xrd)?;
|
||||
println!("host_meta: {:?}", host_meta);
|
||||
//assert_eq!(host_meta.links("urn:xmpp:alt-connections:websocket"), vec!["wss://example.org/xmpp-websocket"]);
|
||||
|
||||
let xrd = read_file("contrib/host-meta/xep-0156-current.json")?;
|
||||
let host_meta: HostMeta = serde_json::from_slice(&xrd)?;
|
||||
println!("host_meta: {:?}", host_meta);
|
||||
//assert_eq!(host_meta.links("urn:xmpp:alt-connections:websocket"), vec!["wss://example.org/xmpp-websocket"]);
|
||||
|
||||
let xrd = read_file("contrib/host-meta/xep-0156-proposed.json")?;
|
||||
let host_meta: HostMeta = serde_json::from_slice(&xrd)?;
|
||||
println!("host_meta: {:?}", host_meta);
|
||||
//assert_eq!(host_meta.links("urn:xmpp:alt-connections:websocket"), vec!["wss://example.org/xmpp-websocket"]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dedup() {
|
||||
let domain = "example.org";
|
||||
let mut ret = Vec::new();
|
||||
ret.push(XmppConnection {
|
||||
priority: 10,
|
||||
weight: 0,
|
||||
target: domain.to_string(),
|
||||
conn_type: XmppConnectionType::DirectTLS,
|
||||
port: 443,
|
||||
secure: false,
|
||||
ips: Vec::new(),
|
||||
ech: None,
|
||||
});
|
||||
ret.push(XmppConnection {
|
||||
priority: 0,
|
||||
weight: 0,
|
||||
target: domain.to_string(),
|
||||
conn_type: XmppConnectionType::StartTLS,
|
||||
port: 5222,
|
||||
secure: false,
|
||||
ips: Vec::new(),
|
||||
ech: None,
|
||||
});
|
||||
ret.push(XmppConnection {
|
||||
priority: 15,
|
||||
weight: 0,
|
||||
target: domain.to_string(),
|
||||
conn_type: XmppConnectionType::DirectTLS,
|
||||
port: 443,
|
||||
secure: true,
|
||||
ips: Vec::new(),
|
||||
ech: None,
|
||||
});
|
||||
ret.push(XmppConnection {
|
||||
priority: 10,
|
||||
weight: 0,
|
||||
target: domain.to_string(),
|
||||
conn_type: XmppConnectionType::DirectTLS,
|
||||
port: 443,
|
||||
secure: true,
|
||||
ips: Vec::new(),
|
||||
ech: None,
|
||||
});
|
||||
ret.push(XmppConnection {
|
||||
priority: 10,
|
||||
weight: 50,
|
||||
target: domain.to_string(),
|
||||
conn_type: XmppConnectionType::DirectTLS,
|
||||
port: 443,
|
||||
secure: true,
|
||||
ips: Vec::new(),
|
||||
ech: None,
|
||||
});
|
||||
ret.push(XmppConnection {
|
||||
priority: 10,
|
||||
weight: 100,
|
||||
target: "example.com".to_string(),
|
||||
conn_type: XmppConnectionType::DirectTLS,
|
||||
port: 443,
|
||||
secure: true,
|
||||
ips: Vec::new(),
|
||||
ech: None,
|
||||
});
|
||||
ret.push(XmppConnection {
|
||||
priority: 0,
|
||||
weight: 100,
|
||||
target: "example.com".to_string(),
|
||||
conn_type: XmppConnectionType::DirectTLS,
|
||||
port: 443,
|
||||
secure: true,
|
||||
ips: Vec::new(),
|
||||
ech: None,
|
||||
});
|
||||
sort_dedup(&mut ret);
|
||||
println!("ret dedup: {:?}", ret);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::Posh;
|
||||
use crate::{digest, Posh};
|
||||
use log::debug;
|
||||
use ring::digest::SHA256;
|
||||
use rustls::client::{ServerCertVerified, ServerCertVerifier};
|
||||
use rustls::server::{ClientCertVerified, ClientCertVerifier};
|
||||
use rustls::{Certificate, DistinguishedNames, Error, ServerName};
|
||||
@ -73,14 +74,31 @@ fn prepare<'a, 'b>(end_entity: &'a Certificate, intermediates: &'a [Certificate]
|
||||
pub struct XmppServerCertVerifier {
|
||||
names: Vec<DnsName>,
|
||||
posh: Option<Posh>,
|
||||
sha256_pinnedpubkeys: Vec<String>,
|
||||
}
|
||||
|
||||
impl XmppServerCertVerifier {
|
||||
pub fn new(names: Vec<DnsName>, posh: Option<Posh>) -> Self {
|
||||
XmppServerCertVerifier { names, posh }
|
||||
pub fn new(names: Vec<DnsName>, posh: Option<Posh>, sha256_pinnedpubkeys: Vec<String>) -> Self {
|
||||
XmppServerCertVerifier { names, posh, sha256_pinnedpubkeys }
|
||||
}
|
||||
|
||||
pub fn verify_cert(&self, end_entity: &Certificate, intermediates: &[Certificate], now: SystemTime) -> Result<ServerCertVerified, Error> {
|
||||
if !self.sha256_pinnedpubkeys.is_empty() {
|
||||
let cert = webpki::TrustAnchor::try_from_cert_der(end_entity.0.as_ref()).map_err(pki_error)?;
|
||||
println!("spki.len(): {}", cert.spki.len());
|
||||
println!("spki: {:?}", cert.spki);
|
||||
// todo: what is wrong with webpki? it returns *almost* the right answer but missing these leading bytes:
|
||||
// guess I'll open an issue... (I assume this is some type of algorithm identifying header or something)
|
||||
let mut pubkey: Vec<u8> = vec![48, 130, 1, 34];
|
||||
pubkey.extend(cert.spki);
|
||||
|
||||
if self.sha256_pinnedpubkeys.contains(&digest(&SHA256, &pubkey)) {
|
||||
debug!("pinnedpubkey succeeded for {:?}", self.names.first());
|
||||
return Ok(ServerCertVerified::assertion());
|
||||
}
|
||||
// todo: else fail ????
|
||||
}
|
||||
|
||||
if let Some(ref posh) = self.posh {
|
||||
if posh.valid_cert(end_entity.as_ref()) {
|
||||
debug!("posh succeeded for {:?}", self.names.first());
|
||||
|
@ -74,7 +74,7 @@ pub fn to_ws_new(buf: &[u8], mut end_of_first_tag: usize, is_c2s: bool) -> Resul
|
||||
.replace("<stream:stream ", "<open ")
|
||||
.replace("jabber:server", "urn:ietf:params:xml:ns:xmpp-framing-server")
|
||||
.replace("jabber:client", "urn:ietf:params:xml:ns:xmpp-framing")
|
||||
.replace(">", "/>"));
|
||||
.replace('>', "/>"));
|
||||
}
|
||||
if buf.starts_with(b"</stream:stream") {
|
||||
return Ok(r#"<close xmlns="urn:ietf:params:xml:ns:xmpp-framing" />"#.to_string());
|
||||
|
Loading…
Reference in New Issue
Block a user