summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeorg Pfuetzenreuter2022-07-31 19:52:23 +0200
committerGeorg Pfuetzenreuter2022-07-31 19:52:23 +0200
commit19bcae4b1a639537f447460df76219459f33caaa (patch)
tree4719811d11f55482f31e6ce3105d309bea5897fe
downloadsyncplay-19bcae4b1a639537f447460df76219459f33caaa.tar.gz
syncplay-19bcae4b1a639537f447460df76219459f33caaa.tar.bz2
syncplay-19bcae4b1a639537f447460df76219459f33caaa.zip
Init SyncPlayHEADmaster
Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
-rw-r--r--README.md9
-rw-r--r--stunnel/syncplay.conf6
-rwxr-xr-xsyncplay-bridge/syncplay-bridge189
-rw-r--r--syncplay-bridge/syncplay-bridge.service25
-rwxr-xr-xsyncplay-proxy/syncplay-proxy62
-rw-r--r--syncplay-proxy/syncplay-proxy.service23
-rw-r--r--syncplay/motd1
-rw-r--r--syncplay/syncplay.service23
8 files changed, 338 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..fd1c0cd
--- /dev/null
+++ b/README.md
@@ -0,0 +1,9 @@
+Resources powering the `lysergic.media` SyncPlay instance.
+
+#### Proxy:
+The proxy removes the superfluous STARTTLS message in order for stunnel to terminate the TLS connection.
+
+SyncPlay Client -> syncplay-proxy (lysergic.media:8999) -> stunnel (TLS [::1]:8997) -> syncplay (PLAIN [::1]:8998)
+
+#### Bridge:
+The bridge relays chat messages between SyncPlay and IRC.
diff --git a/stunnel/syncplay.conf b/stunnel/syncplay.conf
new file mode 100644
index 0000000..d029bea
--- /dev/null
+++ b/stunnel/syncplay.conf
@@ -0,0 +1,6 @@
+[syncplay]
+accept = ::1:8997
+connect = ::1:8998
+cert = /etc/ssl/lysergic.media/crt
+key = /etc/ssl/lysergic.media/key
+sslVersion = TLSv1.3
diff --git a/syncplay-bridge/syncplay-bridge b/syncplay-bridge/syncplay-bridge
new file mode 100755
index 0000000..20d8c5f
--- /dev/null
+++ b/syncplay-bridge/syncplay-bridge
@@ -0,0 +1,189 @@
+#!/usr/bin/env bash
+# dependencies: bash 4+, jq
+
+# original: https://gist.github.com/ncfavier/55c20a77a3fd72e00407f8889fc6e852
+# forked for LibertaCasa by Georg Pfuetzenreuter <georg@lysergic.dev>
+# notable changes: use of IRCv3 draft/relaymsg and NickServ authentication using PASS
+
+set -Cu
+
+#. ./config.sh
+#: "${verbose:=0}"
+#: "${syncplay_host:=syncplay.pl}"
+#: "${syncplay_port:=8999}"
+#: "${syncplay_nick:=syncplaybridge}"
+#: "${syncplay_room:?"can't be empty"}"
+#: "${irc_host:=chat.freenode.net}"
+#: "${irc_port:=6667}"
+#: "${irc_nick:=syncplaybridge}"
+#: "${irc_channel:?"can't be empty"}"
+
+verbose=0
+syncplay_host='::1'
+syncplay_port=8998
+syncplay_password=NOT_IMPLEMENTED
+syncplay_nick=irc
+syncplay_room=%%SP_ROOM%%
+irc_host='::1'
+irc_port=6667
+irc_nick=syncplay
+irc_nickserv_nick=syncplay
+irc_nickserv_pass='%%IRC_NS_PASS%%'
+irc_channel='%%IRC_CHANNEL%%' #including channel prefix such as # or !
+#irc_channel='#dev'
+
+irc_send() {
+ local args=("$@")
+ #echo "DEBUG: ARGS ARE - $args"
+ args[0]=${args[0]^^}
+ #echo "DEBUG: ARGS ARE - $args"
+ if (( ${#args[@]} > 1 )); then
+ args[${#args[@]}-1]=:${args[${#args[@]}-1]}
+ fi
+ printf '%s\r\n' "${args[*]}" >&"$irc_fd"
+ (( verbose )) && printf 'IRC %s==>%s %s\n' "$(tput setaf 1)" "$(tput sgr0)" "${args[*]}" >&2
+}
+
+irc_getnick() {
+ local arg=$1
+ echo "$arg" | grep -Po "(?<=\<).*(?=\>)"
+}
+
+irc_zwspnick() {
+ local arg=$1
+ local nick1="${arg:0:${#arg}/2}"
+ local nick2="${arg:${#arg}/2}"
+ printf "$nick1\u200b$nick2"
+}
+
+irc_say() {
+ #echo "DEBUG: irc_say $irc_channel - $*"
+ local arg="$*"
+ #echo "DEBUG: irc_say ARG: $arg"
+ local msg="$(echo $arg | cut -d '>' -f 2 | sed -e 's/^[[:space:]]*//')"
+ local nick="$(irc_getnick $arg)"
+ if [ -n "$nick" ]
+ then
+ local zwspnick="$(irc_zwspnick $nick)"
+ #irc_send relaymsg "$irc_channel" "$zwspnick/sp" "$msg"
+ irc_send relaymsg "$irc_channel" "$nick/sp" "$msg"
+ fi
+ #echo "DEBUG: irc_say NICK $zwspnick"
+ #echo "DEBUG: irc_say MSG $msg"
+ if [ -z "$nick" ]
+ then
+ irc_send privmsg "$irc_channel" "$msg"
+ fi
+}
+
+irc_ctcp() {
+ local args=("$@")
+ args[0]=${args[0]^^}
+ irc_send notice "$sender_nick" $'\1'"${args[*]}"$'\1'
+}
+
+syncplay_send() {
+ local msg=$(jq -nc "$@")
+ printf '%s\r\n' "$msg" >&"$syncplay_fd"
+ (( verbose )) && printf 'syncplay %s==>%s %s\n' "$(tput setaf 1)" "$(tput sgr0)" "$msg" >&2
+}
+
+syncplay_say() {
+ syncplay_send --arg message "$*" '{Chat: $message}'
+}
+
+on_exit() {
+ irc_send quit
+ kill $(jobs -p)
+}
+
+exec {irc_fd}<> /dev/tcp/"$irc_host"/"$irc_port" || exit
+exec {syncplay_fd}<> /dev/tcp/"$syncplay_host"/"$syncplay_port" || exit
+
+trap on_exit exit
+
+irc_send pass "$irc_nickserv_nick:$irc_nickserv_pass"
+irc_send nick "$irc_nick"
+irc_send user "$irc_nick" 0 '*' 'Syncplay<->IRC'
+
+# patched version with server password attempt
+#syncplay_send --arg username "$syncplay_nick" --arg room "$syncplay_room" --arg password "$syncplay_password" \
+# '{Hello: {$username, password: $password, room: {name: $room}, realversion: "1.6.5"}}'
+syncplay_send --arg username "$syncplay_nick" --arg room "$syncplay_room" \
+ '{Hello: {$username, room: {name: $room}, realversion: "1.6.5"}}'
+
+while read -ru "$irc_fd" line; do
+ line=${line%$'\r'}
+ (( verbose )) && printf 'IRC %s<==%s %s\n' "$(tput setaf 2)" "$(tput sgr0)" "$line" >&2
+ sender= sender_nick=
+ args=()
+ if [[ $line == :* ]]; then
+ sender=${line%% *}
+ sender=${sender#:}
+ sender_nick=${sender%%!*}
+ line=${line#* }
+ fi
+ while [[ $line == *' '* && $line != :* ]]; do
+ args+=("${line%% *}")
+ line=${line#* }
+ done
+ args+=("${line#:}")
+ cmd=${args[0]}
+ set -- "${args[@]:1}"
+ case ${cmd,,} in
+ 001) irc_send join "$irc_channel";;
+ ping) irc_send pong "$1";;
+ privmsg)
+ target=$1
+ message=$2
+ #echo "DEBUG: $target"
+ #echo "DEBUG: $message"
+ if [[ $target == "$irc_nick" ]] && [[ $message =~ ^$'\1'(.*)$'\1'$ ]]; then
+ #echo "DEBUG: privmsg IF 1"
+ message=${BASH_REMATCH[1]}
+ #echo "DEBUG: BASH REMATCH: $message"
+ read -r ctcp_cmd _ <<< "$message"
+ case ${ctcp_cmd,,} in
+ version) irc_ctcp version "syncplaybridge";;
+ esac
+ elif [[ "$target" == "$irc_channel" ]]; then
+ #echo "DEBUG: sender_nick IS $sender_nick"
+ if [[ ! "$sender_nick" == */sp ]]; then
+ #echo "DEBUG: privmsg IF 2"
+ if [[ $message =~ ^$'\1ACTION '(.*)$'\1'$ ]]; then
+ #echo "DEBUG: privmsg IF 2 $message MATCHES"
+ syncplay_say "* $sender_nick ${BASH_REMATCH[1]}"
+ else
+ #echo "DEBUG: privmsg IF 2 DOES NOT MATCH"
+ syncplay_say "<$sender_nick> $2"
+ fi
+ fi
+ fi
+ esac
+done &
+
+while read -ru "$syncplay_fd" line; do
+ line=${line%$'\r'}
+ (( verbose )) && printf 'syncplay %s<==%s %s\n' "$(tput setaf 2)" "$(tput sgr0)" "$line" >&2
+ . <(jq <<< "$line" -r --arg self "$syncplay_nick" '
+ def irc($msg): @sh "irc_say \($msg)";
+ (.State // empty
+ | @sh "syncplay_send \"{State: {}}\""),
+ (.Chat // empty
+ | select(.username != $self)
+ | irc("<\(.username)> \(.message)")),
+ (.Set // empty
+ | .user // empty
+ | keys[0] as $nick
+ | .[$nick]
+ | (
+ (.event // empty
+ | irc("\($nick) \(if .joined then "joined the room" else "left the room" end)")),
+ (.file // empty
+ | irc("\($nick) is now playing \(.name)"))
+ )
+ )
+ ')
+done &
+
+wait
diff --git a/syncplay-bridge/syncplay-bridge.service b/syncplay-bridge/syncplay-bridge.service
new file mode 100644
index 0000000..a8d4e97
--- /dev/null
+++ b/syncplay-bridge/syncplay-bridge.service
@@ -0,0 +1,25 @@
+[Unit]
+Description=Syncplay IRC Bridge
+Wants=network.target
+After=syncplay.service stunnel.service
+BindsTo=syncplay.service stunnel.service
+
+[Service]
+User=syncplay
+Group=syncplay
+ExecStart=/bin/bash /usr/local/bin/syncplay-bridge
+ProtectSystem=strict
+ProtectHome=yes
+PrivateDevices=yes
+PrivateTmp=yes
+PrivateUsers=yes
+ProtectKernelTunables=yes
+ProtectKernelLogs=yes
+ProtectControlGroups=yes
+ReadWritePaths=/var/lib/syncplay
+RestrictAddressFamilies=AF_INET6
+SystemCallArchitectures=native
+SystemCallFilter=@system-service
+
+[Install]
+WantedBy=multi-user.target
diff --git a/syncplay-proxy/syncplay-proxy b/syncplay-proxy/syncplay-proxy
new file mode 100755
index 0000000..5ed7a4e
--- /dev/null
+++ b/syncplay-proxy/syncplay-proxy
@@ -0,0 +1,62 @@
+#!/usr/bin/python3
+
+# See https://github.com/Syncplay/syncplay/issues/346 for this script's raison-d'etre.
+
+# original: https://github.com/Syncplay/syncplay/issues/346#issuecomment-698134786
+# forked for LibertaCasa by Georg Pfuetzenreuter <georg@lysergic.dev>
+# notable changes: utilize IPv6
+
+import select
+import socket
+import socketserver
+import sys
+
+listen_port = int(sys.argv[1])
+forward_port = int(sys.argv[2])
+
+class SyncplayRequestHandler(socketserver.BaseRequestHandler):
+ def handle(self):
+ print('Handling connection from:', self.client_address)
+ data = self.request.recv(1024)
+ if data.strip() != b'{"TLS": {"startTLS": "send"}}':
+ print('Bad connection header from:', self.client_address)
+ try:
+ self.request.close()
+ except:
+ pass
+ return
+ print('Opening forwarding connection on behalf of:', self.client_address)
+ forwarded_conn = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
+ forwarded_conn.connect(('localhost', forward_port))
+ # Acknowledge to the client that we are ready.
+ self.request.sendall(b'{"TLS": {"startTLS": "true"}}\r\n')
+ # Start proxying.
+ print('Proxying started for:', self.client_address)
+ sockets = (self.request, forwarded_conn)
+ while True:
+ rlist, _, xlist = select.select(sockets, (), sockets)
+ if len(xlist):
+ break
+ for s in rlist:
+ if s is self.request:
+ forwarded_conn.sendall(self.request.recv(1024))
+ elif s is forwarded_conn:
+ self.request.sendall(forwarded_conn.recv(1024))
+ print('Proxying stopped for:', self.client_address)
+ try:
+ forwarded_conn.close()
+ except:
+ pass
+ try:
+ self.request.close()
+ except:
+ pass
+
+class SyncplayServer(socketserver.ThreadingTCPServer):
+ address_family = socket.AF_INET6
+ def server_bind(self):
+ self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ socketserver.ThreadingTCPServer.server_bind(self)
+
+with SyncplayServer(('', listen_port), SyncplayRequestHandler) as server:
+ server.serve_forever()
diff --git a/syncplay-proxy/syncplay-proxy.service b/syncplay-proxy/syncplay-proxy.service
new file mode 100644
index 0000000..6d11d46
--- /dev/null
+++ b/syncplay-proxy/syncplay-proxy.service
@@ -0,0 +1,23 @@
+[Unit]
+Description=Syncplay TLS Fixer
+Wants=network.target
+
+[Service]
+User=syncplay
+Group=syncplay
+ExecStart=/usr/bin/python3.10 /usr/local/bin/syncplay-proxy 8999 8997
+ProtectSystem=strict
+ProtectHome=yes
+PrivateDevices=yes
+PrivateTmp=yes
+PrivateUsers=yes
+ProtectKernelTunables=yes
+ProtectKernelLogs=yes
+ProtectControlGroups=yes
+ReadWritePaths=/var/lib/syncplay
+RestrictAddressFamilies=AF_INET6 AF_INET
+SystemCallArchitectures=native
+SystemCallFilter=@system-service
+
+[Install]
+WantedBy=multi-user.target
diff --git a/syncplay/motd b/syncplay/motd
new file mode 100644
index 0000000..196c138
--- /dev/null
+++ b/syncplay/motd
@@ -0,0 +1 @@
+The LYSERGIC SyncPlay server hugs you for a warm welcome!!! <3
diff --git a/syncplay/syncplay.service b/syncplay/syncplay.service
new file mode 100644
index 0000000..286ac0d
--- /dev/null
+++ b/syncplay/syncplay.service
@@ -0,0 +1,23 @@
+[Unit]
+Description=Syncplay Server
+Wants=network.target
+
+[Service]
+User=syncplay
+Group=syncplay
+ExecStart=/usr/bin/syncplay-server --salt %%SALT%% --stats-db-file /var/lib/syncplay/db.sqlite --port 8998 --motd-file /etc/syncplay/motd
+ProtectSystem=strict
+ProtectHome=yes
+PrivateDevices=yes
+PrivateTmp=yes
+PrivateUsers=yes
+ProtectKernelTunables=yes
+ProtectKernelLogs=yes
+ProtectControlGroups=yes
+ReadWritePaths=/var/lib/syncplay
+RestrictAddressFamilies=AF_INET6 AF_INET
+SystemCallArchitectures=native
+SystemCallFilter=@system-service
+
+[Install]
+WantedBy=multi-user.target