0

I'm trying to fetch a page from https://termbin.com/9hc2k using bash redirections and socat, especially using special file /dev/tcp/localhost/8080 to open a network connection.

# 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>&-

If I try to fetch 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

First time I get Connection refused error.

If I try again however,

$ ./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

As you can see the second time of trying it works. I have fetched text hello from the link.

I don't know why it fails the first time and then second time (and subsequent) around it works. Can you tell me why this is so and what is the most rational fix? Thanks.

2 Answers 2

1

My guess is exec 3<>/dev/tcp/localhost/8080 happens too soon after socat … & and at this time socat is not ready yet.

Your script does not kill the socat it spawned. When you try again, the old socat is already there and able to handle a new connection.

A quick and dirty fix is to sleep 1 or so before exec 3<>…. A better fix is to try and periodically retry exec 3<>… until it succeeds or the number of tries reaches some sane threshold (then you assume failure) or kill -s 0 "$socat_pid" fails (then you know socat has exited for whatever reason).

0

Thanks Kamil for your explanation. I ended up fixing it by monitoring the status of the socat pid to become 'S'.

Here's my fixed code.

# 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>&-
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.