There is a little known feature of Bash that allows a programmer to send and receive data from a device socket that you can create in Bash. This is especially handy if you have a service running on your system that is listening for data on a port, and a shell script that handles another function, you can communicate between your shell script and your service via the local loopback bound with this socket. Another trick allows you to send a GET request from this Bash socket to a website much like wget does and retrieve the HTML code of that page using Bash redirection.
Create Socket In Bash
To create a socket in Bash we take advantage of the Bash exec command, and redirection. To open a bidirectional udp socket to a specific IP address and specific port we use the /dev/udp/ip/port syntax. So for example, if we wanted to send to the loopback device on port 12345, we use /dev/udp/127.0.0.1/12345 as our device. We then use exec and redirection on this path to create the socket. We use file handles 3 or higher, as mentioned in the earlier redirection article, stdin = 0 and stdout = 1 and stderr = 2 and thus we have to use 3 or higher. Putting it all together:
exec 3<> /dev/udp/127.0.0.1/12345
We can now direct data to the 3 file handle much like directing data to /dev/null or standard out. Lets verify that the socket was made using netstat.
$ netstat -u
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
udp 0 0 localhost:40834 localhost:12345 ESTABLISHED
Okay, we definitely have a socket open, lets send data to it.
echo "hi" >&3
This will send the string “hi” to the local loopback at port 12345, so if you had a C service listening for the string, it could act upon the receipt of that traffic. Lets watch this in action. I have a tcpdump session running in another terminal to grab the data being sent.
# tcpdump -i lo -n -vv -s 256 -X
tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 256 bytes
15:21:13.613402 IP (tos 0x0, ttl 64, id 64305, offset 0, flags [DF], proto UDP (17), length 31)
127.0.0.1.40834 > 127.0.0.1.12345: [bad udp cksum 91c1!] UDP, length 3
0x0000: 4500 001f fb31 4000 4011 419a 7f00 0001 E....1@.@.A.....
0x0010: 7f00 0001 9f82 3039 000b fe1e 6869 0a ......09....hi.
Well look at that! At the very end of line 6, you can see the “hi” being sent to 127.0.0.1 at port 12345.
Nifty! To create a tcp socket you simply replace the /udp/ with the /tcp/ and voila, TCP socket made. Of course if you plan to use TCP then you have to take into account the handshake procedure and listening for the returned data.
Close Socket File Handle
To close the file handle after use execute the commands:
exec 3<&-
exec 3>&-
This is important to do, as leaving this handle open has potential as a method of attack on the system.
Send Get Request To Google With Bash Socket
Another cool trick using the Bash socket is to create your socket for a website rather than strictly an IP address. In doing so, we can send a GET request to the web server and wait for a reply back. The commands to achieve this are:
$ exec 3<>/dev/tcp/www.google.com/80
$ echo -e "GET / HTTP/1.1\n\n" >&3
$ cat <&3
HTTP/1.1 302 Found
Location: http://www.google.ca/
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Set-Cookie: PREF=ID=c0937b14e3b5e4a2:FF=0:TM=1304375672:LM=1304375672:S=qvrh_IjuRcStbI-i; expires=Wed, 01-May-2013 22:34:32 GMT; path=/; domain=.google.com
Date: Mon, 02 May 2011 22:34:32 GMT
Server: gws
Content-Length: 218
X-XSS-Protection: 1; mode=block
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="http://www.google.ca/">here</A>.
</BODY></HTML>
Sweet! I found that nifty example here. With some other great comments from other users.
There you have it, there are many uses for Bash sockets when your system may not have netcat.