Using perl module Log::Syslog::Fast - Unable to catch exception - perl

I am using Log::Syslog::Fast to forward logs to a syslog server. I was testing the script to see how it would react if the syslog server suddenly crashed.
To test I created a file with test messages, started the script & then shutdown the syslog server after 2 messages were received at the syslog server.
The script sent the third message & then dies. The termination is not being caught by eval & 'use warnings 'FATAL' => 'all';' does not help.
Could someone please help me catch the exception & close down the script more gracefully?
What needs to happen here is - After the Command2 is sent, the script should catch the exception & display:
Fail: Command3
Code extract:
$logger = Log::Syslog::Fast->new(LOG_TCP,$server, 514, 13, 6, "test_machine", "Syslog");
$logger->set_pid(0);
foreach $line(<SPOOL>)
{
($machine,$time,$message)=(split '\|',$line);
eval{
$logger->set_sender($machine);
$logger->send($message,$time);
};
if($#)
{
print "\nFail: $message\n";
exit;
}
else
{
print "\nSuccess: $message\n";
}
sleep 5;
}
Input File:
test_machine1|1461201306|Command1
test_machine1|1461201311|Command2
test_machine1|1461203214|Command3
test_machine1|1461203219|Command4
test_machine2|1461204005|Command5
test_machine2|1461204006|Command6
test_machine2|1461204149|Command7
test_machine3|1461204154|Command8
test_machine3|1461206936|Command9
test_machine3|1461206942|Command10
Output:
Success: Command1
Success: Command2
Success: Command3
Strace Output:
read(4, "test_machine1|1461201306|Command"..., 4096) = 341
read(4, "", 4096) = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
sendto(3, "<110>Apr 20 21:15:06 test_machin"..., 59, 0, NULL, 0) = 59
write(1, "Success Command1\n\n\n", 19Success Command1
) = 19
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({5, 0}, 0x7ffc707478f0) = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
sendto(3, "<110>Apr 20 21:15:11 test_machin"..., 59, 0, NULL, 0) = 59
write(1, "Success Command2\n\n\n", 19Success Command2
) = 19
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({5, 0}, 0x7ffc707478f0) = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
sendto(3, "<110>Apr 20 21:46:54 test_machin"..., 59, 0, NULL, 0) = 59
I want the script to fail here when it tries to send the third message but it does not.
write(1, "Success Command3\n\n\n", 19Success Command3
) = 19
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({5, 0}, 0x7ffc707478f0) = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
sendto(3, "<110>Apr 20 21:46:59 test_machin"..., 59, 0, NULL, 0) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=26037, si_uid=3179} ---
+++ killed by SIGPIPE +++
The script finally dies while trying to send the fourth message. Unfortunately the eval is not catching the exception.

Try adding a line
$SIG{PIPE} = sub {
die "SIGPIPE";
};
before anything is being sent.
You might also want to try "print" instead of die.

You might want to trap SIGPIPE like this:
$SIG{PIPE} = "IGNORE";
From the perlipc:
If you're writing to a pipe, you should also trap SIGPIPE. Otherwise, think of what happens when you start up a pipe to a command that doesn't exist: the open() will in all likelihood succeed (it only reflects the fork()'s success), but then your output will fail--spectacularly. Perl can't know whether the command worked, because your command is actually running in a separate process whose exec() might have failed. Therefore, while readers of bogus commands return just a quick EOF, writers to bogus commands will get hit with a signal, which they'd best be prepared to handle.
Also, have a look at this behavior in a C program when writing to a broken socket.

I want the script to fail here when it tries to send the third message but it does not.
TL;DR You can't do this because of the way the TCP protocol works.
The client and server communicate via a socket. When the client writes to the socket, it's actually writing to a buffer; there's no indication whether the message is actually delivered.
The client can only know that the connection is closed after some data is actually sent to the server, so the first write to the buffer will succeed.
Here's what's happening:
When you shut down the server, it sends a TCP FIN packet. A FIN indicates that one side of the connection is done sending data, but can still receive; it doesn't indicate that the connection is closed.
The client writes your third log message to the socket buffer successfully, so no exception is thrown.
The server sends a TCP RST packet to indicate that it's no longer listening.
Because of the RST, the OS now knows that the server's end of the TCP connection is closed. When the client tries to write to the socket buffer, the process is signaled with SIGPIPE and the write returns EPIPE.
The script finally dies while trying to send the fourth message. Unfortunately the eval is not catching the exception.
You're not handling SIGPIPE so your program dies when it's signaled. Add the following near the top of your script to ignore SIGPIPE:
$SIG{PIPE} = 'IGNORE';
Now you can handle the exception raised by the send method however you like.
Further reading:
Writing on a TCP socket closed by the peer
Writing to a closed, local TCP socket not failing
write on closed socket doesn't generate sigpipe immediatly
Programming UNIX Sockets in C - Frequently Asked Questions section 2.22, "When will my application receive SIGPIPE?"

Related

How to run scapy in cooked mode?

I have a problem with capturing traffic.
My system is configured with two iterfaces - ethX and tunelX.
tunelX is a tunneling iterface.
The scapy and tcpdump are capture different count of packets.
The problem is the tcpdump runs, if the "any" iterface was set, in cooked mode but scapy don't.
cooked mode means that the SOCK_DGRAM will be created instead the SOCK_RAW. It is nessesary because some data in "tunneling packtes" in link-layer might be missing or contain not enoght data to determinate type of the packet.
When I ran strace with my scapy sctipt I saw this.
927698 socket(PF_PACKET, SOCK_RAW, 768) = 4
927689 recvfrom(3, "..some-data..."..., 65535, 0, {sa_family=AF_INET6, sin6_port=htons(53), inet_pton(AF_INET6, "...some address...", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 105
927689 recvfrom(3, "..some-data..."..., 32767, 0, {sa_family=AF_PACKET, proto=0x86dd, if4, pkttype=PACKET_HOST, addr(6)={1, 30d17e75727f}, [18]) = 246
927689 recvfrom(3, "..some-data..."..., 32767, 0, {sa_family=AF_PACKET, proto=0x86dd, if4, pkttype=PACKET_HOST, addr(6)={1, 30d17e75727f}, [18]) = 86
927689 recvfrom(3, "..some-data..."..., 32767, 0, {sa_family=AF_PACKET, proto=0x86dd, if4, pkttype=PACKET_HOST, addr(6)={1, 30d17e75727f}, [18]) = 86
927689 recvfrom(3, "..some-data..."..., 32767, 0, {sa_family=AF_PACKET, proto=0x86dd, if4, pkttype=PACKET_OUTGOING, addr(6)={1, 90e2ba55f6e8}, [18]) = 271
The only last packet was added into dump.
The question is:
Is my assumption right? :) How can I launch scapy in cooked mode? I couldn't find this in manual.
Thank you.

ZeroMQ push/pull pattern

I've decided to write a test code to see how pusher - many pullers bundle works and my suspicions came true.
Pullers receive messages in order they were connected, for example 1st message is received by 1st puller connected, 2nd by 2nd, etc. I've simulated a situation when one of the pullers stayed busy after receiving a message, but when it's time came to receive a message, it queued anyway, so I have 'lost' message. That's bad. I want this message to be received by next 'free' puller. Is that real?
My test code. I use zmqpp as bindings
void main()
{
auto _socket = sIpcContext->CreateNewSocket(zmqpp::socket_type::push);
_socket->bind("tcp://*:4242");
for (auto i = 0; i < 3; ++i)
{
new std::thread([&](int _idx)
{
auto idx = _idx;
auto sock = sIpcContext->CreateNewSocket(zmqpp::socket_type::pull);
sock->connect("tcp://127.0.0.1:4242");
for (;;)
{
std::string msg;
sock->receive(msg);
std::cout << idx << " received: " << msg << std::endl;
if (idx == 1)
{
std::cout << "Puller 1 is now busy" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(10000));
}
}
}, i);
}
for (auto i = 0;; ++i)
{
_socket->send(std::to_string(i));
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
I get this output:
0 received: 0
0 received: 1
1 received: 2
Puller 1 is now busy
2 received: 3
0 received: 4
2 received: 6
0 received: 7
2 received: 9
0 received: 10
2 received: 12
0 received: 13
2 received: 15
As you can see, 5, 8, and so on are 'missed' but actually queued in puller #1
Yes, push/pull sockets are dumb enough to let that happen. You could use other sockets, such as router/dealer, to send work to a free worker.
The 0MQ Guide explains this case (calls it the Post Office Analogy):
It's the post office analogy. If you have one queue per counter, and
you have some people buying stamps (a fast, simple transaction), and
some people opening new accounts (a very slow transaction), then you
will find stamp buyers getting unfairly stuck in queues. Just as in a
post office, if your messaging architecture is unfair, people will get
annoyed.
The solution in the post office is to create a single queue so that
even if one or two counters get stuck with slow work, other counters
will continue to serve clients on a first-come, first-serve basis.
Long story short, you should be using the ROUTER when dealing with slow running workers.

UDP socket: error 34 but works fine

I have a recvfrom returning error 34, I have checked and it means "Numerical result out of range" but after it is receiving correctly the data and also the amount of data received is correct. I think it may crash after some time. Here i show the call to recvfrom:
int dataRCV = -55;
dataRCV = recvfrom ( sockfd2, data_CPV, sizeData_CPV, 0, (struct sockaddr*)&client_addr2,&client_addresslen2);
fprintf(%i %s, dataRCV,sterror(errno));
Thanks
recvfrom() returns the number of bytes read, not an error code. If recvfrom() fails, it will return -1 and errno will report the actual error code.
int dataRCV = recvfrom ( sockfd2, data_CPV, sizeData_CPV, 0, (struct sockaddr*)&client_addr2, &client_addresslen2);
if (dataRCV == -1)
fprintf(%i %s, errno, sterror(errno));
So if dataRCV is being set to 34 then recvfrom() has successfully read 34 bytes, not failed.

Best way to handle multiple connections at the same time

I have an application which listens to multiple connections and verifies whether the user is active or not
I use a 1 thread socket handling method with WSAASyncSelect.
The problem is that sometimes when a lot of users connecting at the same time some users get no reply
i think it is because the "send" hasn't been called yet and the program has received another connection so it goes again to handle the new connection ignoring the previous one. Like WSAASyncSelect has triggered and now it processing a new connection instead of completing the previous request.
So what to do to fix this issue? i tried to stop the events from WSAASyncSelect temporary by calling it with zero parameters when handling the connection until finish it then re enable network events but that didn't help either.
Here are the codes that handling the events (recieve then decrypt and then compare the bytes then send data according to what in listbox ie Active user or not)
This called upon receive of FD_READ
WSAAsyncSelect s, frmMain.hwnd, 0, 0 'Disabling Notifications event
Do Until bytesRecieved = SOCKET_ERROR
bytesRecieved = recv(wParam, buffer(Bytes), 500, 0)
If bytesRecieved > 0 Then
Bytes = Bytes + bytesRecieved
ElseIf bytesRecieved = 0 Then
Exit Sub
End If
Loop
Call MemCopy(ByVal decryptedArrival, buffer(0), Bytes)
WSAAsyncSelect s, frmMain.hwnd, WINSOCKMSG, FD_CONNECT + FD_READ + FD_CLOSE + FD_ACCEPT + FD_WRITE
If frmMain.chkSaveLog.value = vbChecked Then
frmMain.txtConnectionsLog.Text = frmMain.txtConnectionsLog.Text & Now & " Receiving a connection (" & wParam & ")" & vbNewLine
AutoScroll
If frmMain.chkAutoSave.value = vbChecked Then
strCurrentLogLine = Now & " Receiving a connection (" & wParam & ")"
AutoSaveLog (strCurrentLogLine)
frmMain.cmdClearLogs.Enabled = True
End If
End If
Below here is a decryption of bytes then comparing by ID as byte identifier like 1 = check for update
2 - send user info etc
in a Select Case statement following by a send Api.
And the accepting procedure
This called upon receive of FD_ACCEPT
Function AcceptConnection(wParam As Long)
lpString = String(32, 0)
AcSock = accept(wParam, sockaddress, Len(sockaddress))
strTempIP = getascip(sockaddress.sin_addr)
frmMain.txtConnectionsLog.Text = frmMain.txtConnectionsLog.Text & Now & " Getting a connection from IP address: " & _
strTempIP & " (" & AcSock & ")" & vbNewLine
AutoScroll
If frmMain.chkAutoSave.value = vbChecked Then
strCurrentLogLine = Now & " Getting a connection from IP address: " & strTempIP & " (" & AcSock & ")" & vbNewLine
AutoSaveLog (strCurrentLogLine)
End If
End Function
Are there any suggestions for a better performance?
What you showed is NOT the correct way to use WSAAsyncSelect(). Try something more like this instead:
When creating a listening socket:
lSock = socket(...)
bind(lSock, ...)
listen(lSock, ...)
WSAAsyncSelect lSock, frmMain.hwnd, WINSOCKMSG, FD_ACCEPT
When a listening socket receives FD_ACCEPT:
Function AcceptConnection(wParam As Long)
AcSock = accept(wParam, sockaddress, Len(sockaddress))
If AcSock = INVALID_SOCKET Then
Exit Sub
End If
WSAAsyncSelect AcSock, frmMain.hwnd, WINSOCKMSG, FD_READ + FD_CLOSE + FD_WRITE
...
End Function
When an accepted client socket receives FD_READ:
Function ReadConnection(wParam As Long)
Do
bytesRecieved = recv(wParam, ReadBuffer(ReadBytes), 500, 0)
If bytesRecieved = SOCKET_ERROR Then
If WSAGetLastError() <> WSAEWOULDBLOCK Then
Exit Sub
End If
ElseIf bytesRecieved = 0 Then
Exit Sub
Else
ReadBytes = ReadBytes + bytesRecieved
End If
Loop Until bytesRecieved = SOCKET_ERROR
' process ReadBuffer up to ReadBytes number of bytes as needed...
' remove processed bytes from front of ReadBuffer and decrement ReadBytes accordingly
...
End Function
When an accepted client socket receives FD_WRITE:
Function WriteConnection(wParam As Long)
While SendBytes > 0
bytesSent = send(wParam, SendBuffer(0), SendBytes, 0)
If bytesSent = SOCKET_ERROR Then
Exit Sub
End If
' remove bytesSent number of bytes from front of SendBuffer ...
SendBytes = SendBytes - bytesSent;
End While
End Function
The trick is that you need to allocate separate ReadBuffer and SendBuffer buffers for each accepted client. Make sure that each time you receive FD_READ that you are appending bytes only to the ReadBuffer of the socket that triggered FD_READ, and each time you receive FD_WRITE that you are removing bytes only from the SendBuffer of the socket that triggered FD_WRITE.
When recv() has no more bytes to read, process that socket's ReadBuffer as needed, removing only complete messages from the front and leaving incomplete messages for later processing.
When send() fails with WSAEWOULDBLOCK, append any unsent bytes to the SendBuffer of the socket that caused send() to fail. When you receive an FD_WRITE event for a socket, check that socket's SenBuffer and resend any bytes that are in it, stopping when the buffer is exhausted or an WSAEWOULDBLOCK error occurs.
Very easy, and quite effective, way to do it is to fork out for every incoming connection. This will most likely require you to restructure your application, but the basic flow should be as follows:
1. New connection is opened to the server
2. Server accepts the connection and forks out
3. The fork closes the original socket for listening, so only the parent will be accepting new connections
4. And then your magic happens, separate from the original thread.
This way you do not have to worry about issues of concurrency, as long as your machine can handle all the traffic and load because each connections is independent.

UDP: why result of function send fails

I am trying to send message with UDP protocol from client to server.
But
function send(sockfd, buf, strlen(buf), 0)
<socket.h>
which I use for send buffer returns -1, any time when I call it.
In description of func i read about results and i know if result = 0 or below - function finished with errors, and if result > 0 function successfully finised.
for example:
result of calling
send(socket, "1.2.3.4", 7, 0) will be -1.
What am I missing?