Can bash be used to create a server? - bash

Is there any way to program a server in bash?
Basically I want be able to connect to a bash server from a PHP client and send messages that will be displayed in console.

The bad news first
Unfortunately, there seems to be no hope to do this in pure Bash.
Even doing a
exec 3<> /dev/tcp/<ip>/<port>
does not work, because these special files are implemented on top on connect() instead of bind(). This is apparent if we look at the source.
In Bash 4.2, for example, the function _netopen4() (or _netopen6() for IPv6) reads as follows (lib/sh/netopen.c):
s = socket(AF_INET, (typ == 't') ? SOCK_STREAM : SOCK_DGRAM, 0);
if (s < 0)
{
sys_error ("socket");
return (-1);
}
if (connect (s, (struct sockaddr *)&sin, sizeof (sin)) < 0)
{
e = errno;
sys_error("connect");
close(s);
errno = e;
return (-1);
}
But
It is possible to use a command line tool such as nc. E.g.,
nc -l <port>
will listen for incoming connections on localhost:<port>.

There is a project on GIT which implements a HTTP web server fully written in bash;
https://github.com/avleen/bashttpd

Create a process that reads from a socket, executes the data via shell, and prints back the response. Possible with the following script, which listens on port 9213:
ncat -l -kp 9213 | while read line; do
out=$($line)
# or echo $line
echo $out
done
If all you want is to display the data, ncat -l -p 9213 is sufficient though.

Related

Difference in behavior between shell and script

I have a set of commands that I am attempting to run in a script. To be exact, the lines are
rm tmp_pipe
mkfifo tmp_pipe
python listen_pipe.py &
while [ true ]; do nc -l -w30 7036 >>tmp_pipe; done &
listen_pipe.py is simply
if __name__ == "__main__":
f = open("tmp_pipe")
vals = " "
while "END" not in vals:
vals = f.readline()
if len(vals) > 0:
print(vals)
else:
f = open("tmp_pipe")
If I run the commands in the order shown I get my desired output, which is a connection to an ESP device that streams motion data. The connection resets after 30 seconds if the ESP device leaves the network range or if the device is turned off. The python script continues to read from the pipe and does not terminate when the tcp connection is reset. However, if I run this code inside a script file nc fails to connect and the device remains in an unconnected state indefinitely. The script is just
#!/bin/bash
rm tmp_pipe
mkfifo tmp_pipe
python listen_pipe.py &
while [ true ]; do nc -l -w30 7036 >>tmp_pipe; done &
This is being run on Ubuntu 16.04. Any suggestions are greatly welcomed, I have been fighting with this code all day. Thanks,
Ian

Reply to IRC PING with PONG in bash script

So far I have a basic IRC Bot which I hope to be able to successfully PRIVMSG myself with but on the server I am testing on it requires a PONG response to PING.
I have absolutely no idea how to get around this. How do I reply to the PING?
My current code:
#!/bin/bash
function ircpreamble {
echo "NICK ${1}"
}
function privmsg {
TARGET=$1
sed -re "s/^(.*)\$/PRIVMSG ${TARGET} :\1/"
}
function delay {
while read LINE; do
sleep 2
echo $LINE
done
}
function messages {
msg=`cat pmmsg.txt`
echo $msg
}
function disconnect {
echo "QUIT goodbye :)"
}
(
nick=`cat randnick.txt`
pms=`cat pmnickname.txt`
ircpreamble "$nick";
messages | privmsg "$pms";
disconnect;
) | delay | nc irc.seersirc.net 6667
You first need to "catch" the server responses. This can be done like:
$ nc irc.seersirc.net 6667 |while read res;do echo "==>$res";done
==>:irc.seersirc.net NOTICE AUTH :*** Looking up your hostname...
==>:irc.seersirc.net NOTICE AUTH :*** Couldn't resolve your hostname; using your IP address instead
Since now you catch the response in a variable, you can filter out these responses and send the appropriate commands back to the server.
The problem is that when running netcat/telnet from a script a simple echo "PONG" is not sending the messages back to server but echo prints the messages locally in your terminal.
To send messages to the server you need either to pipe those messages to netcat in the beginning (the technique you already use) or to use some kind of expect script or to use a file to feed the netcat.
Regarding the PONG response, note that in order pong to be accepted by the server it must include the message sent by the server along with it's ping request.
Your server sends something like PING :D7AA1D1D (different every time) and thus the correct pong response is PONG :D7AA1D1D
As a result you can not just include a pong response in the first messages send in the beginning to netcat, since you don't know what is the ID that your server will sent to you along with the ping request.
This is a working draft script using a file to continuously feed the netcat:
rm .ircbot
touch .ircbot
prmnick="gv"
tail -f .ircbot |nc irc.seersirc.net 6667 |while read res
do
echo "==>$res"
if [[ "$res" == *"Couldn't resolve your hostname; using your IP address instead"* ]];then
sleep 2
echo "NICK gvgv" >>.ircbot
tail -n1 .ircbot #used just to display the last line of the helper file in my screen.
elif [[ "$res" == *"PING"* ]]; then
sleep 2
echo "$res" |sed 's/PING/PONG/' >>.ircbot
tail -n1 .ircbot
sleep 2
echo "USER gvgv 8 * :gvgv " >>.ircbot
tail -n1 .ircbot
sleep 2
echo "PRIVMSG $prmnick : hello from bot" >>.ircbot
tail -n1 .ircbot
fi
done
Tip: By opening a second terminal , you can manually "control" above bot by sending more commands to the .ircbot file (i.e $ echo "JOIN #channel" >>.ircbot) which will be also fed to netcat.
By the way, some web search about bash irc bots will return some useful results.
This is one easy bash script to use as an irc bot: https://github.com/Newbrict/bash-irc-bot/blob/master/bot.sh
Also , i found this useful IRC Over Telnet guide: http://archive.oreilly.com/pub/h/1963
Finally , this is an alternative using the /dev/tcp directly : https://gist.github.com/Wollw/3330337

wget script to connect, wait if successful, continue if no connection

I have to create a script that sends a wget contact message to all devices on our network. wget connects to a url and this trigger the endpoint to contact a server.
The problem I have is I need to be able to send the command to each IP address on the network and if the connection is successful do noting for 30 seconds then move on to the next url in the list. However if the connection isn't successful I want the script to move on to the next url with no pause.
Currently I'm using a bash script to send the command with a pause=30 in-between url's, connection attempts set to 1 and time-out set to 1. this works OK for the connections that are successful but it also hangs on the addresses that are not.
Any advise on how I can pause on success and move on after time out on dead addresses?
This is the command I'm currently running,
wget --connect-timeout=1 --tries=1 http://xx.xx.xx.xx:8085/contact_dls.html/ContactDLS
sleep 30
wget --connect-timeout=1 --tries=1 http://xx.xx.xx.xx:8085/contact_dls.html/ContactDLS
etc etc etc
thanks
You don't need wget for this task - everything can be done in Perl.
Simply use code like this:
use LWP::UserAgent;
use HTTP::Request;
my $ua = new LWP::UserAgent;
$ua->timeout(1);
my $req = new HTTP::Request(GET => $url);
my $res = $ua->request($req);
if ($res->is_success()) {
print("Connection to '$url' was OK");
sleep(30);
} else {
print("Cannot access '$url'");
sleep(1);
}
This will hit your url, but will timeout in just 1 second.
I would probably load the urls into an array and iterate through the array. Something like this
#!/usr/bin/perl
use warnings;
use strict;
my #urls = qw(test.com url.com 123abc.com fail.aa);
foreach my $url (#urls){
my $check = `wget --server-response $url 2>&1 | grep HTTP/ | awk '{print \$2}'`;
#if there is a successful response you can set the sleep(3) to sleep(30) for your instance.
if($check){
print "Found $url -- sleeping 3 seconds\n";
sleep(3);
}
else{
print "$url does not exist\n";
#If the url does not exist or respond it will move on to the next item in the array. You could add a sleep(1) before the next for a 1 second pause
next;
}
}
Of course this is assuming that you are using linux. The urls could be loaded another way as well, I don't know how your current script it getting them. The above is an example and you of course would need to be adjusted to fit your environment

Not able to connect to socket using socat

I am trying to parse rsyslog logs. For this i am sending all my logs to socat which is then sending them to Unix Domain Socket. That socket is created via perl script which is listening on that socket to parse logs.
My bash script to which rsyslog is sending all log is
if [ ! `pidof -x log_parser.pl` ]
then
./log_parser.pl & 1>&1
fi
if [ -S /tmp/sock ]
then
/usr/bin/socat -t0 -T0 - UNIX-CONNECT:/tmp/sock 2>> /var/log/socat.log
fi
/tmp/sock is created using perl script log_parser.pl which is
use IO::Socket::UNIX;
sub socket_create {
$socket_path = '/tmp/sock';
unlink($socket_path);
$listner = IO::Socket::UNIX->new(
Type => SOCK_STREAM,
Local => $socket_path,
Listen => SOMAXCONN,
Blocking => 0,
)
or die("Can't create server socket: $!\n");
$socket = $listner->accept()
or die("Can't accept connection: $!\n");
}
socket_create();
while(1) {
chomp($line=<$socket>);
print "$line\n";
}
There is this error i am getting from socat which is
2015/02/24 11:58:01 socat[4608] E connect(3, AF=1 "/tmp/sock", 11): Connection refused
I am no champion in sockets so i am not able to understand what is this. Please help. Thanks in advance.
The main issue is that when i kill my perl script then bash script is suppose to call it again and start it.
What actually happening is that sript is started but socat is not started instead it give this error and never start.
I can duplicate your error if I don't run your perl program before trying to use socat. Here is what works for me:
1) my_prog.pl:
use strict;
use warnings;
use 5.016;
use Data::Dumper;
use IO::Socket::UNIX;
my $socket_path = '/tmp/sock';
unlink $socket_path;
my $socket = IO::Socket::UNIX->new(
Local => $socket_path,
Type => SOCK_STREAM,
Listen => SOMAXCONN,
) or die "Couldn't create socket: $!";
say "Connected to $socket_path...";
my $CONN = $socket->accept()
or die "Whoops! Failed to open a connection: $!";
{
local $/ = undef; #local -> restore previous value when the enclosing scope, delimited by the braces, is exited.
#Setting $/ to undef puts file reads in 'slurp mode' => whole file is considered one line.
my $file = <$CONN>; #Read one line.
print $file;
}`
2) $ perl my_prog.pl
3) socat -u -v GOPEN:./data.txt UNIX-CONNECT:/tmp/sock
The -u and -v options aren't necessary:
-u Uses unidirectional mode. The first address is only used for
reading, and the second address is only used for writing (exam-
ple).
-v Writes the transferred data not only to their target streams,
but also to stderr. The output format is text with some conver-
sions for readability, and prefixed with "> " or "< " indicating
flow directions.
4) You can also do it like this:
cat data.txt | socat STDIN UNIX-CONNECT:/tmp/sock
Pipe stdout of cat command to socat, then list STDIN as one of socat's files.
Response to comment:
This bash script works for me:
#!/usr/bin/env bash
echo 'bash script'
../pperl_programs/my_prog.pl &
sleep 1s
socat GOPEN:./data.txt UNIX-CONNECT:/tmp/sock
It looks like the perl script doesn't have enough time to setup the socket before socat tries to transfer data.

Script that open telnet connection and send message via socket to a server

I must do a script that run a connection telnet and send to a server two numbers. I am sure that server work because if I send values directly from telnet everything works correctly. If I run the script that do exactly what I do something does not work. I can see client has been connected, but I don't see on screen the numbers that should be had sent via telnet.
Here the script (really simple):
{ echo "3"; echo "6"; sleep 1; } | telnet localhost 8887
and part of the server :
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
server.sin_port = htons(8887);
...
//bind and listen
...
printf("connesso\n");
//Riceve un emssaggio dal client
for(i=0; i<2;) {
bzero(client_message,2000);
if((read_size = recv(client_sock , client_message , 2000 , 0)) > 0)
{
//Rimanda indietro il messaggio al client
write(client_sock , client_message , strlen(client_message));
numeri[i]=atoi(client_message);
i++;
}
}
...
// some check
...
if(numeri[1] % numeri[0] == 0) {
printf("multiplo\n");
}
else {
printf("negativo\n");
}
So, I should see on server some messages:
"Connesso"
"multiplo" or "negativo"
But i see just "Connesso"... It's like i do not receive nothing.. Obviously i see "connesso" after I run my script.
Any idea?
Thanx
This code will work
{ echo "3"; echo "6"; sleep 1; } | telnet localhost 8887
How I tested it? Install netcat. Then from one terminal
nc -l 8887
And from another terminal type
{ echo "3"; echo "6"; sleep 1; } | telnet localhost 8887
You will see the number 3 and 6 in the first terminal running nc

Resources