Created
December 2, 2019 13:30
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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