mirror of
https://repo.or.cz/socat.git
synced 2025-07-20 09:52:56 +00:00
Added socat-chain.sh for layering protocols
This commit is contained in:
parent
27877ea777
commit
e62f62e5b9
7 changed files with 442 additions and 21 deletions
264
socat-chain.sh
Executable file
264
socat-chain.sh
Executable file
|
@ -0,0 +1,264 @@
|
|||
#! /usr/bin/env bash
|
||||
# Copyright Gerhard Rieger and contributors (see file CHANGES)
|
||||
# Published under the GNU General Public License V.2, see file COPYING
|
||||
|
||||
# Shell script to build a chain of Socat instances connected via TCP sockets.
|
||||
# This allows to drive, e.g., PROXY-CONNECT over SSL, or SSL over serial.
|
||||
# Currently only a chain made from 3 addresses, resulting in two instances, is
|
||||
# implemented.
|
||||
# The 2nd address must be one of OPENSSL (SSL), PROXY-CONNECT (PROXY),
|
||||
# SOCKS4, SOCKS4A, SOCKS5
|
||||
|
||||
# This is beta!
|
||||
|
||||
# Examples:
|
||||
|
||||
# Drive HTTP CONNECT (PROXY) over SSL
|
||||
# (establish an SSL tunnel to a proxy server, request being forwarded to a
|
||||
# telnet server):
|
||||
# socat-chain.sh \
|
||||
# STDIO \
|
||||
# PROXY::<telnet-server>:23 \
|
||||
# OPENSSL:<proxy-server>:8443
|
||||
|
||||
# Accept connections that arrive on port 7777, encrypt the data, and send it
|
||||
# via socks server to final target:
|
||||
# socat-chain.sh \
|
||||
# TCP-L:7777,reuseaddr,fork \
|
||||
# OPENSSL,verify=0 \
|
||||
# SOCKS4:<socks-server>:<ssl-server>:8443
|
||||
|
||||
# Receive SSL coming from a serial lie
|
||||
# socat-chain.sh \
|
||||
# /dev/ttyS0,cfmakeraw \
|
||||
# SSL-L,cafile=server.pem,verify=0 \
|
||||
# TCP4:localhost:80
|
||||
|
||||
# Formally, this is what happens:
|
||||
# socat-chain.sh addr1 addr2 addr3
|
||||
# results in something like:
|
||||
# socat TCP-L:RANDOM addr3 &
|
||||
# socat addr1 addr2:localhost:RANDOM
|
||||
# or on passive/listening addr2:
|
||||
# socat addr2:RANDOM addr3 &
|
||||
# socat addr1 TCP:localhost:RANDOM
|
||||
|
||||
ECHO="echo -e"
|
||||
|
||||
usage () {
|
||||
$ECHO "Usage: $0 <options> <address1> <address2> <address3>"
|
||||
$ECHO " <address1> is typically a passive (listening) address like"
|
||||
$ECHO " TCP-L:1234"
|
||||
$ECHO " <address2> must be one of OPENSSL, PROXY, SOCK4, SOCKS4A, or SOCKS5,"
|
||||
$ECHO " or SSL-L (passive/listening)"
|
||||
$ECHO " Given server hostname and port are ignored and replaced by internal"
|
||||
$ECHO " communication point"
|
||||
$ECHO " <address3> is typically a client address with protocol like OPENSSL"
|
||||
$ECHO " <options>:"
|
||||
$ECHO " -d* -S <sigmask> -t <timeout> -T <timeout> are passed to socat"
|
||||
$ECHO " -V prints the socat commands before starting them"
|
||||
$ECHO "Example to drive SOCKS over TLS:"
|
||||
$ECHO " $0 \\"
|
||||
$ECHO " TCP4-L:1234,reuseaddr,fork \\"
|
||||
$ECHO " SOCKS::<server>:<port> \\"
|
||||
$ECHO " OPENSSL:10.2.3.4:12345,cafile=..."
|
||||
$ECHO " Clients that connect to port 1234 will be forwarded to <server>:<port> using socks"
|
||||
$ECHO " over TLS"
|
||||
}
|
||||
|
||||
|
||||
LOCALHOST=127.0.0.1
|
||||
|
||||
VERBOSE= QUIET= OPTS=
|
||||
while [ "$1" ]; do
|
||||
case "X$1" in
|
||||
X-h) usage; exit ;;
|
||||
X-V) VERBOSE=1 ;;
|
||||
X-q) QUIET=1; OPTS="-d0" ;;
|
||||
X-d*|X-l?*) OPTS="$OPTS $1" ;;
|
||||
X-b|X-S|X-t|X-T|X-l) OPT=$1; shift; OPTS="$OPTS $OPT $1" ;;
|
||||
X-) break ;;
|
||||
X-*) echo "$0: Unknown option \"$1\"" >&2
|
||||
usage >&2
|
||||
exit 1 ;;
|
||||
*) break ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
ARG0="$1"
|
||||
ARG1="$2"
|
||||
ARG2="$3"
|
||||
|
||||
if [ -z "$ARG0" -o -z "$ARG1" -o -z "$ARG2" ]; then
|
||||
echo "$0: Three addresses required" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
mkprogname () {
|
||||
ARG="$1"
|
||||
if [[ "$ARG" =~ .*[:].* ]]; then
|
||||
NAME="${ARG%%:*}"
|
||||
elif [[ "$ARG" =~ .*[,].* ]]; then
|
||||
NAME="${ARG%%,*}"
|
||||
elif [ "X$ARG" = X- ]; then
|
||||
NAME=stdio
|
||||
else
|
||||
NAME="$ARG"
|
||||
fi
|
||||
NAME="${NAME,,*}"
|
||||
echo $NAME
|
||||
}
|
||||
|
||||
|
||||
# You may place a fork option in the first address
|
||||
# in which case the following internal listeners do fork too
|
||||
FORK=
|
||||
case "$ARG0" in
|
||||
*,fork,*|*,fork) FORK=fork ;;
|
||||
esac
|
||||
|
||||
# Split middle address for insertion of additional parts
|
||||
if [[ "$ARG1" =~ .*,.* ]]; then
|
||||
ARG1A="${ARG1%%,*}"
|
||||
ARG1B="${ARG1#*,}"
|
||||
else
|
||||
ARG1A="$ARG1"
|
||||
ARG1B=
|
||||
fi
|
||||
|
||||
case "$0" in
|
||||
*/*) SOCAT=${0%/*}/socat ;;
|
||||
*) SOCAT=socat ;;
|
||||
esac
|
||||
|
||||
PORT=$($SOCAT -d -d TCP4-L:0,accept-timeout=0.000001 /dev/null 2>&1 |grep listening |sed 's/.*:\([1-9][0-9]*\)$/\1/')
|
||||
if [ -z "$PORT" ]; then
|
||||
echo "$0: Failed to determine free TCP port" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PASSIVE= # is the second address passive/listening/server?
|
||||
case "${ARG1A^^*}" in
|
||||
OPENSSL|OPENSSL:*|SSL|SSL:.*)
|
||||
OPTS1A=
|
||||
#if [[ $ARG1A =~ ^\([^:]*\):\([^:]*\):\([^,]*\)\(.*\) ]]; then # bash 3
|
||||
if [[ $ARG1A =~ ^([^:]*):([^:]*):([^,]*)(.*) ]]; then
|
||||
OPTS1A="${BASH_REMATCH[4]}"
|
||||
#elif [[ $ARG1A =~ ^\([^,]*\)\(.*\) ]]; then # bash 3
|
||||
elif [[ $ARG1A =~ ^([^,]*)(.*) ]]; then
|
||||
OPTS1A="${BASH_REMATCH[2]}"
|
||||
else
|
||||
echo "$0: \"$ARG1A\": invalid arguments" >&2
|
||||
exit 1
|
||||
fi
|
||||
PROG1="${BASH_REMATCH[1]}"
|
||||
NAME1=$(mkprogname "${BASH_REMATCH[1]}")
|
||||
NAME2=$(mkprogname "$ARG2")
|
||||
ARG1A=$PROG1:$LOCALHOST:$PORT$OPTS1A ;;
|
||||
PROXY-CONNECT:*|PROXY:*)
|
||||
#if ! [[ $ARG1A =~ ^\([^:]*\):\([^:]*\):\([^:]*\):\([^,]*\)\(.*\) ]]; then # bash 3
|
||||
if ! [[ $ARG1A =~ ^([^:]*):([^:]*):([^:]*):([^,]*)(.*) ]]; then
|
||||
echo "$0: \"$ARG1A\": invalid arguments" >&2
|
||||
exit 1
|
||||
fi
|
||||
#echo "0:\"${BASH_REMATCH[0]}\" 1:\"${BASH_REMATCH[1]}\" 2:\"${BASH_REMATCH[2]}\" 3:\"${BASH_REMATCH[3]}\" 4:\"${BASH_REMATCH[4]}\""
|
||||
PROG1="${BASH_REMATCH[1]}"
|
||||
NAME1=$(mkprogname "${PROG1,,*}")
|
||||
NAME2=$(mkprogname "$ARG2")
|
||||
OPTS1A="${BASH_REMATCH[5]}"
|
||||
ARG1A="$PROG1:$LOCALHOST:${BASH_REMATCH[3]}:${BASH_REMATCH[4]},proxyport=$PORT,$OPTS1A" ;;
|
||||
SOCKS:*|SOCKS4:*|SOCKS4A*)
|
||||
#if ! [[ $ARG1A =~ ^\([^:]*\):\([^:]*\):\([^:]*\):\([^:,]*\),* ]]; then # bash 3
|
||||
if ! [[ $ARG1A =~ ^([^:]*):([^:]*):([^:]*):([^:,]*),* ]]; then
|
||||
echo "$0: \"$ARG1A\": invalid arguments" >&2
|
||||
exit 1
|
||||
fi
|
||||
PROG1="${BASH_REMATCH[1]}"
|
||||
NAME1=$(mkprogname "${PROG1,,*}")
|
||||
NAME2=$(mkprogname "$ARG2")
|
||||
OPTS1A="${BASH_REMATCH[5]}"
|
||||
ARG1A="$PROG1:$LOCALHOST:${BASH_REMATCH[3]}:${BASH_REMATCH[4]},socksport=$PORT,$OPTS1A" ;;
|
||||
SOCKS5:*|SOCKS5-CONNECT*)
|
||||
#if ! [[ $ARG1A =~ ^\([^:]*\):\([^:]*\):\([^:]*\):\([^:,]*\):\([^:,]*\),* ]]; then # bash 3
|
||||
if ! [[ $ARG1A =~ ^([^:]*):([^:]*):([^:]*):([^:,]*):([^:,]*),* ]]; then
|
||||
echo "$0: \"$ARG1A\": invalid arguments" >&2
|
||||
exit 1
|
||||
fi
|
||||
PROG1="${BASH_REMATCH[1]}"
|
||||
NAME1=$(mkprogname "${PROG1,,*}")
|
||||
NAME2=$(mkprogname "$ARG2")
|
||||
OPTS1A="${BASH_REMATCH[6]}"
|
||||
ARG1A="$PROG1:$LOCALHOST:$PORT:${BASH_REMATCH[4]}:${BASH_REMATCH[5]},$OPTS1A" ;;
|
||||
# Passive (server) addresses
|
||||
OPENSSL-LISTEN|OPENSSL-LISTEN:*|SSL-L|SSL-L:.*)
|
||||
PASSIVE=1
|
||||
OPTS1A=
|
||||
#if [[ $ARG1A =~ ^\([^:]*\):\([^,]*\)\(.*\) ]]; then # bash 3
|
||||
if [[ $ARG1A =~ ^([^:]*):([^,]*)(.*) ]]; then
|
||||
OPTS1A="${BASH_REMATCH[3]}"
|
||||
#elif [[ $ARG1A =~ ^\([^,]*\)\(.*\) ]]; then # bash 3
|
||||
elif [[ $ARG1A =~ ^([^,]*)(.*) ]]; then
|
||||
OPTS1A="${BASH_REMATCH[2]}"
|
||||
else
|
||||
echo "$0: \"$ARG1A\": invalid arguments" >&2
|
||||
exit 1
|
||||
fi
|
||||
PROG1="${BASH_REMATCH[1]}"
|
||||
NAME1=$(mkprogname "$ARG0")
|
||||
NAME2=$(mkprogname "${BASH_REMATCH[1]}")
|
||||
ARG1A=$PROG1:$PORT$OPTS1A ;;
|
||||
*) echo "$0: Unsupported address \"$ARG1A\"" >&2
|
||||
usage >&2
|
||||
exit 1 ;;
|
||||
esac
|
||||
|
||||
ADDR1A="$ARG0"
|
||||
if [ -z "$PASSIVE" ]; then
|
||||
ADDR1B="$ARG1A,bind=$LOCALHOST,$ARG1B"
|
||||
ADDR2A="TCP4-L:$PORT,reuseaddr,$FORK,bind=$LOCALHOST,range=$LOCALHOST/32"
|
||||
else
|
||||
ADDR1B="TCP4:$LOCALHOST:$PORT,bind=$LOCALHOST"
|
||||
ADDR2A="$ARG1A,reuseaddr,$FORK,bind=$LOCALHOST,range=$LOCALHOST/32,$ARG1B"
|
||||
fi
|
||||
ADDR2B="$ARG2"
|
||||
|
||||
|
||||
pid1= pid2=
|
||||
trap '[ "$pid1" ] && kill $pid1 2>/dev/null; [ "$pid2" ] && kill $pid2 2>/dev/null' EXIT
|
||||
|
||||
set -bm
|
||||
trap 'rc=$?; if ! kill -n 0 $pid2 2>/dev/null; then [ -z "$QUIET" -a $rc -ne 0 ] && echo "$0: socat-$NAME2 exited with rc=$rc" >&2; exit $rc; fi' SIGCHLD
|
||||
|
||||
# Start instance 2 first, because instance 1 ("left") connects to 2
|
||||
if [ "$VERBOSE" ]; then
|
||||
$ECHO "$SOCAT $OPTS -lp socat-$NAME2 \\
|
||||
\"$ADDR2A\" \\
|
||||
\"$ADDR2B\" &"
|
||||
fi
|
||||
$SOCAT $OPTS -lp socat-$NAME2 \
|
||||
"$ADDR2A" \
|
||||
"$ADDR2B" &
|
||||
pid2=$!
|
||||
sleep 0.1
|
||||
|
||||
#trap 'if ! kill -n 0 $pid1 2>/dev/null; then [ -z "$QUIET" ] && echo "$0: socat-$NAME1 exited with rc=$?" >&2; kill $pid2 2>/dev/null; exit 1; elif ! kill -n 0 $pid2 2>/dev/null; then [ -z "$QUIET" ] && echo "$0: socat-$NAME2 exited with rc=$?" >&2; kill $pid1 2>/dev/null; exit 1; fi' SIGCHLD
|
||||
|
||||
if [ "$VERBOSE" ]; then
|
||||
$ECHO "$SOCAT $OPTS -lp socat-$NAME1 \\
|
||||
\"$ADDR1A\" \\
|
||||
\"$ADDR1B\""
|
||||
fi
|
||||
$SOCAT $OPTS -lp socat-$NAME1 \
|
||||
"$ADDR1A" \
|
||||
"$ADDR1B"
|
||||
#pid1=$!
|
||||
rc1=$?
|
||||
|
||||
kill $pid2 2>/dev/null
|
||||
wait 2>/dev/null
|
||||
#wait -f
|
||||
|
||||
exit $rc1
|
Loading…
Add table
Add a link
Reference in a new issue