Search code examples
bashsocketsfile-descriptorio-redirection

Broken pipe using "echo" command on a file descriptor holding a TCP connection


After reading article https://www.xmodulo.com/tcp-udp-socket-bash-shell.html, I tried to write a simple script:

#!/bin/bash

exec 3<>/dev/tcp/time.nist.gov/13

for i in {1..3}; do
    echo -e "GET / HTTP/1.1\r\nhost: service\r\nConnection: close\r\n\r\n" >&3
    cat <&3
    echo "get sent"
    sleep 10
done
exec 3<&-
echo "done"

It successfully returned me the time but then only "Bad file descriptor".

But if I move the opening and the opening of file descriptor 3, it runs fine

#!/bin/bash

for i in {1..3}; do
    exec 3<>/dev/tcp/time.nist.gov/13
    echo -e "GET / HTTP/1.1\r\nhost: service\r\nConnection: close\r\n\r\n" >&3
    cat <&3
    echo "get sent"
    exec 3<&-
    sleep 10
done

echo "done"

Why is that?

(I'd prefer to keep the TCP socket open without closing and re-opening it.)


Solution

  • Port 13 is the daytime protocol, which is not HTTP. Thus sending a HTTP request to the server is wrong in the first place. Instead one must follow the specification of the daytime protocol - see Wikipedia: Daytime Protocol for an overview.

    Basically the protocol works by doing a connect to the server and reading until connection close - the client does not send any application data at all. Since one must read until connection close and there is no concept of a "request" one also cannot keep the connection open for more "requests". Instead a new connection must be created for each new query.

    for i in {1..3}; do
        exec 3<>/dev/tcp/time.nist.gov/13
        cat <&3
        exec 3<&-
        sleep 10
    done
    echo "done"
    

    But even if the other side would be an HTTP server the tried approach will not work. For one: the HTTP request explicitly asks for Connection: close, thus excluding multiple requests on the same connection. And, the cat <&3 reads until connection close by the server instead of HTTP response end, which makes getting multiple responses on the same connection and thus also multiple requests impossible too.