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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user