Init SyncPlay
Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
This commit is contained in:
commit
19bcae4b1a
9
README.md
Normal file
9
README.md
Normal file
@ -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.
|
6
stunnel/syncplay.conf
Normal file
6
stunnel/syncplay.conf
Normal file
@ -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
|
189
syncplay-bridge/syncplay-bridge
Executable file
189
syncplay-bridge/syncplay-bridge
Executable file
@ -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
|
25
syncplay-bridge/syncplay-bridge.service
Normal file
25
syncplay-bridge/syncplay-bridge.service
Normal file
@ -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
|
62
syncplay-proxy/syncplay-proxy
Executable file
62
syncplay-proxy/syncplay-proxy
Executable file
@ -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()
|
23
syncplay-proxy/syncplay-proxy.service
Normal file
23
syncplay-proxy/syncplay-proxy.service
Normal file
@ -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
|
1
syncplay/motd
Normal file
1
syncplay/motd
Normal file
@ -0,0 +1 @@
|
|||||||
|
The LYSERGIC SyncPlay server hugs you for a warm welcome!!! <3
|
23
syncplay/syncplay.service
Normal file
23
syncplay/syncplay.service
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user