Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

udp recv question

by smackdab (Pilgrim)
on Jun 27, 2004 at 23:56 UTC ( #370063=perlquestion: print w/ replies, xml ) Need Help??
smackdab has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks,

I am trying to get the ICMP host unreachable msg back when I send a message to a UDP host:port that isn't listening... I have tried a bunch of things, including reading: Network Programming with Perl by Lincoln D. Stein. But, networking is something I am still very new at...

Here is my sample app, if anyone can kick me over the hump, I would appreciate it ;-)
# # How to get an ICMP host unreachable packet??? # use IO::Select; use IO::Socket; use strict; use warnings; my $sock_udp = IO::Socket::INET->new(Proto=>'udp'); print "created udp=$sock_udp\n"; my $sock_icmp = IO::Socket::INET->new(Proto=>'icmp'); print "created icmp=$sock_icmp\n"; my $select = IO::Select->new(); my $host = '192.168.0.4'; my $port = 2; my $rbits = 0; my $aton = inet_aton($host); my $addr = sockaddr_in($port, $aton); $sock_udp->connect($addr) or die "$!\n"; #Unix socket faq says udp +must be "connected" to get icmp msg print " udp 'connected' to $host:$port\n"; #http://www.unixprogram.co +m/socket/socket-faq.html#faq55 $select->add($sock_icmp); $select->add($sock_udp); vec($rbits, $sock_icmp->fileno(), 1) = 1; vec($rbits, $sock_udp->fileno(), 1) = 1; $sock_udp->send("test", 0) or die "$!\n"; print " send test msg to $host:$port\n"; # select attempt my $found = select($rbits, undef, undef, 1); if ($found > 0) { my $msg = ""; my $size = 1500; my $from_icmp = recv($sock_icmp, $msg, $size, 0) or die "$!\n"; my $from_udp = recv($sock_icmp, $msg, $size, 0) or die "$!\n"; my ($from_port, $from_ip) = sockaddr_in($from_icmp); # I need to unpack the packet here I think... } # io::select attempt my @found = $select->can_read(); foreach (@found) { print " got a can_read on socket=$_\n"; my $msg = ""; my $size = 1500; #my $from_icmp = recv($sock_icmp, $msg, $size, 0) or die " $!\n +"; my $from_udp = recv($sock_icmp, $msg, $size, 0) or die " $!\n"; my ($from_port, $from_ip) = sockaddr_in($from_udp); # I need to unpack the packet here I think... } #If you can figure this out, here is another question: #I want to scan more than 1 machine for a certain open UDP ports #(for use internally at my business and I want to write it myself and +not use the awesome nmap...) #Should I create a new UDP socket for each host I want to test? #and then wait in the select loop? #The reason I ask is that I expect to get stuck on determinig #what hosts/ports replied back as ICMP unreachable... #I am not there yet, but expect to get stuck ;-)

Comment on udp recv question
Download Code
Re: udp recv question
by tachyon (Chancellor) on Jun 28, 2004 at 01:05 UTC

    Net::Ping is probably what you are looking for. RTFS for the implementation details. Hint - you can't send the string 'test' and expect it to be treated as a valid ICMP request packet. See RFC792 for details or NetPacket::ICMP for an implementation that will assemble and disassemble ICMP packets for you.

    cheers

    tachyon

      Thanks, NetPacket::ICMP looks great and I know I will need to decode the ICMP packet once I can figure out how to recv() it...

      My question is a UDP->ICMP question.

      I am sending out a UDP packet, which in the examples I have seen is valid and my example works correctly (when viewed from a sniffer type product).

      When a UDP port isn't available on the remote machine a ICMP unreachable packet is sent back.

      I can't figure out how to "read" that packet...and that is my question.

        You really don't want to code this yourself. Here is a trivial example for interest sake only. It shows you how to build a packet (and its checksum) and that you do get data back which as you can see it is encoded in a binary packet format.

        use IO::Socket; use constant ICMP_ECHO => 8; use constant SUBCODE => 0; # No ICMP subcode for ECHO and ECHOR +EPLY use constant ICMP_STRUCT => "C2 n3 A64"; my $icmp = IO::Socket::INET->new( PeerAddr => 'perlmonks.org', Proto=>'icmp' ); print "Got socket\n"; my $data = '1'x64; my $seq = 1; my $checksum = 0; my $msg = pack(ICMP_STRUCT, ICMP_ECHO, SUBCODE, $checksum, $$, $seq, $ +data); $checksum = checksum($msg); my $msg = pack(ICMP_STRUCT, ICMP_ECHO, SUBCODE, $checksum, $$, $seq, $ +data); print "Sending: $msg\n"; $icmp->send($msg); $icmp->recv(my $buf, 1500); print "Got: $buf"; print "Done\n"; sub checksum { my ($msg ) = @_; my $len_msg = length($msg); my $num_short = int($len_msg / 2); my $chk = 0; for my $short (unpack("n$num_short", $msg)) { $chk += $short; } $chk += (unpack("C", substr($msg, $len_msg - 1, 1)) << 8) if $len_ms +g % 2; $chk = ($chk >> 16) + ($chk & 0xffff); return(~(($chk >> 16) + $chk) & 0xffff); }

        cheers

        tachyon

Re: udp recv question
by dave_the_m (Parson) on Jun 28, 2004 at 11:08 UTC
    If a UDP socket has an address bound to it, then ICMP errors related to that that socket are passed back to it, eg
    use IO::Socket; use strict; use warnings; my $sock = IO::Socket::INET->new( PeerAddr => 'localhost:9999', Proto=>'udp', ) or die "sock new: $!\n"; my $result; $sock->send("abc",0) or die "send: $!\n"; defined($sock->recv($result,1,0)) or die "recv: $!\n";

    For a destination port that isn't open, this code outputs:

    recv: Connection refused

    Dave.

      Thanks, that makes sense...

      I just tried your code on an old linux box I have and it worked but doesn't work on windows 2000. I need something to work on both OSes.

      On windows, I get: "recv: Unknown error"
      How can I get "ICMP error back from my UDP message"
      This may work as advertised on your OS ( Linux? ), but on Win2K returns recv: Unknown error. The above link explains why it works for some Linux implementations.

      UDP is a connectionless protocol ( There is not TTL added to the message so ICMP is not guaranteed! ) If you want to see what makes up it message IP Message Formats
      Cheers!
      JamesNC
        This may work as advertised on your OS ( Linux? ), but on Win2K returns recv: Unknown error. The above link explains why it works for some Linux implementations.
        I tested it on Solaris. It should work on any decent OS. The link you gave refers to this working with unconnected sockets, which seems to be a linux-specific feature, but by adding the PeerAddr parameter, perl creates a connected socket, which should allow any errors to be reported by the OS. It looks like even W2K is reporting the returned ICMP, just not with a very helpful error number.

        Of course with UDP and ICMP you're never guaranteed a reply, but that's not due to TTLs - all IP packets, including UDP and ICMP, have TTL fields.

        Dave.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://370063]
Approved by davido
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (7)
As of 2015-03-29 13:20 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    When putting a smiley right before a closing parenthesis, do you:









    Results (630 votes), past polls