commit 3871589254b1a9afa424bfb54ca14ef4fd8b1ec3 Author: moparisthebest Date: Mon Apr 12 23:40:44 2021 -0400 Initial commit diff --git a/.ci/Jenkinsfile b/.ci/Jenkinsfile new file mode 100644 index 0000000..803a002 --- /dev/null +++ b/.ci/Jenkinsfile @@ -0,0 +1,46 @@ +properties( + [ + disableConcurrentBuilds() + ] +) + +node('linux && docker') { + try { + stage('Checkout') { + //branch name from Jenkins environment variables + echo "My branch is: ${env.BRANCH_NAME}" + + // this doesn't grab tags pointing to this branch + //checkout scm + // this hack does... https://issues.jenkins.io/browse/JENKINS-45164 + checkout([ + $class: 'GitSCM', + branches: [[name: 'refs/heads/'+env.BRANCH_NAME]], + extensions: [[$class: 'CloneOption', noTags: false, shallow: false, depth: 0, reference: '']], + userRemoteConfigs: scm.userRemoteConfigs, + ]) + sh ''' + set -euxo pipefail + git checkout "$BRANCH_NAME" -- + git reset --hard "origin/$BRANCH_NAME" + ''' + } + + stage('Build + Deploy') { + sh ''' + mkdir -p release + cp xmpp-proxy.toml release + curl --compressed -sL https://code.moparisthebest.com/moparisthebest/self-ci/raw/branch/master/build-ci.sh | bash + ''' + } + + currentBuild.result = 'SUCCESS' + } catch (Exception err) { + currentBuild.result = 'FAILURE' + } finally { + stage('Email') { + step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: 'admin.jenkins@moparisthebest.com', sendToIndividuals: true]) + } + deleteDir() + } +} diff --git a/.ci/build.sh b/.ci/build.sh new file mode 100755 index 0000000..1a36f47 --- /dev/null +++ b/.ci/build.sh @@ -0,0 +1,29 @@ +#!/bin/bash +set -exo pipefail + +echo "starting build for TARGET $TARGET" + +export CRATE_NAME=xmpp-proxy + +SUFFIX="" + +echo "$TARGET" | grep -E '^x86_64-pc-windows-gnu$' >/dev/null && SUFFIX=".exe" + +# ring fails to compile here +echo "$TARGET" | grep -E '^(s390x|powerpc|mips|riscv64gc|.*solaris$)' >/dev/null && echo "$TARGET not supported in rustls" && exit 0 +# mio fails to link here +echo "$TARGET" | grep -E '^x86_64-unknown-netbsd$' >/dev/null && echo "$TARGET not supported in mio" && exit 0 + +# build binary +cross build --target $TARGET --release + +# to check how they are built +file "target/$TARGET/release/${CRATE_NAME}$SUFFIX" + +# if this commit has a tag, upload artifact to release +strip "target/$TARGET/release/${CRATE_NAME}$SUFFIX" || true # if strip fails, it's fine +mkdir -p release +cp "target/$TARGET/release/${CRATE_NAME}$SUFFIX" "release/${CRATE_NAME}-$TARGET$SUFFIX" + +echo 'build success!' +exit 0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0264c41 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target/ +**/*.rs.bk +.idea +**/*.kate-swp \ No newline at end of file diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..ffc467d --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1 @@ +max_width = 200 diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..0d888af --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,543 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "anyhow" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bumpalo" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" + +[[package]] +name = "bytes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" + +[[package]] +name = "cc" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "die" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8634d5e6139f7364a4e99bd718b2f511f2f25863146360e70909bc45a016290" + +[[package]] +name = "futures" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94" + +[[package]] +name = "futures-executor" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59" + +[[package]] +name = "futures-macro" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3" + +[[package]] +name = "futures-task" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80" + +[[package]] +name = "futures-util" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "hermit-abi" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc15e39392125075f60c95ba416f5381ff6c3a948ff02ab12464715adf56c821" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" + +[[package]] +name = "log" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2" +dependencies = [ + "cfg-if 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "mio" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e50ae3f04d169fcc9bde0b547d1c205219b7157e07ded9c5aff03e0637cb3ed7" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" +dependencies = [ + "socket2", + "winapi", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" + +[[package]] +name = "pin-project-lite" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustls" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b" +dependencies = [ + "base64", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "sct" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552954ce79a059ddd5fd68c271592374bd15cab2274970380c000118aeffe1cd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "socket2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "syn" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tokio" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134af885d758d645f0f0505c9a8b3f9bf8a348fd822e112ab5248138348f1722" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "wasm-bindgen" +version = "0.2.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fe8f61dba8e5d645a4d8132dc7a0a66861ed5e1045d2c0ed940fab33bac0fbe" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046ceba58ff062da072c7cb4ba5b22a37f00a302483f7e2a6cdc18fedbdc1fd3" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9aa01d36cda046f797c57959ff5f3c615c9cc63997a8d545831ec7976819b" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96eb45c1b2ee33545a813a92dbb53856418bf7eb54ab34f7f7ff1448a5b3735d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7148f4696fb4960a346eaa60bbfb42a1ac4ebba21f750f75fc1375b098d5ffa" + +[[package]] +name = "web-sys" +version = "0.3.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59fe19d70f5dacc03f6e46777213facae5ac3801575d56ca6cbd4c93dcd12310" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "xmpp-proxy" +version = "1.0.0" +dependencies = [ + "anyhow", + "die", + "futures", + "serde", + "serde_derive", + "tokio", + "tokio-rustls", + "toml", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9c7be85 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "xmpp-proxy" +version = "1.0.0" +authors = ["moparisthebest "] + +description = "Reverse XMPP proxy." +repository = "https://code.moparisthebest.com/moparisthebest/xmpp-proxy" +keywords = ["xmpp", "proxy"] + +license = "AGPL-3.0-or-later" +readme = "README.md" + +edition = "2018" + +include = [ + "**/*.rs", + "Cargo.toml", + "*.md", + "xmpp-proxy.toml", +] + +[dependencies] +toml = "0.5" +serde_derive = "1.0" +serde = { version = "1.0", features = ["derive"] } +futures = "0.3" +die = "0.2.0" +anyhow = "1.0" +tokio = { version = "1.4", features = ["net", "rt", "rt-multi-thread", "macros", "io-util"] } +tokio-rustls = "0.22" diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..cba6f6a --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,660 @@ +### GNU AFFERO GENERAL PUBLIC LICENSE + +Version 3, 19 November 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +### Preamble + +The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains +free software for all its users. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + +A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + +The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + +An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing +under this license. + +The precise terms and conditions for copying, distribution and +modification follow. + +### TERMS AND CONDITIONS + +#### 0. Definitions. + +"This License" refers to version 3 of the GNU Affero General Public +License. + +"Copyright" also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of +an exact copy. The resulting work is called a "modified version" of +the earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user +through a computer network, with no transfer of a copy, is not +conveying. + +An interactive user interface displays "Appropriate Legal Notices" to +the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +#### 1. Source Code. + +The "source code" for a work means the preferred form of the work for +making modifications to it. "Object code" means any non-source form of +a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can +regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same +work. + +#### 2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, +without conditions so long as your license otherwise remains in force. +You may convey covered works to others for the sole purpose of having +them make modifications exclusively for you, or provide you with +facilities for running those works, provided that you comply with the +terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for +you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your +copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes +it unnecessary. + +#### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such +circumvention is effected by exercising rights under this License with +respect to the covered work, and you disclaim any intention to limit +operation or modification of the work as a means of enforcing, against +the work's users, your or third parties' legal rights to forbid +circumvention of technological measures. + +#### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +#### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these +conditions: + +- a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. +- b) The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in section 4 + to "keep intact all notices". +- c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +#### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of +sections 4 and 5, provided that you also convey the machine-readable +Corresponding Source under the terms of this License, in one of these +ways: + +- a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. +- b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the Corresponding + Source from a network server at no charge. +- c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. +- d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. +- e) Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the general + public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of +coverage. For a particular product received by a particular user, +"normally used" refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected +to use, the product. A product is a consumer product regardless of +whether the product has substantial commercial, industrial or +non-consumer uses, unless such uses represent the only significant +mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to +install and execute modified versions of a covered work in that User +Product from a modified version of its Corresponding Source. The +information must suffice to ensure that the continued functioning of +the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or +updates for a work that has been modified or installed by the +recipient, or for the User Product in which it has been modified or +installed. Access to a network may be denied when the modification +itself materially and adversely affects the operation of the network +or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +#### 7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + +- a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or +- b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or +- c) Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or +- d) Limiting the use for publicity purposes of names of licensors + or authors of the material; or +- e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or +- f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions + of it) with contractual assumptions of liability to the recipient, + for any liability that these contractual assumptions directly + impose on those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; the +above requirements apply either way. + +#### 8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +#### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run +a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +#### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +#### 11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned +or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on +the non-exercise of one or more of the rights that are specifically +granted under this License. You may not convey a covered work if you +are a party to an arrangement with a third party that is in the +business of distributing software, under which you make payment to the +third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties +who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by +you (or copies made from those copies), or (b) primarily for and in +connection with specific products or compilations that contain the +covered work, unless you entered into that arrangement, or that patent +license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +#### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not convey it at all. For example, if you agree to +terms that obligate you to collect a royalty for further conveying +from those to whom you convey the Program, the only way you could +satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +#### 13. Remote Network Interaction; Use with the GNU General Public License. + +Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your +version supports such interaction) an opportunity to receive the +Corresponding Source of your version by providing access to the +Corresponding Source from a network server at no charge, through some +standard or customary means of facilitating copying of software. This +Corresponding Source shall include the Corresponding Source for any +work covered by version 3 of the GNU General Public License that is +incorporated pursuant to the following paragraph. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + +#### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU Affero General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever +published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions +of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +#### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +#### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +#### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively state +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper +mail. + +If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for +the specific requirements. + +You should also get your employer (if you work as a programmer) or +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. For more information on this, and how to apply and follow +the GNU AGPL, see . diff --git a/README.md b/README.md new file mode 100644 index 0000000..6e0801c --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# xmpp-proxy + +[![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 for XMPP servers, providing STARTTLS and TLS over plain-text XMPP connections +and limiting stanza sizes without an XML parser. + +xmpp-proxy will listen on any number of interfaces/ports and accept any STARTTLS or [Direct TLS](https://xmpp.org/extensions/xep-0368.html) +c2s or s2s connections, terminate TLS, and connect them to a real XMPP server, limiting stanza sizes as configured. + +#### Installation + * `cargo install xmpp-proxy` + * Download static binary from [xmpp-proxy](https://code.moparisthebest.com/moparisthebest/xmpp-proxy/releases) + or [xmpp-proxy (github mirror)](https://github.com/moparisthebest/xmpp-proxy/releases) + * your favorite package manager + +#### Configuration + * `mkdir /etc/xmpp-proxy/ && cp xmpp-proxy.toml /etc/xmpp-proxy/` + * edit `/etc/xmpp-proxy/xmpp-proxy.toml` as needed, file is annotated clearly with comments + * put your TLS key/cert in `/etc/xmpp-proxy/`, if your key has "RSA PRIVATE KEY" in it, change that to "PRIVATE KEY": + `sed -i 's/RSA PRIVATE KEY/PRIVATE KEY/' /etc/xmpp-proxy/le.key` + * Example systemd unit is provided in xmpp-proxy.service and locks it down with bare minimum permissions. Need to + set the permissions correctly: `chown -Rv 'systemd-network:' /etc/xmpp-proxy/` + * start xmpp-proxy: `Usage: xmpp-proxy [/path/to/xmpp-proxy.toml (default /etc/xmpp-proxy/xmpp-proxy.toml]` + +#### How do I adapt my running Prosody config to use this instead? + +Add these to modules_enabled: +``` +"secure_interfaces"; +"net_proxy"; +``` +Until prosody-modules is updated, use my patched version of [mod_secure_interfaces.lua](https://www.moparisthebest.com/mod_secure_interfaces.lua) +which also works for s2s. + +Add this config: +``` +-- trust connections coming from these IPs +secure_interfaces = { "127.0.0.1", "::1" } + +-- handle PROXY protocol on these ports +proxy_port_mappings = { + [15222] = "c2s", + [15269] = "s2s" +} + +-- 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 = {15269} +``` + +Copy prosody's TLS key to `/etc/xmpp-proxy/le.key` and TLS cert to `/etc/xmpp-proxy/fullchain.cer`, and use the provided +`xmpp-proxy.toml` configuration as-is. + +#### License +GNU/AGPLv3 - Check LICENSE.md for details diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..31f5e60 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,403 @@ +use std::ffi::OsString; +use std::fs::File; +use std::io; +use std::io::{BufReader, Read}; +use std::iter::Iterator; +use std::net::SocketAddr; +use std::path::Path; +use std::sync::Arc; + +use die::Die; + +use serde_derive::Deserialize; + +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::TcpListener; +use tokio::task::JoinHandle; + +use tokio_rustls::rustls::internal::pemfile::{certs, pkcs8_private_keys}; +use tokio_rustls::rustls::{NoClientAuth, ServerConfig}; +use tokio_rustls::TlsAcceptor; + +use anyhow::{bail, Result}; + +mod slicesubsequence; +use slicesubsequence::*; + +const IN_BUFFER_SIZE: usize = 8192; +const OUT_BUFFER_SIZE: usize = 8192; + +const WHITESPACE: &[u8] = b" \t\n\r"; + +#[cfg(debug_assertions)] +fn c2s(is_c2s: bool) -> &'static str { + if is_c2s { + "c2s" + } else { + "s2s" + } +} + +#[cfg(debug_assertions)] +#[macro_export] +macro_rules! debug { + ($($y:expr),+) => (println!($($y),+)); +} + +#[cfg(not(debug_assertions))] +#[macro_export] +macro_rules! debug { + ($($y:expr),+) => {}; +} + +#[derive(Deserialize)] +struct Config { + tls_key: String, + tls_cert: String, + listen: Vec, + max_stanza_size_bytes: usize, + s2s_target: String, + c2s_target: String, + proxy: bool, +} + +#[derive(Clone)] +struct CloneableConfig { + max_stanza_size_bytes: usize, + s2s_target: String, + c2s_target: String, + proxy: bool, + acceptor: TlsAcceptor, +} + +impl Config { + fn parse>(path: P) -> Result { + let mut f = File::open(path)?; + let mut input = String::new(); + f.read_to_string(&mut input)?; + Ok(toml::from_str(&input)?) + } + + fn get_cloneable_cfg(&self) -> Result { + Ok(CloneableConfig { + max_stanza_size_bytes: self.max_stanza_size_bytes, + s2s_target: self.s2s_target.clone(), + c2s_target: self.c2s_target.clone(), + proxy: self.proxy, + acceptor: self.tls_acceptor()?, + }) + } + + fn tls_acceptor(&self) -> Result { + let mut tls_key = pkcs8_private_keys(&mut BufReader::new(File::open(&self.tls_key)?)).map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid key"))?; + if tls_key.is_empty() { + bail!("invalid key"); + } + let tls_key = tls_key.remove(0); + + let tls_cert = certs(&mut BufReader::new(File::open(&self.tls_cert)?)).map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert"))?; + + let mut config = ServerConfig::new(NoClientAuth::new()); + config.set_single_cert(tls_cert, tls_key).map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?; + Ok(TlsAcceptor::from(Arc::new(config))) + } +} + +fn to_str(buf: &[u8]) -> &str { + std::str::from_utf8(buf).unwrap_or("[invalid utf-8]") +} + +async fn handle_connection(mut stream: tokio::net::TcpStream, client_addr: SocketAddr, local_addr: SocketAddr, config: CloneableConfig) -> Result<()> { + println!("INFO: {} connected", client_addr); + + let mut in_filter = StanzaFilter::new(config.max_stanza_size_bytes); + + let direct_tls = { + // sooo... I don't think peek here can be used for > 1 byte without this timer + // craziness... can it? this could be switched to only peek 1 byte and assume + // a leading 0x16 is TLS, it would *probably* be ok ? + //let mut p = [0u8; 3]; + let mut p = &mut in_filter.buf[0..3]; + // wait up to 10 seconds until 3 bytes have been read + use std::time::{Duration, Instant}; + let duration = Duration::from_secs(10); + let now = Instant::now(); + loop { + let n = stream.peek(&mut p).await?; + if n == 3 { + break; // success + } + if n == 0 { + bail!("not enough bytes"); + } + if Instant::now() - now > duration { + bail!("less than 3 bytes in 10 seconds, closed connection?"); + } + } + + /* TLS packet starts with a record "Hello" (0x16), followed by version + * (0x03 0x00-0x03) (RFC6101 A.1) + * This means we reject SSLv2 and lower, which is actually a good thing (RFC6176) + */ + p[0] == 0x16 && p[1] == 0x03 && p[2] <= 0x03 + }; + + println!("INFO: {} direct_tls: {}", client_addr, direct_tls); + + // starttls + if !direct_tls { + let mut stream_open = Vec::new(); + + while let Ok(n) = stream.read(in_filter.current_buf()).await { + if n == 0 { + bail!("stream ended before open"); + } + if let Some(buf) = in_filter.process_next_byte()? { + debug!("received pre-tls stanza: {} '{}'", client_addr, to_str(&buf)); + let buf = buf.trim_start(WHITESPACE); + if buf.starts_with(b" {} '{}'", client_addr, to_str(&stream_open)); + stream.write_all(&stream_open).await?; + stream_open.clear(); + + // gajim seems to REQUIRE an id here... + let buf = if buf.contains_seq(b"id=") { + buf.replace(b" id='", b" id='xmpp-proxy") + .replace(br#" id=""#, br#" id="xmpp-proxy"#) + .replace(b" to=", br#" bla toblala="#) + .replace(b" from=", b" to=") + .replace(br#" bla toblala="#, br#" from="#) + } else { + buf.replace(b" to=", br#" bla toblala="#) + .replace(b" from=", b" to=") + .replace(br#" bla toblala="#, br#" id='xmpp-proxy' from="#) + }; + + debug!("> {} '{}'", client_addr, to_str(&buf)); + stream.write_all(&buf).await?; + + stream + .write_all(br###""###) + .await?; + stream.flush().await?; + } else if buf.starts_with(b""###).await?; + stream.flush().await?; + break; + } else { + bail!("bad pre-tls stanza: {}", to_str(&buf)); + } + } + } + } + + let stream = config.acceptor.accept(stream).await?; + + let (in_rd, mut in_wr) = tokio::io::split(stream); + + // we naively read 1 byte at a time, which buffering significantly speeds up + let mut in_rd = tokio::io::BufReader::with_capacity(IN_BUFFER_SIZE, in_rd); + + // now read to figure out client vs server + let (stream_open, is_c2s) = { + let mut stream_open = Vec::new(); + let mut ret = None; + + while let Ok(n) = in_rd.read(in_filter.current_buf()).await { + if n == 0 { + bail!("stream ended before open"); + } + if let Some(buf) = in_filter.process_next_byte()? { + debug!("received pre- stanza: {} '{}'", client_addr, to_str(&buf)); + let buf = buf.trim_start(WHITESPACE); + if buf.starts_with(b" stanza: {}", to_str(&buf)); + } + } + } + if ret.is_some() { + ret.unwrap() + } else { + bail!("stream ended before open"); + } + }; + + let target = if is_c2s { config.c2s_target } else { config.s2s_target }; + + println!("INFO: {} is_c2s: {}, target: {}", client_addr, is_c2s, target); + + let out_stream = tokio::net::TcpStream::connect(target).await?; + let (mut out_rd, mut out_wr) = tokio::io::split(out_stream); + + if config.proxy { + /* + https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt + PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n + PROXY TCP6 ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n + PROXY TCP6 SOURCE_IP DEST_IP SOURCE_PORT DEST_PORT\r\n + */ + // tokio AsyncWrite doesn't have write_fmt so have to go through this buffer for some crazy reason + //write!(out_wr, "PROXY TCP{} {} {} {} {}\r\n", if client_addr.is_ipv4() { '4' } else {'6' }, client_addr.ip(), local_addr.ip(), client_addr.port(), local_addr.port())?; + use std::io::Write; + write!( + &mut in_filter.buf[0..], + "PROXY TCP{} {} {} {} {}\r\n", + if client_addr.is_ipv4() { '4' } else { '6' }, + client_addr.ip(), + local_addr.ip(), + client_addr.port(), + local_addr.port() + )?; + let end_idx = &(&in_filter.buf[0..]).first_index_of(b"\n")? + 1; + debug!("< {} {} '{}'", client_addr, c2s(is_c2s), to_str(&in_filter.buf[0..end_idx])); + out_wr.write_all(&in_filter.buf[0..end_idx]).await?; + } + debug!("< {} {} '{}'", client_addr, c2s(is_c2s), to_str(&stream_open)); + out_wr.write_all(&stream_open).await?; + out_wr.flush().await?; + drop(stream_open); + + let mut out_buf = [0u8; OUT_BUFFER_SIZE]; + + loop { + tokio::select! { + Ok(n) = in_rd.read(in_filter.current_buf()) => { + if n == 0 { + break; + } + if let Some(buf) = in_filter.process_next_byte()? { + debug!("< {} {} '{}'", client_addr, c2s(is_c2s), to_str(buf)); + out_wr.write_all(buf).await?; + out_wr.flush().await?; + } + }, + // we could filter outgoing from-server stanzas by size here too by doing same as above + // but instead, we'll just send whatever the server sends as it sends it... + Ok(n) = out_rd.read(&mut out_buf) => { + if n == 0 { + break; + } + debug!("> {} {} '{}'", client_addr, c2s(is_c2s), to_str(&out_buf[0..n])); + in_wr.write_all(&out_buf[0..n]).await?; + in_wr.flush().await?; + }, + } + } + + println!("INFO: {} disconnected", client_addr); + Ok(()) +} + +fn spawn_listener(listener: TcpListener, config: CloneableConfig) -> JoinHandle> { + let local_addr = listener.local_addr().die("could not get local_addr?"); + tokio::spawn(async move { + loop { + let (stream, client_addr) = listener.accept().await?; + let config = config.clone(); + tokio::spawn(async move { + if let Err(e) = handle_connection(stream, client_addr, local_addr, config).await { + eprintln!("ERROR: {} {}", client_addr, e); + } + }); + } + #[allow(unreachable_code)] + Ok(()) + }) +} + +#[tokio::main] +//#[tokio::main(flavor = "multi_thread", worker_threads = 10)] +async fn main() { + let main_config = Config::parse(std::env::args_os().skip(1).next().unwrap_or(OsString::from("/etc/xmpp-proxy/xmpp-proxy.toml"))).die("invalid config file"); + + let config = main_config.get_cloneable_cfg().die("invalid cert/key ?"); + + let mut handles = Vec::with_capacity(main_config.listen.len()); + for listener in main_config.listen { + let listener = TcpListener::bind(&listener).await.die("cannot listen on port/interface"); + handles.push(spawn_listener(listener, config.clone())); + } + futures::future::join_all(handles).await; +} + +struct StanzaFilter { + buf_size: usize, + buf: Vec, + cnt: usize, + tag_cnt: usize, + last_char_was_lt: bool, + last_char_was_backslash: bool, +} + +impl StanzaFilter { + pub fn new(buf_size: usize) -> StanzaFilter { + StanzaFilter { + buf_size, + buf: vec![0u8; buf_size], + cnt: 0, + tag_cnt: 0, + last_char_was_lt: false, + last_char_was_backslash: false, + } + } + + #[inline(always)] + pub fn current_buf(&mut self) -> &mut [u8] { + &mut self.buf[self.cnt..(self.cnt + 1)] + } + + pub fn process_next_byte(&mut self) -> Result> { + //println!("n: {}", n); + let b = self.buf[self.cnt]; + if b == b'<' { + self.tag_cnt += 1; + self.last_char_was_lt = true; + } else { + if b == b'/' { + // if last_char_was_lt but tag_cnt < 2, should only be + if self.last_char_was_lt && self.tag_cnt >= 2 { + // non-self-closing tag + self.tag_cnt -= 2; + } + self.last_char_was_backslash = true; + } else { + if b == b'>' { + if self.last_char_was_backslash { + // self-closing tag + self.tag_cnt -= 1; + } + // now special case some tags we want to send stand-alone: + if self.tag_cnt == 1 && self.cnt >= 15 && (b" { + fn trim_start(&self, needle: &[T]) -> &[T]; + fn first_index_of(&self, needle: &[T]) -> Result; + fn replace(self, needle: &[T], replacement: &[T]) -> Vec; + + fn contains_seq(&self, needle: &[T]) -> bool { + self.first_index_of(needle).is_ok() + } +} + +fn last_index_of(s: &[T], needle: &[T]) -> usize { + let mut len = 0; + for i in s { + if needle.contains(i) { + len += 1; + } else { + break; + } + } + len +} + +impl SliceSubsequence for &[T] { + fn trim_start(&self, needle: &[T]) -> &[T] { + &self[last_index_of(self, needle)..] + } + + fn first_index_of(&self, needle: &[T]) -> Result { + for i in 0..self.len() - needle.len() + 1 { + if self[i..i + needle.len()] == needle[..] { + return Ok(i); + } + } + Err(anyhow!("not found")) + } + + fn replace(self, needle: &[T], replacement: &[T]) -> Vec { + self.to_vec().replace(needle, replacement) + } +} + +impl SliceSubsequence for Vec { + fn trim_start(&self, needle: &[T]) -> &[T] { + &self[last_index_of(self, needle)..] + } + + fn first_index_of(&self, needle: &[T]) -> Result { + return (self.as_slice()).first_index_of(needle); + } + + fn replace(mut self, needle: &[T], replacement: &[T]) -> Vec { + while let Ok(idx) = self.first_index_of(needle) { + self.splice(idx..(idx + needle.len()), replacement.iter().cloned()); + } + self + } +} + +#[cfg(test)] +mod tests { + use crate::slicesubsequence::*; + const WHITESPACE: &[u8] = b" \t\n\r"; + + #[test] + fn trim_start() { + let buf = &b" bla"[..]; + let buf = buf.trim_start(WHITESPACE); + assert_eq!(buf, b"bla"); + + let buf = &b"\n\t\r \rbla"[..]; + let buf = buf.trim_start(WHITESPACE); + assert_eq!(buf, b"bla"); + } + #[test] + fn replace() { + let buf = b"bla to='tsnhaou' bla2".replace(b" to=", b" from="); + assert_eq!(buf, b"bla from='tsnhaou' bla2"); + + let buf = buf.replace(b" from=", b" to="); + assert_eq!(buf, b"bla to='tsnhaou' bla2"); + + let buf = buf.replace(b" to=", b" from="); + assert_eq!(buf, b"bla from='tsnhaou' bla2"); + + let buf = b"bla to='tsnhaou' bla2".replace(b"bla", b"boo"); + assert_eq!(buf, b"boo to='tsnhaou' boo2"); + + let buf = buf.replace(b"boo", b"bla"); + assert_eq!(buf, b"bla to='tsnhaou' bla2"); + + let buf = buf.replace(b" bla2", b""); + assert_eq!(buf, b"bla to='tsnhaou'"); + } +} diff --git a/systemd/xmpp-proxy.service b/systemd/xmpp-proxy.service new file mode 100644 index 0000000..8652f5c --- /dev/null +++ b/systemd/xmpp-proxy.service @@ -0,0 +1,48 @@ +[Unit] +Description=xmpp-proxy +After=network-online.target nss-lookup.target +Wants=network-online.target nss-lookup.target +StartLimitIntervalSec=0 +Documentation=https://code.moparisthebest.com/moparisthebest/xmpp-proxy +Documentation=https://github.com/moparisthebest/xmpp-proxy + +[Service] +ExecStart=/usr/bin/xmpp-proxy +Restart=always +RestartSec=1s +# anything under here isn't strictly needed, but probably good +# to lock this down with the minimal permissions necessary +# which are not many at all +User=xmpp-proxy +DynamicUser=yes +ConfigurationDirectory=xmpp-proxy +ConfigurationDirectoryMode=0750 +ProtectSystem=strict +ProtectHome=true +PrivateTmp=true +PrivateDevices=true +ProtectHostname=true +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectKernelLogs=true +ProtectControlGroups=true +RestrictAddressFamilies=AF_INET AF_INET6 +RestrictNamespaces=net +LockPersonality=true +MemoryDenyWriteExecute=true +RestrictRealtime=true +RestrictSUIDSGID=true +RemoveIPC=true +SystemCallArchitectures=native +# these are just needed to bind to low ports +CapabilityBoundingSet=CAP_NET_BIND_SERVICE +AmbientCapabilities=CAP_NET_BIND_SERVICE + +# this is any existing group the key/cert can be owned by +# so that our random user can read them +# not needed at all without TLS, can comment out +# see https://github.com/systemd/systemd/issues/9535 +SupplementaryGroups=systemd-network + +[Install] +WantedBy=multi-user.target diff --git a/xmpp-proxy.toml b/xmpp-proxy.toml new file mode 100644 index 0000000..8617803 --- /dev/null +++ b/xmpp-proxy.toml @@ -0,0 +1,28 @@ + +# interfaces to listen for external XMPP connections on +listen = [ "0.0.0.0:5222", "0.0.0.0:5269" ] + +# 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/xmpp-proxy/le.key" +tls_cert = "/etc/xmpp-proxy/fullchain.cer"