使用 socat 将 localhost 代理到 HTTPS 第一次返回连接被拒绝

使用 socat 将 localhost 代理到 HTTPS 第一次返回连接被拒绝

https://termbin.com/9hc2k我正在尝试使用 bash 重定向和来获取页面socat,特别是使用特殊文件/dev/tcp/localhost/8080来打开网络连接。

# fetch.sh
# fetch uploaded text from termbin.com

url=$1

# check if url is provided as argument
if [[ $# -lt 1 ]]; then
    echo "error: provide url" >&2
    echo >&2
    echo "Usage: fetch.sh [url]" >&2
    exit 1
fi

n1=$'\r\n'
request=$(cat <<END
GET /${url}/ HTTP/1.1
Host: termbin.com
Connection: close
$n1 
$n1
END
)
#echo "$request"

socat TCP-LISTEN:8080,fork,reuseaddr ssl:termbin.com:443,verify=0 &
socat_pid=$!
exec 3<>/dev/tcp/localhost/8080
echo "$request" >&3
cat <&3

exec 3>&-

如果我尝试获取https://termbin.com/9hc2k

$ ./fetch.sh 9hc2k
./fetch.sh: connect: Connection refused
./fetch.sh: line 26: /dev/tcp/localhost/8080: Connection refused
./fetch.sh: line 27: 3: Bad file descriptor
./fetch.sh: line 28: 3: Bad file descriptor

我第一次收到连接拒绝错误。

但如果我再试一次,

$ ./fetch.sh 9hc2k
2022/07/30 12:29:18 socat[497218] E bind(5, {AF=2 0.0.0.0:8080}, 16): Address already in use
HTTP/1.1 200 OK
Server: nginx
Date: Sat, 30 Jul 2022 02:29:20 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 6
Last-Modified: Sat, 30 Jul 2022 01:51:42 GMT
Connection: close
ETag: "62e48eae-6"
Accept-Ranges: bytes

hello

正如你所看到的,第二次尝试它有效。我已从hello链接中获取文本。

我不知道为什么它第一次失败,然后第二次(以及随后的)它工作。您能告诉我为什么会这样以及最合理的解决办法是什么吗?谢谢。

答案1

我的猜测是exec 3<>/dev/tcp/localhost/8080发生了太快了之后socat … &,此时socat还没有准备好。

您的脚本不会杀死它所socat产生的。当您重试时,旧连接socat已经存在并且能够处理新连接。

一个快速而肮脏的修复是在sleep 1之前exec 3<>…。更好的解决方法是尝试并定期重试,exec 3<>…直到成功或尝试次数达到某个合理的阈值(然后您假设失败)或kill -s 0 "$socat_pid"失败(那么你就知道socat由于某种原因已经退出)。

答案2

感谢卡米尔的解释。我最终通过监视 socat pid 的状态使其变为“S”来修复它。

这是我的固定代码。

# fetch uploaded text from termbin.com

url=$1

# check if url is provided as argument
if [[ $# -lt 1 ]]; then
    echo "error: provide url" >&2
    echo >&2
    echo "Usage: fetch.sh [url]" >&2
    exit 1
fi

n1=$'\r\n'
request=$(cat <<END
GET /${url}/ HTTP/1.1
Host: termbin.com
Connection: close
$n1 
$n1
END
)
#echo "$request"

socat TCP-LISTEN:8080,fork,reuseaddr ssl:termbin.com:443,verify=0 &
socat_pid=$!

# loop until socat's pid status becomes 'S'
while [[ -z $(ps -p $socat_pid -o stat --no-headers | grep 'S') ]]; do
    :
done

exec 3<>/dev/tcp/localhost/8080
echo "$request" >&3
cat <&3

# if socat's pid status is 'S' then kill it
if [[ -n $(ps -p $socat_pid -o stat --no-headers | grep 'S') ]]; then
    kill -9 $socat_pid
fi

exec 3>&-

相关内容