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.

Logan Lee
  • 249

2 Answers2

2

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

Logan Lee
  • 249