Network programing with bash

In my last article I showed how to use file descriptors in shell scripts. If your shell happens to be bash you can even have a file descriptor map to a network socket.

When executing a command on a /dev/tcp/$host/$port pseudo-device, Bash opens a TCP connection to the associated socket.

Exemple using TCP:
cat </dev/tcp/nist1.symmetricom.com/13
54496 08-01-31 02:30:53 00 0 3 345.7 UTC(NIST) *

cat </dev/tcp/nist1.symmetricom.com/13
54496 08-01-31 02:30:53 00 0 3 345.7 UTC(NIST) *


Exemple using UDP:
$ exec 4<>/dev/udp/172.16.2.200/7
$ echo -e "Networking with bash" >&4 && head -1 <&4
Networking with bash
$ exec 4>&-
$ exec 4<&-

$ exec 4<>/dev/udp/172.16.2.200/7
$ echo -e "Networking with bash" >&4 && head -1 <&4
Networking with bash
$ exec 4>&-
$ exec 4<&-

In this example I open a datagram (UDP) connection to my home server (172.16.2.200) on the echo service (port 7). The file descriptor is open in read/write mode.

An almost useful program


This is an example of a small stripped down version of wget (yes very stripped down) written in bash.

The line `exec 6<>/dev/tcp/$1/80` opens a socket in read write mode. Since we are using HTTP protocol 1.1 we need to pass the host name in the header with the line `Host: $1`. Also by default HTTP version 1.1 keeps all the connections alive unless specified otherwise. The line `Connection: close` tells the http server to close the connection once the response has been sent. I we forget this line the script will stay connected until the server times out.
#!/bin/bash
#
PATH=${2-/}

exec 6<>/dev/tcp/$1/80

echo -e "GET ${PATH} HTTP/1.1" >&6
echo -e "Host: $1\nConnection: close\n" >&6
echo "Reading: http://${1}${PATH}" >&2

#read and discard the header
while read <&6
do
    LINE=${REPLY//$'\r'/}
    if [ -z "$LINE" ]; then
	break
    fi
done

#read the page
while read <&6
do
    echo -n $REPLY >&1
done
# close the file descriptor
exec 6<&-
exec 6>&-

#!/bin/bash
#
PATH=${2-/}

exec 6<>/dev/tcp/$1/80

echo -e "GET ${PATH} HTTP/1.1" >&6
echo -e "Host: $1\nConnection: close\n" >&6
echo "Reading: http://${1}${PATH}" >&2

#read and discard the header
while read <&6
do
LINE=${REPLY//$'\r'/}
if [ -z "$LINE" ]; then
break
fi
done

#read the page
while read <&6
do
echo -n $REPLY >&1
done
# close the file descriptor
exec 6<&-
exec 6>&-


This program takes two arguments, the server name and the path to the file to download.
$ ./wget.sh www.google.com '/search?q=bsd' > ~/result.html
Reading: http://www.google.com/search?q=bsd
$

$ ./wget.sh www.google.com '/search?q=bsd' > ~/result.html
Reading: http://www.google.com/search?q=bsd
$

There is one caveat though. The version of bash you are using needs to be compiled with the flag --enable-net-redirections. This code should work on Mac, *BSD and most of the linux distributions (some Debian by default use a version of bash without net redirection). I don't know about the bash version running on Windows. The last version of windows I ever used was Windows 95.
 

Leave a message

(Required)
(Required and not displayed)
(Optional)
obfuscated letters Enter the text shown in the image