Skip to content

Instantly share code, notes, and snippets.

@qguv
Created December 2, 2019 13:30
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save qguv/b7c602879e4ba3c58ccca96a7a305198 to your computer and use it in GitHub Desktop.
Save qguv/b7c602879e4ba3c58ccca96a7a305198 to your computer and use it in GitHub Desktop.
shudder's guide to network programming in bash (Mirror of broken http://shudder.daemonette.org/source/BashNP-Guide.txt)
shudder's guide to network programming in bash
==============================================
1. What's this?
I'm kind of a maniac when it's about doing nifty things with
tools not originally designed for that.that's why I wrote the "guide".
maybe there are some other resources that document this (not
counting the bash manual page) but I wanted to express my own
impressions on the possibilities of bash.
2. What's bash?
err...if you really don't know what's bash, then maybe you shouldn't
read the rest of the document.
3. Aren't you going to start?
so as I wrote in the beginning I'll document the part of bash
where you could do some sort of network programming (or sort of).
why writing a complex program in C (perl is out of the question ;)
to open a socket to pass some data through it when all this could
be done in a few lines with the good old bash? (don't answer that)
actually sometimes it's better to do that in C but let me show you
the alternative.
let's start from the beginning.
for a program to read and write, it should use some technique to
deal with several streams that point to different resources.
to do that a "file descriptor" is used when this is needed.
a descriptor is just an integer that correspondents to the opened stream.
when a program is started three descriptors are opened for I/O.they are:
0 - input
1 - output
2 - error (diagnostic) output
now if we want to write something on the screen we could use
descriptors 1 or 2.we can look what descriptors are open in a running
program by checking that in /proc/<pid>/fd.
the diagnostic output is used by programs to distinguish the normal
messages from the error messages.for example if we have a file
called blah in our current working directory and do a `ls blah'
the result will be printed in the standard output.but if there's no
file by that name the result will be something like:
ls: blah: No such file or directory
and it will be printed using the standard error.
we can check that by redirecting the output.as we know (or maybe not?)
bash can redirect streams with a simple techniques.here's what the man
page of bash says about redirection:
Redirecting Input
Redirection of input causes the file whose name results
from the expansion of word to be opened for reading on
file descriptor n, or the standard input (file descriptor
0) if n is not specified.
The general format for redirecting input is:
[n]<word
Redirecting Output
Redirection of output causes the file whose name results
from the expansion of word to be opened for writing on
file descriptor n, or the standard output (file descriptor
1) if n is not specified. If the file does not exist it
is created; if it does exist it is truncated to zero size.
The general format for redirecting output is:
[n]>word
let's do this again.we suppose that there's a file named blah in the
current working directory:
$ ls blah
blah
and there's no file named whop:
$ ls whop
ls: whop: No such file or directory
so the next should be correct:
$ ls blah whop
ls: whop: No such file or directory
blah
$ ls blah whop 2> /dev/null
blah
$ ls blah whop 1> /dev/null
ls: whop: No such file or directory
$ ls blah whop &> /dev/null
$
to redirect one descriptor to another we should use:
[n]>&word and [n]<&word
to close one:
[n]<&-
everything should be clear now.except for the &> redirection.it redirects
both output and error messages.read the bash manual for more details.
now as we know how to redirect (at least the basis) let's learn how to
open streams.the form is:
[n]<> word
where n is the number of the new descriptor and word is the name of the file.
lets open a stream to the file whop and write something in it:
$ exec 3<> whop
$ echo "hello" 1>&3
$ exec 3<&-
exec is used for the opening and closing to be in the current shell
in the second line I'm redirecting the standard output of `echo' to the
descriptor 3 which points to the newly created file - whop
now lets read what we have written:
$ exec 3<> whop
$ read 0<&3
$ echo $REPLY
hello
$ exec 3<&-
voila.it's simple, right? but, where's the network part of all of this?!
bash handles two special "files" which are used to open network sockets.
the manual page of bash says:
/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.
well actually you'll not going to find directories named /dev/tcp and
/dev/udp (unless you create them, but don't).they are valid only
in bash.let's suppose that you have port 80 (www) open.
we could improvise a simple web browser in bash by opening a tcp connection
like this:
$ exec 3<> /dev/tcp/127.0.0.1/80
now descriptor 3 points to port 80 of our machine (if you're connected to
the Internet you can open sockets to remote hosts by simply providing
their hostname/address and port)
lets send the http request:
$ echo "GET /index.html HTTP/1.0" 1>&3
$ echo 1>&3
now the result is waiting for us to get it:
$ while read 0<&3; do echo $REPLY; done
this reads up a line until EOF and prints it on screen
now I suppose you know what `network programming in bash' is.
but if you're new to it I recommend you to read the manual page first
then guides like this.
4. Network programming in bash is cool.Now write something useful!
this is a very simple irc bot.all it does is to stay in a channel and
keep the connection to the server open.feel free to modify this script to do
something better (see rfc1459 for more information on IRC)
#!/bin/bash
#
# configuration
nick="`basename $0`$$" # nickname
name="$0 $*" # real name
chan="#sh #bash" # channels to join
mode="+i" # irc mode
# end of configuration
# use our login name if there's no nickname
nick="${nick:-$USER}"
# see if we can find a realname for our nickname
name="${name:-`grep $nick /etc/passwd | cut -d : -f 5`}"
host="$1"
port="$2"
# redirect error messages to file `irc-errors'
exec 3<> irc-errors 2>&3-
if [ ! "$2" ]; then
echo "usage: `basename $0` [hostname] [port]"
exit 1
fi
# try to connect
if ! exec 3<> /dev/tcp/$host/$port; then
echo "`basename $0`: unable to connect to $host:$port"
exit 1
fi
# duplicate standard input and output with the newly created socket
exec 0<&3 1>&3-
# register to the server
echo "USER $nick ${mode:-+iw} $nick :$name"
echo "NICK $nick"
# join channels
for c in $chan; do echo "JOIN $c"; done
while read; do
set -- ${REPLY//$'\r'/}
# answer the critical ping request
# otherwise the server will disconnect us
[ "$1" == "PING" ] && echo "PONG $2"
# your code should go here
done
exec 1<&- 2<&-
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment