Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

udp recv question

by smackdab (Pilgrim)
on Jun 27, 2004 at 23:56 UTC ( [id://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 ;-)

Replies are listed 'Best First'.
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 (Monsignor) 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
Domain Nodelet?
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?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (5)
As of 2024-03-28 22:10 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found