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.
Related
I am currently struggling with receiving broadcast packets from the IP address 255.255.255.255 in ruby.
In general my network configuration has two different vlans:
vlan10: ip 10.10.10.1 netmask 255.255.0.0 => broadcast address 10.10.255.255
vlan20: ip 10.0.0.1 netmask 255.0.0.0 => broadcast address 10.255.255.255
As receiver I use the following test code to dispay the incoming packets:
require 'socket'
addr = ['0.0.0.0', 3020]
BasicSocket.do_not_reverse_lookup = true
# Create socket and bind to address
UDPSock = UDPSocket.new
UDPSock.bind(addr[0], addr[1])
while true
data, addr = UDPSock.recvfrom(1024)
puts "From addr: '%s', msg: '%s'" % [addr[0], data]
end
UDPSock.close
Receiving packets from 10.255.255.255 and 10.10.255.255 works fine.
Packets sent to IP 255.255.255.255 are not received.
Do I need to set additional properties to make ruby receive "limited broadcast" packets?
I hope somebody can help. I am really lost.
Thanks, Uwe
Thanks for your help. During checking the router configuration I noticed that I had a wrong vlan configuration that result in this behavior.
I am trying to gather information from a TCP connection made to my web server to assist in our troubleshooting efforts, similar to http://speedguide.net/analyzer.php tool.
We have a simple PHP server script test page that users connect that returns their private IP to an AJAX call waiting for the response.
I would like to either build on that or prefer using Ruby. I played with the PacketFu lib and get all the information I think I need, however, I'm having trouble with the recipe to combine it all:
listen on port x,
parse the packet
respond back to client.
Using Ruby's TCPServer I can easily handle 1 and 3. With Packetfu, 2.
I've coded with PHP in the past but only HTML-based, no sockets. And I'm not really all that familiar with Ruby sockets either.
Though the packet stream and client.accept don't seem to play nice. The packets aren't always IP or TCP meeting the Packetfu::Packet.is_ip? or is_tcp?.
Could someone point me in the right direction or give me some practical example of how I might combine the two, or adjust my thinking on how I would accomplish this task?
This is the Playground code:
require 'socket'
require 'json'
require 'packetfu'
iface = ARGV[0] || "eno1"
server = TCPServer.open(31337)
cap = PacketFu::Capture.new(:iface => iface, :start => true, :promisc => true)
loop {
cap.stream.each do |p|
pkt = PacketFu::Packet.parse(p)
if pkt.is_ip? || pkt.is_tcp?
if pkt.tcp_dport == 31337
print "Source Addr: #{pkt.ip_saddr}\n"
print "Source Port: #{pkt.tcp_src}\n"
print "Destination Addr: #{pkt.ip_daddr}\n"
print "Destination Port: #{pkt.tcp_dport}\n"
print "TCP Options: #{pkt.tcp_options.inspect}\n"
print "TCP Win: #{pkt.tcp_win}\n"
print "TCP SYN?: #{pkt.tcp_flags.syn}\n"
print "TCP ACK?: #{pkt.tcp_flags.ack}\n"
print "TCP FLAGS ALL: #{pkt.tcp_flags.inspect}\n"
print "TTL: #{pkt.ip_ttl}\n"
print "IP FRAG: #{pkt.ip_frag}\n"
end
end
client = server.accept # Wait for a client to connect
h = { ipaddress: client.peeraddr[2] }
client.puts h.to_json
client.close
end
}
This is the output:
Source Addr: 172.20.0.15
Source Port: 41165
Destination Addr: 172.20.0.10
Destination Port: 31337
TCP Options: "NOP,NOP,TS:216432150;57946250"
TCP Win: 229
TCP SYN?: 0
TCP ACK?: 1
TCP FLAGS ALL: #<struct PacketFu::TcpFlags urg=0, ack=1, psh=0, rst=0, syn=0, fin=0>
TTL: 61
IP FRAG: 16384
This is the browser response:
{"ipaddress":"172.20.0.15"}
Using this small ruby script + packetfu, I try to retransmit a broadcast packet to several hosts:
#!/usr/bin/ruby
require 'rubygems'
require 'packetfu'
dev = ARGV[0]
mac=`ip link show #{dev} | awk '/ether/ {print $2}'`
ARGV.shift
dests = ARGV
cap = PacketFu::Capture.new(
:timeout => 4000,
:iface => dev, :start => true,
:filter => "ether src #{mac} and ether[0] & 1 = 1")
loop do
cap.stream.each do |pkt|
frame = PacketFu::IPPacket.parse(pkt)
i = 0
dests.each do |dest|
frame.eth_daddr = dest
frame.ip_header.ip_id = frame.ip_header.ip_id + i
frame.ip_header.ip_sum = frame.ip_header.ip_calc_sum()
frame.to_w(dev)
i = i + 1
end
end
end
Using tcpdump, I see that they are retransmitted (sent several
times).
$ echo "foo" | netcat -ub 192.168.15.255 54321
On host1 (source host), tcpdump shows me at the moment:
12:19:30.602374 0a:f4:8d:75:e5:af > ff:ff:ff:ff:ff:ff, ethertype IPv4 (0x0800), length 46: 192.168.15.17.36339 > 192.168.15.255.54321: UDP, length 4
12:19:30.605856 0a:f4:8d:75:e5:af > 0a:a2:5f:f1:de:e4, ethertype IPv4 (0x0800), length 46: 192.168.15.17.36339 > 192.168.15.255.54321: UDP, length 4
12:19:30.608896 0a:f4:8d:75:e5:af > 0a:25:ef:2c:24:e3, ethertype IPv4 (0x0800), length 46: 192.168.15.17.36339 > 192.168.15.255.54321: UDP, length 4
On host2 (destination host, mac address 0a:a2:5f:f1:de:e4), tcpdump shows nothing.
Security policy allows all traffic in the VPC.
Have you any ideas?
I'm sure that I miss some small thing.
(I thank Kenta Yasukawa for the answer):
Have you turned off the source/destination check for your instances? By
default, EC2 instances drop an incoming packet whose destination address does
not match the receiver IP address. You can disable it by right-clicking on an
instance in Management console and select "Change Source / Destination check",
and click disable.
To disable the ckeck using EC API Tools:
$ ec2-modify-network-interface-attribute --source-dest-check False eni-8c8c9bca
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: $!";
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