help-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: How to know /dev/tcp/host/port is closed?


From: Leonid Isaev (ifax)
Subject: Re: How to know /dev/tcp/host/port is closed?
Date: Thu, 1 Apr 2021 01:06:48 +0000

On Wed, Mar 31, 2021 at 04:17:01PM -0500, Peng Yu wrote:
> See the following code. When the first printf is finished, the
> connection should have been closed. But it seems that bash does not
> know it (see the exit status code of { true >&"$fd"; } 2>/dev/null).
> Is it a bug?

No, it's not a bug, but a feature, called "half-closed" TCP sockets.

Let's pick the client-server conversation off-wire with tshark(1) (starting
from the end of your {...}:

> } {fd}<>/dev/tcp/httpbin.org/80

Here we have a TCP handshake:
-----8<-----
 25          2 192.168.122.43 -> 54.166.163.67 TCP 74 59392 > http [SYN] Seq=0 
Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=1043835330 TSecr=0 WS=64
 26          2 54.166.163.67 -> 192.168.122.43 TCP 74 http > 59392 [SYN, ACK] 
Seq=0 Ack=1 Win=26847 Len=0 MSS=1460 SACK_PERM=1 TSval=781007956 
TSecr=1043835330 WS=256
 27          2 192.168.122.43 -> 54.166.163.67 TCP 66 59392 > http [ACK] Seq=1 
Ack=1 Win=29248 Len=0 TSval=1043835340 TSecr=781007956
----->8-----
(54.166.163.67 is one of IPs httpbin.org resolves to, and 192.168.122.43 is
my test VM)

> $ {
>     header=(
>         'GET /get HTTP/1.1'
>         'Host: httpbin.org'
>         'Connection: close'
>     )
>     builtin printf '%s\r\n' "${header[@]}" '' >&"$fd"

Now, we send data, and the server replies (frame 38):
-----8<-----
 30          2 192.168.122.43 -> 54.166.163.67 TCP 85 [TCP segment of a 
reassembled PDU]
 35          2 54.166.163.67 -> 192.168.122.43 TCP 66 http > 59392 [ACK] Seq=1 
Ack=20 Win=26880 Len=0 TSval=781007960 TSecr=1043835346
 36          2 192.168.122.43 -> 54.166.163.67 HTTP 106 GET /get HTTP/1.1 
 37          2 54.166.163.67 -> 192.168.122.43 TCP 66 http > 59392 [ACK] Seq=1 
Ack=60 Win=26880 Len=0 TSval=781007961 TSecr=1043835353
 38          2 54.166.163.67 -> 192.168.122.43 HTTP 490 HTTP/1.1 200 OK  
(application/json)
----->8-----

But since we sent "Connection: close", the server closes it's part of TCP
connection with a FIN+ACK:
-----8<-----
 39          2 54.166.163.67 -> 192.168.122.43 TCP 66 http > 59392 [FIN, ACK] 
Seq=425 Ack=60 Win=26880 Len=0 TSval=781007962 TSecr=1043835353
 40          2 192.168.122.43 -> 54.166.163.67 TCP 66 59392 > http [ACK] Seq=60 
Ack=425 Win=30272 Len=0 TSval=1043835362 TSecr=781007962
----->8-----

At this point, it won't send you anymore data, but you can still write to
socket.

>     IFS= read -d '' -r -t 3 response <&"$fd"
>     builtin printf '>>>%s\n' "$response"

We read json returned from server.

>     { true >&"$fd"; } 2>/dev/null
>     echo ">>>$?"

This will succeed because you keep writing to the socket.

>     builtin printf '%s\r\n' "${header[@]}" '' >&"$fd"

This too.

>     IFS= read -d '' -r -t 3 response <&"$fd"

This returns nothing because EOF is reached. In fact, this is your indication
that the other side closed connection. If this wasn't the case, read (or
cat(1)) would hang.

>     builtin printf '>>>%s\n' "$response"

Nothing is printf'ed.

> Since the connection is closed in the middle of the code block, is {
> ... } {fd}<>/dev/tcp/httpbin.org/80 not appropriate to be used in this
> case?

The connection is in half-closed state. You can still write to the socket, but
don't expect to hear anything back.

HTH,

-- 
Leonid Isaev



reply via email to

[Prev in Thread] Current Thread [Next in Thread]