Unix : Epoll, catch ctrl+d and ctrl+c in server - sockets

I use epoll to build a server, this is the code where I init epoll :
core->fd_epoll = epoll_create(LIMIT_CLIENT);
ev.events = EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP;
ev.data.fd = core->socket_main;
epoll_ctl(core->fd_epoll, EPOLL_CTL_ADD, core->socket_main, &ev);
while (1)
{
nfds = epoll_wait(core->fd_epoll, &ev, 90000, -1);
...
}
And when I use it to check if there's something new on my fds :
for (i = 0; i < nfds; i++)
{
fd = ev[i].data.fd;
if (fd == core->socket_main)
{
socket_fils = socket_accept(core->socket_main, 0);
event.data.fd = socket_fils;
event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
xepoll_ctl(core->fd_epoll, EPOLL_CTL_ADD, socket_fils, &event);
printf("Incoming => FD fils %d\n", socket_fils);
}
else
printf("Event %x\n", ev[i].events);
}
When I use netcat to send a message to the server the bitfield events is equal to 1 (EPOLLIN)
When I send a ctrl+c, netcat quits and my bitfield is equal to 2001 (EPOLLIN and EPOLLRDHUP)
When I send a ctrl+d, netcat doesn't quit but my bitfield is equal to 2001 too...
After a ctrl+d, my server closes the socket. It's not normal... A ctrl+d should'nt close the socket and return a different bitfield.
How can I know, in the server, if it's ctrl+c or ctrl+d ?
Thank you.

ctrl+c and ctrl+d keypresses on the terminal that is running netcat cannot be "seen" directly by your server. They cause, respectively, a SIGINT signal to be sent to netcat, and an EOF condition to be seen by netcat on its stdin. What netcat does with that is really up to netcat, not up to your server. Here's what they do for me:
ctrl+c which sends SIGINT to netcat: netcat is killed because that is the default action of SIGINT, and netcat doesn't change it. When netcat dies the socket is automatically closed. The server senses this as available incoming data, consistent with the EPOLLIN|EPOLLRDHUP condition you are seeing. If you read the socket, you will find that an EOF is waiting for you.
ctrl+d which sends an EOF on netcat's stdin: netcat noticed the EOF. It will send no further data through the socket. However, it continues running and reading from the socket in case the server has more data to send.
In other words, I can't reproduce the netcat behaviour you are seeing (with Linux 2.6 and netcat v1.10-38). Perhaps your version of netcat shuts down the socket for writing after reading an EOF on stdin?

Related

Minimal TCP client-server: accept() never receives a connection?

I copied the example code for the book TCP/IP Sockets in C from http://cs.ecs.baylor.edu/~donahoo/practical/CSockets/winsock.html and got it to compile on MinGW (without warnings by changing clntLen from unsigned int to int and void main to int main).
$ gcc.exe -Wall -o TCPEchoServerWS TCPEchoServerWS.c HandleTCPClientWS.c DieWithErrorWS.c -lws2_32
$ gcc.exe -o TCPEchoClientWS TCPEchoClientWS.c DieWithErrorWS.c -lws2_32
When I run the executables the server but not the client triggers a Windows firewall notification.
$ ./TCPEchoServerWS.exe 5000
inside for loop
$ ./TCPEchoClientWS.exe 169.1.1.1 "Echo this" 5000
connect() failed: 10060
The from printf debugging
for (;;) /* Run forever */
{
printf("inside for loop");
clntLen = sizeof(echoClntAddr);
if ((clntSock = accept(servSock, (struct sockaddr *) &echoClntAddr, &clntLen)) < 0)
DieWithError("accept() failed");
printf("Handling client %s\n", inet_ntoa(echoClntAddr.sin_addr));
it appears that the accept() never returns. I assume this is because it never has a connection to extract?? Any ideas please? I've also tried linking with -lwsock32, and disabling windows firewall.
It turns out that I was using the wrong IP (I just copied the one from the command it the book).
I just needed to use the IPv4 address from ipconfig as Remy suggested.

Julia TCP select

I have one problem with TCP connection.
I have made server like:
server = listen(5000)
sock = accept(server)
while isopen(sock)
yes=read(sock,Float64,2)
println(yes)
end
I want that it will continually print [0.0,0.0] when there is nothing to read, otherwise it will print what it reads from server.
This will go to loop(trying to read something), if there is nothing to read or it crashes.
I try to do this with task like:
begin
server = listen(5000)
while true
sock = accept(server)
while isopen(sock)
yes=read(sock,Float64,2)
println(yes)
end
println([0.0,0.0])
end
end
but this will only print what it reads. I'm making connection with other console and ride through consol:
clientside=connect(5000)
write(clientside,[2.0,2.0])
So I'm trying to make server that prints [0.0,0.0], if there is nothing to read and it will print what it reads when there is something to read.
Any good ideas?
Maybe, one strategy to make the server is to run the accept / print block asynchronously (since the accept call blocks the main thread).
Following the tutorial "Using TCP Sockets in Julia", one way to make the server is:
notwaiting = true
server = listen(5000)
while true
if notwaiting
notwaiting = false
# Runs accept async (does not block the main thread)
#async begin
sock = accept(server)
ret = read(sock, Float64, 2)
println(ret)
global notwaiting = true
end
end
println([0.0, 0.0])
sleep(1) # slow down the loop
end
The variable notwaiting makes the async block runs only once per connection (without it, the server runs a kind of "race condition").
Testing it with two calls to the client program, produces the following output:
C:\research\stackoverflow\EN-US>julia s.jl
[0.0,0.0]
[0.0,0.0]
[0.0,0.0]
[0.0,0.0]
[2.0,2.0]
[0.0,0.0]
[0.0,0.0]
[0.0,0.0]
[2.0,2.0]
[0.0,0.0]
[0.0,0.0]
[0.0,0.0]
tested with Julia Version 0.5.0-rc3+0

How to configure logstash 2.3.3 websocket

I am trying to get logstash 2.3.3 websocket input working.
Logstash: https://download.elastic.co/logstash/logstash/logstash-2.3.3.tar.gz
Websocket Input Plugin for Logstash: https://www.elastic.co/guide/en/logstash/current/plugins-inputs-websocket.html
Websocket server: https://github.com/joewalnes/websocketd/releases/download/v0.2.11/websocketd-0.2.11-linux_amd64.zip
Websocket Client: Chrome Plugin "Simple Web Socket Client"
I am aware of a bug filed last year logstash 1.5.0 and the websocket input plugin. https://github.com/logstash-plugins/logstash-input-websocket/issues/3 I have also received those same error messages, although I can't reproduce them anymore. The following is my current procedure and result. I am hoping that bug has since been fixed and I just can't find the correct config.
First I installed the plugin and confirmed it is listed as installed.
/app/bin/logstash-plugin list | grep "websocket"
Next, I checked that logstash was working with the following config
input {
stdin { }
}
output {
file {
path => "/app/logstash-2.3.3/logstash-log.txt"
}
}
Logstash worked.
/app/logstash-2.3.3/bin/logstash agent --config /app/logstash-2.3.3/logstash.conf
Hello World
The file logstash-log.txt contained:
{"message":"Hello World","#version":"1","#timestamp":"2016-07-05T20:04:14.850Z","host":"server-name.domain.com"}
Next I opened port 9300
I wrote a simple bash script to return some numbers
#!/bin/bash
case $1 in
-t|--to)
COUNTTO=$2
shift
;;
esac
shift
printf 'Count to %i\n' $COUNTTO
for COUNT in $(seq 1 $COUNTTO); do
echo $COUNT
sleep 0.1
done
I started up websocketd pointing to my bash script
/app/websocketd --port=9300 /app/count.sh --to 7
I opened Simple Web Socket Client in Chrome and connected
ws://server-name.domain.com:9300
Success! It returned the following.
Count to 7
1
2
3
4
5
6
7
At this point I know websocketd works and logstash works. Now is when the trouble starts.
Logstash websocket input configuration file
input {
websocket {
codec => "plain"
url => "ws://127.0.0.1:9300/"
}
}
output {
file {
path => "/app/logstash-2.3.3/logstash-log.txt"
}
}
Run configtest
/app/logstash-2.3.3/bin/logstash agent --config /app/logstash-2.3.3/logstash.conf --configtest
Receive "Configuration OK"
Start up websocketd
/app/websocketd --port=9300 /app/logstash-2.3.3/bin/logstash agent --config /app/logstash-2.3.3/logstash.conf
Back in Simple Web Socket Client, I connect to ws://server-name.domain.com:9300. I see a message pop up that I started a session.
Tue, 05 Jul 2016 20:07:13 -0400 | ACCESS | session | url:'http://server-name.domain.com:9300/' id:'1467732248361139010' remote:'192.168.0.1' command:'/app/logstash-2.3.3/bin/logstash' origin:'chrome-extension://pfdhoblngbopfeibdeiidpjgfnlcodoo' | CONNECT
I try to send "hello world". Nothing apparent happens on the server. After about 15 seconds I see a disconnect message in my console window. logstash-log.txt is never created.
Any ideas for what to try? Thank you!
UPDATE 1:
I tried putting the following in a bash script called "launch_logstash.sh":
#!/bin/bash
exec /app/logstash-2.3.3/bin/logstash agent --config /app/logstash-2.3.3/logstash.conf
Then I started websocketd like so:
/app/websocketd --port=9300 /app/logstash-2.3.3/bin/launch_logstash.sh
Same result; no success.
Upon reading the websocketd documentation more closely, it sends the data received on the socket to a program's stdin. I was trying to listen to a socket in my logstash config, but the data is actually going to that app's stdin. I changed my config to this:
input {
stdin { }
}
output {
file {
path => "/app/logstash-2.3.3/logstash-log.txt"
}
}
Then launched websocketd like this:
/app/websocketd --port=9300 /app/logstash-2.3.3/bin/logstash agent --config /app/logstash-2.3.3/logstash.conf
So in short, until logstash-websocket-input implements their server option, stdin{} and stdout{} are the input and output if using websocketd as the web server.

How to change IP address using perl (something like ipconfig release/renew)

I am beginner to Perl and I have to write a script which could change the IP address after every 1 hour.
I want to change it because I receive some data from a dongle from a website and that website has some time limit to receive that data, so currently we need to unplug the connecting device and use another to
change IP address. (I mean I have to request DHCP for another IP.)
But currently I am asked to write a script using Perl. Could someone please help me how to do that?
My try to do it is :
#!/usr/bin/perl
# Simple DHCP client - sending a broadcasted DHCP Discover request
use IO::Socket::INET;
use Net::DHCP::Packet;
use Net::DHCP::Constants;
use POSIX qw(setsid strftime);
# sample logger
sub logger{
my $str = shift;
print STDOUT strftime "[%d/%b/%Y:%H:%M:%S] ", localtime;
print STDOUT "$str\n";
}
logger("DHCPd tester - dummy client");
logger("Opening socket");
$handle = IO::Socket::INET->new(Proto => 'udp',
Broadcast => 1,
PeerPort => '67',
LocalPort => '68',
PeerAddr => '255.255.255.255',
)
|| die "Socket creation error: $#\n"; # yes, it uses $# here
# create DHCP Packet DISCOVER
$discover = Net::DHCP::Packet->new(
Xid => 0x12345678,
DHO_DHCP_MESSAGE_TYPE() => DHCPDISCOVER(),
DHO_VENDOR_CLASS_IDENTIFIER() => 'foo',
);
logger("Sending DISCOVER to 127.0.0.1:67");
logger($discover->toString());
$handle->send($discover->serialize())
or die "Error sending:$!\n";
logger("Waiting for response from server");
$handle->recv($buf, 4096) || die("recv:$!");
logger("Got response");
$response = new Net::DHCP::Packet($buf);
logger($response->toString());
# create DHCP Packet REQUEST
$request = Net::DHCP::Packet->new(
Xid => 0x12345678,
Ciaddr => $response->yiaddr(),
DHO_DHCP_MESSAGE_TYPE() => DHCPREQUEST(),
DHO_VENDOR_CLASS_IDENTIFIER() => 'foo',
DHO_DHCP_REQUESTED_ADDRESS() => $response->yiaddr(),
);
logger("Sending REQUEST to 127.0.0.1:67");
logger($request->toString());
$handle->send($request->serialize())
or die "Error sending:$!\n";
logger("Waiting for response from server");
$handle->recv($buf, 4096) || die("recv:$!");
logger("Got response");
$response = new Net::DHCP::Packet($buf);
logger($response->toString());
It's output on terminal is :
C:\shekhar_Axestrack_Intern\IpAddressChangeScripts>test6.pl
[08/Jan/2015:18:01:01] DHCPd tester - dummy client
[08/Jan/2015:18:01:01] Opening socket
[08/Jan/2015:18:01:01] Sending DISCOVER to 127.0.0.1:67
[08/Jan/2015:18:01:01] op = BOOTREQUEST
htype = HTYPE_ETHER
hlen = 6
hops = 0
xid = 12345678
secs = 0
flags = 0
ciaddr = 0.0.0.0
yiaddr = 0.0.0.0
siaddr = 0.0.0.0
giaddr = 0.0.0.0
chaddr =
sname =
file =
Options :
DHO_DHCP_MESSAGE_TYPE(53) = DHCPDISCOVER
DHO_VENDOR_CLASS_IDENTIFIER(60) = foo
padding [0] =
[08/Jan/2015:18:01:01] Waiting for response from server
//And it is stuck here since last 45 minutes....
My idea to do it is:
I will send a request to server (DHCP) (I think DHCPREQUEST() do that)that please provide me new IP adress.
Could some one please let me know if my last line will print ID adress or not ? I mean :
$response = new Net::DHCP::Packet($buf);
logger($response->toString());
EDIT:
I also tried this on suggestion by the experienced guys below but it still do not change IP adress (even i tried to run this perl code 4 times without success in IP change-Even i tried to run manually ipconfig /renew but still the IP is same all the time).
my $ipconfig = `ipconfig /renew`;
my $ipcfg_success = $?;
print $ipconfig;
if ($ipcfg_success == 0) {
do print "n succesfully runned \n";
} else {
do "\n succesfully NOT sunned \n";
}
Writing a DHCP client isn't going to change your system's IP address. You need to use the system's client.
system('ipconfig /release & ipconfig /renew');
You're not guaranteed to get a new address, though. It causes less headaches if machines always have the same IP address, so DHCP servers tend to always give the same address to a machine.
This more of a comment but it grew too long.
Just have your script call ipconfig /release and ipconfig /renew with a few seconds in between. That will request a new IP from the DHCP server, just as your script apparently tries to do.
Of course you are not exactly guaranteed a new IP that way, that depends on the configuration of the DHCP server but you are dependent on that either way. If the possible range of addresses is very small and you are afraid you might get the old IP by bad luck, check and renew again if it happened. If you get the same IP every time, it most likely means that the server recognizes you (by MAC or hostname) and assigns your static IP to you. In that case all you can do is talk to your network adminstrator (a course of action i would suggest anyways).
If you really need a guarantee, then you have to ditch DHCP and set your own IP. That however requires that you have some range of IPs reserved just for you. Otherwise your network administrator might hunt you down with their crossbow.
Be aware that depending on what that dongle is for and who set up that time limit, they may do anyways.

Error handling in Net::Openssh (host ,async =>1) option

I am using Openssh module to connect to hosts using the (async => 1) option.
How is it possible to trap connection errors for those hosts that are not able to connect.I do not want the error to appear in the terminal but instead be stored in a data structure, since I want to finally format all the data as a cgi script.When I run the script the hosts that has a connection problem throw error in the terminal.The code executes further and try to run commands on disconnected hosts.I want to isolate the disconnected hosts.
my (%ssh, %ls); #Code copied from CPAN Net::OpenSSH
my #hosts = qw(host1 host2 host3 host4 );
# multiple connections are stablished in parallel:
for my $host (#hosts) {
$ssh{$host} = Net::OpenSSH->new($host, async => 1);
$ssh{$host}->error and die "no remote connection "; <--- doesn't work here! :-(
}
# then to run some command in all the hosts (sequentially):
for my $host (#hosts) {
$ssh{$host}->system('ls /');
}
$ssh{$host}->error and die "no remote connection doesn't work".
Any help will be appreciated.
Thanks
You run async connections. So program continue work and dont wait when connection is establised.
After new with async option you try to check error but it is not defined because connection in progress and no information about error.
As i understand you need wait after first loop until connection process got results.
Try to use ->wait_for_master(0);
If a false value is given, it will finalize the connection process and wait until the multiplexing socket is available.
It returns a true value after the connection has been succesfully established. False is returned if the connection process fails or if it has not yet completed (then, the "error" method can be used to distinguish between both cases).
for my $host (#hosts) {
$ssh{$host} = Net::OpenSSH->new($host, async => 1);
}
for my $host (#hosts) {
unless ($ssh{$host}->wait_for_master(0)) {
# check $ssh{$host}->error here. For example delete $ssh{$host}
}
}
# Do work here
I don't check this code.
PS: Sorry for my English. Hope it helps you.