perl Socket6 binding to only one wildcard address - windows

I have following program in perl which is supposed to listen on IPv6 address, and by theory should serve to both IPv4 (through IPv4 mapped IPv6 address) and IPv6 clients on a dual stack box.
use Socket;
use Socket6;
#res = getaddrinfo('', 8086, AF_UNSPEC, SOCK_STREAM,0, AI_PASSIVE);
my #ipv6Result;
while(scalar(#res)>=5){
my #currentResult = #res;
($family, $socktype, $proto, $saddr, $canonname, #res) = #res;
if($family == AF_INET6){
#ipv6Result = #currentResult;
}
}
if(#ipv6Result){
($family, $socktype, $proto, $saddr, $canonname) = #ipv6Result;
}
socket(Socket_Handle, $family, $socktype,$proto) || next;
bind(Socket_Handle,$saddr ) || die "bind: $!";
listen(Socket_Handle, 1) || die "listen: $!";
$paddr = accept(Client,Socket_Handle) || die "accept: $!";
After running this the netstat gave following observation:
c:\Perl\bin>netstat -nao | findstr 8086
TCP [::]:8086 [::]:0 LISTENING 2892
It seems, it is listening on only IPv6 wildcard address (::) and not on IPv4 wildcard address (0.0.0.0). I was not able to connect this server process from an IPv4 client, but was able to connect through an IPv6 client.
I tried a similar server program in java as follows (on the same setup):
import java.net.ServerSocket;
public class CodeTCPServer {
public static void main(String[] args) throws Exception{
new ServerSocket(8086).accept();
}
}
The netstat output for this was as follows:
C:\Users\Administrator>netstat -nao | findstr 8086
TCP 0.0.0.0:8086 0.0.0.0:0 LISTENING 3820
TCP [::]:8086 [::]:0 LISTENING 3820
Seems to listen on both IPv6 and IPv4, and I am also able to connect it from IPv4 and IPv6 clients.
If I run the same perl program on a linux box it works fine, and I am able to connect to it through IPv4 and IPv6 clients.
I wonder, if something on windows is stopping the perl program from listening on both IPv4 and IPv6 (but then it should have stopped the java program as well for the same reason). If some problem with the program logic it shouldn't have worked on linux as well.
(I am using Socket6 for now, as I couldn't use perl's inbuilt support for IPv6 somehow on windows, I am in communication with the authors to get it worked on my setup)
UPDATE:
I just tried following:
setsockopt (Socket_Handle, IPPROTO_IPV6, IPV6_V6ONLY, 0 ) or print("\nFailed to set IPV6_V6ONLY $! ");
in anticipation that the socket option has default value 1 (for this platform), and I have to manually override it, but alas! I got following error:
Your vendor has not defined Socket macro IPV6_V6ONLY, used at c:\socket6\Socket6Server.pl line 66
Now I wonder what does 'vendor' mean, is it Socket6 module / perl vendor or OS vendor ?
UPDATE2
I think the answer is given in http://metacpan.org/pod/IO::Socket::IP (for the V6Only argument)
with following lines:
If your platform does not support disabling this option but you still want to listen for both AF_INET and AF_INET6 connections you will have to create two listening sockets, one bound to each protocol.
And this worked for me! But then I need to check if the platform supports V6Only disabling
(protocol aware code in my program :( ), when compared to Java, Java does it automatically for me (checking and creating 2 sockets).

This requires the BIND_V6ONLY socket option to be turned off. See the IO::Socket::IP source for details on how.
Also, in response to your comment
I am using Socket6 for now, as I couldn't use perl's inbuilt support for IPv6 somehow on windows, I am in communication with the authors to get it worked on my setup)
That's not strictly true, if memory serves. You were having trouble with IO::Socket::IP but the plain Socket stuff should all be working fine. You don't need to be using Socket6 because Socket 2.006 already has everything that does. You can replace your code with:
use Socket qw( :addrinfo SOCK_STREAM AF_INET6 );
my ($err, #res) = getaddrinfo('', 8086,
{ socktype => SOCK_STREAM, flags => AI_PASSIVE });
my $ipv6Result;
my $current;
while(#res){
$current = shift #res;
if($current->{family} == AF_INET6) {
$ipv6Result = $current;
}
}
if($ipv6Result) {
$current = $ipv6Result;
}
socket(my $sock, $current->{family}, $current->{socktype}, $current->{proto}) or next;
bind(my $sock ,$current->{addr}) or die "bind: $!";
listen(my $sock, 1) or die "listen: $!";
my $paddr = accept(my $client, $sock) or die "accept: $!";

Related

Rsyslog create two listeners (with and without TLS) with omfile as output. Possible or not?

I am trying to create a rsyslog.conf with multiple listeners e.g. with and without TLS (with streamdriver). It is possible to create multiple inputs, but as I read in the rsyslog documentation, it seems to be impossible to move the streamdriver parameters e.g. streamdriver.mode="1" from module() to inputs() or to action() when using omfile. Does anybody know if there is a way to create multiple listeners with imtcp and omfile as output method?
my working script for single listener:
# Prints every message, even if repeated 1001 times in a second. Strongly recommend for use with Splunk
$RepeatedMsgReduction off
module(load="imtcp"
streamdriver.name="gtls" # use gtls netstream driver
streamdriver.mode="1" # require TLS for the connection
streamdriver.authmode="x509/name" # server is NOT authenticated
)
global(
defaultNetstreamDriverCAFile="/opt/splunk/etc/auth/sslCerts/CACertificate.pem"
defaultNetstreamDriverCertFile="/opt/splunk/etc/auth/sslCerts/ServerCertificate.pem"
defaultNetstreamDriverKeyFile="/opt/splunk/etc/auth/sslCerts/ServerPrivatKeyDec.key"
)
# Create as many inputs as you like. This listens to UDP + TCP 514.
input(type="imtcp" port="514" ruleset="SplunkNetwork")
# Template for directory + filename structure. Use %FROMHOST-IP% for IP without hostname resolution
template(name="filename-by-host" type="string" string="/opt/logfiles/%FROMHOST%/%$YEAR%-%$MONTH%-%$DAY%.log")
ruleset(name="SplunkNetwork") {
action(type="omfile" DynaFile="filename-by-host" DirCreateMode="0755" FileCreateMode="0644" DirOwner="splunk" DirGroup="splunk" FileOwner="splunk" FileGroup="splunk")
}
What I want to do - not working - passing the streamdriver parameters to input() or action():
# Prints every message, even if repeated 1001 times in a second. Strongly recommend for use with Splunk
$RepeatedMsgReduction off
module(load="imtcp")
global(
defaultNetstreamDriverCAFile="/opt/splunk/etc/auth/sslCerts/CACertificate.pem"
defaultNetstreamDriverCertFile="/opt/splunk/etc/auth/sslCerts/ServerCertificate.pem"
defaultNetstreamDriverKeyFile="/opt/splunk/etc/auth/sslCerts/ServerPrivatKeyDec.key"
)
# Create as many inputs as you like. This listens to UDP + TCP 514.
input(type="imtcp" port="514" ruleset="SplunkNetwork-anon-no-tsl")
input(type="imtcp" port="1514" ruleset="SplunkNetwork-anon-tsl")
# Template for directory + filename structure. Use %FROMHOST-IP% for IP without hostname resolution
template(name="filename-by-host" type="string" string="/opt/logfiles/%FROMHOST%/%$YEAR%-%$MONTH%-%$DAY%.log")
ruleset(name="SplunkNetwork-anon-no-tsl") {
action(type="omfile" DynaFile="filename-by-host" DirCreateMode="0755" FileCreateMode="0644" DirOwner="splunk" DirGroup="splunk" FileOwner="splunk" FileGroup="splunk" StreamDriverMode="0" StreamDriver="gtls" StreamDriverAuthMode="anon")
}
ruleset(name="SplunkNetwork-anon-tsl") {
action(type="omfile" DynaFile="filename-by-host" DirCreateMode="0755" FileCreateMode="0644" DirOwner="splunk" DirGroup="splunk" FileOwner="splunk" FileGroup="splunk" StreamDriverMode="1" StreamDriver="gtls" StreamDriverAuthMode="anon")
}
You may use
imtcp for TLS
imptcp for TCP
You an use both the imptcp and imtcp modules to allow plain TCP and TLS connections. The example below shows the rsyslog configuration required to setup the logging input for plain TCP on port 514 and TLS on port 1514.
global(
defaultNetstreamDriverCAFile="/opt/splunk/etc/auth/sslCerts/CACertificate.pem"
defaultNetstreamDriverCertFile="/opt/splunk/etc/auth/sslCerts/ServerCertificate.pem"
defaultNetstreamDriverKeyFile="/opt/splunk/etc/auth/sslCerts/ServerPrivatKeyDec.key"
)
# Load the imptcp module to provide the ability to receive messages over plain TCP
module(load="imptcp")
# Load the imtcp module to provide the ability to receive messages over TLS
module(
load="imtcp"
streamdriver.name="gtls" # use gtls netstream driver
streamdriver.mode="1" # require TLS for the connection
streamdriver.authmode="x509/name" # server is NOT authenticated
)
# Listen op port 514 (imptcp driver)
input(
type="imptcp"
port="514"
)
# Listen on port 1514 (imtcp driver)
input(
type="imtcp"
port="1514"
)

zmq.error.ZMQError: Cannot assign requested address

I have the following pull - publisher ZMQ schema on an Amazon EC2 machine:
I am working with the Public IP address of my EC2 Amazon machine.
I am trying send data via ZMQ PUSH socket from the client side to ZMQ PULL socket server side, which is this:
import zmq
from zmq.log.handlers import PUBHandler
import logging
# from zmq.asyncio import Context
def main():
ctx = zmq.Context()
publisher = ctx.socket(zmq.PUB)
# publisher.bind("tcp://*:5557")
publisher.bind("tcp://54.89.25.43:5557")
handler = PUBHandler(publisher)
logger = logging.getLogger()
logger.addHandler(handler)
print("Network Manager CNVSS Broker listening")
collector = ctx.socket(zmq.PULL)
# collector.bind("tcp://*:5558")
collector.bind("tcp://54.89.25.43:5558")
while True:
message = collector.recv()
print("Publishing update %s" % message)
publisher.send(message)
if __name__ == '__main__':
main()
But when I excute this script, I get this error:
(cnvss_nm) ubuntu#ip-172-31-55-72:~/cnvss_nm$ python pull_pub-nm.py
Traceback (most recent call last):
File "pull_pub-nm.py", line 28, in <module>
main()
File "pull_pub-nm.py", line 10, in main
publisher.bind("tcp://54.89.25.43:5557")
File "zmq/backend/cython/socket.pyx", line 547, in zmq.backend.cython.socket.Socket.bind
File "zmq/backend/cython/checkrc.pxd", line 25, in zmq.backend.cython.checkrc._check_rc
zmq.error.ZMQError: Cannot assign requested address
(cnvss_nm) ubuntu#ip-172-31-55-72:~/cnvss_nm$
I've changed my IP-address to publisher.bind("tcp://*:5557") and collector.bind("tcp://*:5558") in the server side, and my script is running:
(cnvss_nm) ubuntu#ip-x-x-x-x:~/cnvss_nm$ python pull_pub-nm.py
Network Manager CNVSS Broker listening
But from my client-side code ( added recently ), any data is sent.
#include <zmq.hpp>
#include <zmq.h>
#include <iostream>
#include "zhelpers.hpp"
using namespace std;
int main(int argc, char *argv[])
{
zmq::context_t context(1);
/*
std::cout << "Sending message to NM Server…\n" << std::endl; */
zmq::socket_t subscriber(context, ZMQ_SUB);
subscriber.connect("tcp://localhost:5557");
subscriber.setsockopt(ZMQ_SUBSCRIBE, "", 0);
zmq::socket_t sender(context, ZMQ_PUSH);
sender.connect("tcp://localhost:5558");
string firstMessage = "Hola, soy el cliente 1";
while (1)
{
// Wait for next request from client
std::string string = s_recv(subscriber);
std::cout << "Received request: " << string << std::endl;
// Do some 'work'
// sleep(1);
// Send reply back to client
// zmq::message_t message(firstMessage.size() + 1);
// Cualquiera de los dos se puede
// memcpy(message.data(), firstMessage.c_str(), firstMessage.size() + 1);
// s_send(sender, "Hola soy un responder 1");
// sender.send(message);
}
}
I think that my inconvenient is on my EC2 machine network configuration or on the way of setup the IP address of the server.
When I test the clients and server locally, all it works perfectly.
Is there any possibility of performing some forwarding or NAT operation on my EC2 machine?
My clients do not reach the server.
I have the security groups rule the above mentioned ports 5557 and 5558.
How to solve this inconvenience?
I had a similar situation where I was using ZMQ on EC2 and getting "Cannot assign requested address." I was also using Elastic IP as suggested in the answer, but it did not work for me. It turned out that on EC2, the sending side (ZMQ.PUSH) needs to bind to the private IP rather than to the public, while the receiving side needs to bind to the public IP, so trying to bind the server to Elastic IP caused the error. After I changed it to bind the server ZMQ.PUSH side to Private IP and the client ZMQ.PULL to Elastic IP (on the same port), it worked.
How to solve this inconvenience?
1 )If in doubts about the EC2 addresses, first try to test the reversed .bind() / .connect(), so that the EC2-side localhost address assignments are out f the game, and your connectivity proof towards a known IP-address will not depend on the EC2-side settings.
2 )Next, given there are no details about the client-side part of the MCVE, I may have got the scenario idea incorectly, so bear with me - there are only these compatible ZeroMQ Scalable Formal Communication Archetype sockets' matches available ever since, up to API v4.2.x in 2018/Q2:
{ PUB: [ SUB,
XSUB,
None
],
PULL: [ PUSH,
None
],
...
}
3 )There is a good engineering practice not to let unhandled exceptions happen, the more, if Context()-instance may still bear the possession of IP:PORT# (b)locked resource ( sometimes even beyond the python process termination ( many incidents with my own naive and this way deadlocked experiments in my past dark history :o) )
Each step in the infrastructure setup ought be wrapped into error-handling syntax-clause, best including a finally: section, where so far created resources will occasionally get dismantled in a graceful manner in cases, when exception(s) spring out. This way your code will prevent a forever hanging orphan(s), that have just an option to reboot the platform so as to get rid of these, otherwise impossible to salvage, hostages.
Problem solved,a final summary :
The initially indicated problem ( diagnosed at .bind() / .connect() phase ) was, as depicted earlier, related to Amazon EC2 instance IP-address mapping, as the term, needed for any transport-class Endpoint setup, localhost:port#
camdebu on Nov 1, 2012 5:07 PM explained all the steps needed:Setup an Elastic IP to your EC2 isntance. You will then have a static IP address. There's no cost for the Elastic IP as long as you have it pointed to an EC2 instance.
You should then have no problem connecting to your new IP Address and port as long as your security group is setup correctly.
-Cam-
Check your Security Group Rule. Make sure you allow the port to communicate from outside the instance. (Enable All TCP and Check). [ added Yesu Jeya Bensh.P ]
The recently posted client-code but shows another issue, a mutual block, generated by a non-cooperating zmq::socket_t sender( context, ZMQ_PUSH ), which actually never sends a single message.
Given the client goes into while(1)-loop as posted above, the associated peer will inadvertently get into an unsalvageable blocked state inside the python-made main(), since :
def main():
...
collector = ctx.socket( zmq.PULL )
#ollector.bind( "tcp://*:5558" )
collector.bind( "tcp://54.89.25.43:5558" )
while True:
message = collector.recv() # THIS SLOC WILL BLOCK FOREVER HERE,
... # GIVEN <sender> NEVER SENDS...
so more care is to be taken, so as to make the flow of events robust enough, not to ever fall into this or similar unsalvageable mutual block.

Serial Ports /dev/tty.* are not working when /dev/cu.* ports are accessed firstly

I am trying to create a Processing application connected with an Arduino.
Due to the fact that I want the connection between the two to be established automatically, meaning that I do not specify the name of the port, but I'm using Serial.list() to get the names of the ports available and then with a for loop I will check which one is printing the correct string.
The problem is that when I access firstly the /dev/cu.* then all the /dev/tty.* ports are busy and vice versa. This is quite strange and I do not want this to happen.
You should be able to use one(/dev/tty.*) or the other(/dev/cu.*), but not both at the same time as they might point to the same resource in different ways.
I recommend listing the ports, checking the port prefix (agains let's say /dev/tty.*, but not /dev/cu.*), initialising the Serial port, then exiting the loop that traverses the listed serial ports:
import processing.serial.*;
Serial arduino;
final int BAUD_RATE = 9600;
void setup(){
String[] ports = Serial.list();
for(int i = 0 ; i < ports.length; i++){//go through each port
if(ports[i].contains("tty.usbmodem")){//find one that looks like an Arduino on OSX
try{
arduino = new Serial(this,ports[i],BAUD_RATE);//initialize the connection
i = ports.length;//exit the loop, break; should also work
println("Arduino connection succesfully initialized");
}catch(Exception e){
System.err.println("Error opening Serial port!\nPlease check USB connection and ensure the port is not already open in another application.");
e.printStackTrace();
}
}
}
if(arduino == null) System.err.println("Serial connection to Arduino failed!");
}

IPv6 with perl on windows not working

cross-post http://perlmonks.org/index.pl?node_id=984750
(Possible duplicate of perl windows IPv6 )
I tried following sample example from : https://metacpan.org/module/IO::Socket::IP
use IO::Socket::IP -register;
my $sock = IO::Socket->new(
Domain => PF_INET6,
LocalHost => "::1",
Listen => 1,
) or die "Cannot create socket - $#\n";
print "Created a socket of type " . ref($sock) . "\n";
It is giving output as :
Cannot create socket - no address associated with nodename
I am using ActiveState perl 5.14.2 and have built IO::Socket::IP module on it.
Following is the ping result:
c:\>ping ::1
Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Ping statistics for ::1:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
If I use the IPv4 style loopback address 127.0.0.1, the above code works well.
I am wondering what I am missing.
Update:
I just cleaned the perl setup and path, and freshly installed perl 5.14.2 from
http://www.activestate.com/activeperl/downloads
and then I tried following simple code:
use strict;
use warnings;
use Socket qw(getaddrinfo SOCK_STREAM AI_PASSIVE );
my ( $err, #res ) = getaddrinfo( "::", 8086, {
socktype => SOCK_STREAM,
flags => AI_PASSIVE,
} );
die $err if $err;
it ended with following error:
no address associated with nodename at c:\IPv6.pl line 10.
But with 127.0.0.1 it returns proper value.
I am using windows 2008 R2 box, a same run on my another windows box also fails.
I just tried to trace this call in Socket.pm, and found that a "fake_getaddrinfo" is getting called instead of the real getaddrinfo. It seems the XSLoader was either not able to find/load getaddrinfo from Socket.dll or Socket.dll didn't at all have the getaddrinfo.
What could be the reason?
A similar code below using Socket6 works properly on the same setup:
use Socket;
use Socket6;
#res = getaddrinfo('::', 8086, AF_UNSPEC, SOCK_STREAM);
while(scalar(#res)>=5){
($family, $socktype, $proto, $saddr, $canonname, #res) = #res;
($host, $port) = getnameinfo($saddr, NI_NUMERICHOST | NI_NUMERICSERV);
print ("\nhost= $host port = $port");
socket(Socket_Handle, $family, $socktype, $proto) || next;
bind(Socket_Handle,$saddr ) || die "bind: $!";
listen(Socket_Handle, 5) || die "listen: $!";
($host, $port) = getnameinfo($saddr, NI_NUMERICHOST | NI_NUMERICSERV);
print ("\nReady for connections \nhost= $host port = $port");
$paddr = accept(Client, Socket_Handle);
}
So I can't even blame the setup or the system dlls. Is there an issue with perl's built-in IPv6 support for windows' activestate build?
As stated by vinsworldcom on perlmonks.org, to make use of IPv6 sockets you do need to have the Socket6 module installed. As soon as you'd install this via cpan, the code snippet will work fine.

How to find out the port number of a TCP connection

Let's say I have the following piece of code.
server = TCPServer.new(3200)
client = server.accept()
How do I find out what port number that client sent its message to me is? I have tried both client.peeraddr and client.addr and both of them do not give me the proper port number.
Port that clients are connecting to is 3200. And port on client side where connection is created from is random for every connection, given by OS from unused ports.
client.peeraddr gives you an array that corresponds to a struct addrinfo. For AF_INET, it looks something like this:
["AF_INET", 48942, "127.0.0.1", "127.0.0.1"]
You can create an Addrinfo object from it and get the port like so:
require 'socket'
server = TCPServer.new(3200)
client = server.accept()
addr = Addrinfo.new(client.peeraddr)
port = addr.ip_port

Resources