Bash and Networking

Bash is one of these gems in the computer world that do not require reading manual for most of the basic use. But extensive reading can lead to expertise and such people tend to prefer bash for tasks of increasing complexity whereas others would have already turned to higher level programming languages.

One such case is networking. How many people know that you can do basic socket I/O with bash? One of such people is my coworker, who pointed this out for me.

Pseudo device for network I/O

If you take input from or redirect output to specially crafted non-existant device node, bash opens connection to specified host and transmits the data over network. Bash manual explains:

/dev/tcp/host/port
If  host  is  a valid hostname or Internet address, and port is an integer port number or service
name, bash attempts to open a TCP connection to the corresponding socket.
/dev/udp/host/port
If host is a valid hostname or Internet address, and port is an integer port  number  or  service
name, bash attempts to open a UDP connection to the corresponding socket.

Simple use case: netcat

The most basic usage would be in a situation where you need to use netcat, but you do not have it nor have you permission to install it. But you do have bash. So to get input from other host:

user@host1> cat < /dev/tcp/host2.example.com/1234
This is input from host2

Or to output to other host, simply

user@host1> echo "Output this" > /dev/tcp/host2.example.com/1234

Advanced use case: full I/O to HTTP server

There might be situations where a bash script would benefit from ability to communicate with a full-blown HTTP server e.g. to receive input to be parsed or post it’s results to a webpage. Though, talking to HTTP server requires two-way channel between peers and both input and output to that channel, which is a bit tricky in bash, but nonetheless achievable.

This example shows how to GET a file list from HTTP server

#!/bin/bash

SERVER="host2.example.com"
 PORT="80"
 PATH="/filelist.txt"

# Set up filedescriptor so that it can be read and written to
 # Note: the number can be anything, as long as it is not in use already
 #       which means this should be greater than 2
 exec 3<>/dev/tcp/$SERVER/$PORT

# Submit query
 echo "GET $PATH HTTP/1.1
 host: $SERVER

" >&3

# Parse the result
 while read line <&3 do   # output to stdout   echo $line >&1
 done

# close the input
 exec 3<&-
 # close the output
 exec 3>&-

I leave it to the reader to implement header parsing to output only HTTP body and to handle persistent connections correctly (i.e. read Content-Length header and act upon it). Above script would wait until the server closes the connection on timeout or the user terminates the script.

Afterthougths

Though one might argue that what is the use of all this when we do have other programming languages with full support of HTTP and no need to parse it all ourselves. Still occasions might arise where we do not have one or the other required library available or existing code is written in bash and porting it to other language just for simple networking seems unreasonable.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s