From e3550e83216719cca2e57a350be24ac15ecc9e54 Mon Sep 17 00:00:00 2001 From: Jonas Wielicki Date: Fri, 20 Apr 2018 12:41:57 +0200 Subject: Initial version (it works!) --- README.rst | 20 ++++++++++++ echoz.sed | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ echoz.sh | 15 +++++++++ 3 files changed, 137 insertions(+) create mode 100644 README.rst create mode 100755 echoz.sed create mode 100755 echoz.sh diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..17eea4a --- /dev/null +++ b/README.rst @@ -0,0 +1,20 @@ +XMPP Echo Bot +============= + +Do you know that situation, you really really need an XMPP echo bot, but you don’t have access to high-level tools like `Python `_ to write one? All you have is `openssl`, `bash`, `dig`, `stdbuf` and `sed`? Then this tool is for you. + +This is an XMPP echo bot written in (mostly) sed. Bash is used to do the pre-authentication setup (look up DNS records, establish TLS via ``openssl s_client``). sed processes the XML stream and handles all interaction with the server on the XMPP level. Yes, this kinda parses XML in sed. + +Tricks and shortcuts +-------------------- + +* We use ``tr`` to convert ``>`` to ``\n`` -- since sed is line (or NUL) based, there’s not really another way to parse XMPP XML (which generally never contains newlines) with sed. +* TLS is handled outside of sed for similar reasons. And to keep my sanity (some people might question whether I still have any bit of sanity left). +* Likewise, SRV lookup and composition of the authentication data is entirely handled in bash. This also means that only PLAIN SASL authentication is supported -- SCRAM requires a level of interactivity which would be extremely hard to achieve in sed (not impossible though; we would "just" have to implement base64 and sha1-hmac in sed). + +Usage +----- + +:: + + ./echoz.sh user@domain password diff --git a/echoz.sed b/echoz.sed new file mode 100755 index 0000000..2a156be --- /dev/null +++ b/echoz.sed @@ -0,0 +1,102 @@ +#!/bin/sed -unrf +# read config into hold buffer +h; +# use domain as to +s#^\S+@(\S+)\s.+$##; +p;n; + +:wait-for-plain; +/^PLAIN<\/mechanism$/bauth-with-plain; +n; +bwait-for-plain; + +:auth-with-plain; +# load config +g; +s#^\S+\s+(\S+)$#\1#; +p;n; +bwait-for-sasl-result; + +:wait-for-sasl-result; +/^#; +p;n; +bwait-for-bind; + +:sasl-failure; +s#^.+$##; +q1; + +:wait-for-bind; +/^#;p;n; +bwait-for-bind-result; + +:wait-for-bind-result; +/#;p;q1; + +:send-presence; +s#^.+$##;p;n; +bmain-loop; + +:main-loop; +/#; +# store result in hold space +h; +bmessage-search-body-loop; + +:message-search-body-loop; +# drop message if end-of-message before body +\#\1#; +# append result to hold space +H; +# load full hold space and append and send +g; +s#^(.+)$#\1#; +p;n; +bmain-loop; diff --git a/echoz.sh b/echoz.sh new file mode 100755 index 0000000..2aff537 --- /dev/null +++ b/echoz.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -euf +thisdir=$(dirname $0) +pipename="${XDG_RUNTIME_DIR:-/tmp}/echoz-$$.pipe" +jid="$1" +password="$2" +username="$(echo "$jid" | cut -d'@' -f1)" +domain="$(echo "$jid" | cut -d'@' -f2)" +srv="$( ( dig +short SRV "_xmpp-client._tcp.$domain" || echo "0 0 5222 $domain" ) | sort -n)" +host="$(echo "$srv" | cut -d' ' -f4)" +port="$(echo "$srv" | cut -d' ' -f3)" +authstr="$(echo -ne "\0$username\0$password" | base64)" +rm -f $pipename +mkfifo $pipename +stdbuf -i0 -o0 openssl s_client -starttls xmpp -xmpphost $domain -connect $host:$port -quiet < $pipename | (echo -ne "$jid $authstr\n"; stdbuf -o0 tr '>\n' '\n\001') | stdbuf -o0 $thisdir/echoz.sed | stdbuf -o0 tr -d '\n' | stdbuf -o0 tr '\001' '\n' > $pipename -- cgit v1.2.3