From POPmail Tue Nov 17 18:45:15 1998
 (with Netcom Interactive pop3d (v1.21.1 1998/05/07) Wed Nov 18 02:39:15 1998)
X-From_: seg@crow.sec.gov  Tue Nov 17 20:36:28 1998
Received: from crow.sec.gov (crow.sec.gov [204.192.28.11]) by multi33.netcomi.com (8.8.5/8.7.4) with ESMTP id UAA32707 for <fc@all.net>; Tue, 17 Nov 1998 20:36:28 -0600
Received: (from seg@localhost) by crow.sec.gov  id VAA01882 for fc@all.net; Tue, 17 Nov 1998 21:37:15 -0500
From: Joe Segreti <seg@crow.sec.gov>
Message-Id: <199811180237.VAA01882@crow.sec.gov>
Subject: upd dtk
To: fc@all.net
Date: Tue, 17 Nov 1998 21:37:14 -0500 (EST)
X-Mailer: ELM [version 2.4 PL25 PGP7]
MIME-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit

Fred,
Fred Cohen wrote: 

  >I'm beginning to think I should write UDPlisten as a C program - I think that: 
>
          >$this=pack($sockaddr, $AF_INET, $port, "\0\0\0\0");     # setup for bind 
>
  >is the source of the problem, but I'm not sure.  It succeeds in doing a 
  >bind, but to what I am not certain yet.  I tried port 1111 and it said 
  >it succeded, but then I used netcat to talk to port 1111 in UDP and it 
  >never triggered the recv: 
>
          >while ($addr = recv(S, $data, 65535, MSG_WAITALL))      # wait for a datagram 
>
  >It waits forever - meanwhile, I send some bytes with netcat and get no 
  >bytes back.  I am por at doing this stuf in perl, but I hate to 
  >implement things like the response system in C.  I am considering using 
  >netcat to do the listen and response and running it from perl, but that 
  >seems far more insane. Something like: 
>
          >nc -u -l $PORT | perl-script | netcat -u $PORT 
>
  Any notions on this? 
>
  >>FC 
   

I've been doing some digging on perl UDP servers and found an interesting article.  I need to play with the code and do 
some testing.  I've pasted it below.  There is code  for a simple UDP server. 

Earlier today, I tracked the problem in udplisten.pl to line 87 which is the same line you pasted above.  The value of 
$inetaddr is wrong. So either the unpack($partialaddr, $data) has failed or the recv(S, $data, 65535, 
MSG_WAITALL) is broken. 

I just got the kids in bed and have a few hours to play with this. My ISP is going to have intermittent outages tonight as 
they perform some Periodic Mayhem.  Coming from the SEC now.
 

Joe 

**** start of paste **** 
The following is unpublished source code of the Perl Cookbook. 
Comments welcome. 

--tom 

=head1 Setting up a UDP Client 

=head2 Problem 

You want to exchange messages with another process using 
UDP (datagrams). 

=head2 Solution 

To set up a UDP socket handle, either use the low-level Socket module: 

    use Socket; 
    socket(SockHandle, PF_INET, SOCK_DGRAM, getprotobyname("udp")) 
        || die "socket: $!"; 

or else use IO::Socket: 

    use IO::Socket; 
    $handle = IO::Socket::INET->new(Proto => 'udp') 
                || die "socket: $@";     # yes, it uses $@ here 

Then to send a message to a machine named $HOSTNAME on 
port number $PORTNO: 

    $ipaddr   = inet_aton($HOSTNAME)); 
    $portaddr = sockaddr_in($PORTNO, $ipaddr); 
    send(SockHandle, $MSG, 0, $portaddr) == length($MSG) 
            || die "cannot send to $HOSTNAME($PORTNO): $!"; 

And to receive a message of up to length $MAXLEN: 

    $portaddr = recv(SockHandle, $MSG, $MAXLEN, 0))      || die "recv: $!"; 
    ($portno, $ipaddr) = sockaddr_in($portaddr); 
    $host = gethostbyaddr($ipaddr, AF_INET); 
    print "$host($portno) said $MSG\n"; 

=head2 Discussion 

Datagram sockets are quite unlike stream sockets.  Streams provide 
sessions, giving the illusion of a stable connection.  You might think 
of them as working like a telephone call.  Somewhat expensive to set up, 
but once established, reliable and easy to use.  Datagrams, though, 
are more like the postal system.  Just as it'sj cheaper to mail your 
penpal on the other side a letter than to ring them up on the phone, 
datagrams are easier on the system than streams.  You send a small amount 
of information at a time, one message at a time.  But your messages' 
delivery isn't guaranteed, and they might even arrive in the wrong order. 
Like a small post box, the receiver's queue might fill up and cause 
further messages to be dropped. 

Why then, if datagrams are unreliable, do we have them?  Because some 
applications are most sensibly implemented in terms of datagrams. 
For instance, streaming audio where it's more important that the stream 
as a whole be preserved than every packet get through, especially if 
packets are being dropped because there's not enough bandwidth for 
them all.  Another use for datagrams is broadcasting, which corresponds 
to mass mailing of adverts in the postal model, and equally popular in 
most circles.  One use for broadcast packets it to send out a message 
to your local subnet saying "Hey, is there anybody around here who wants 
to be my server?" 

Because datagrams don't providing the illusion of a single durable 
connection, you get a little more freedom in how you use them.  Every 
You 
don't have to C<connect> your socket to the remote socket you're 
sending data to.  Instead, you can address each of your datagrams 
individually when you C<send>.  Assuming C<$remote_addr> is the 
results of a call to C<sockaddr_in>: 

    send(MYSOCKET, $msg_buffer, $flags, $remote_addr) || die "Can't send: $!\n"; 

The only flag argument used with any frequency is MSG_OOB, 
which lets you send and receive out-of-band data.  See the recipe 
``Communicating over a TCP Connection'' for more information on 
out-of-band data. 

The remote address should be a port and internet address combination 
returned by the Socket module's C<sockaddr_in> function.  If you 
want, you can call C<connect> on that address instead.  Then you 
can omit the last argument to your C<send>s, after which they'll 
all go to that recipient.  Unlike streams, you are free to reconnect 
to another machine with the same datagram socket. 

Here's a small example of a UDP program.  It contacts the 
UDP time port of the 
the machine whose name is given on the command line, or the local 
machine by default.   This doesn't work on all machines, but those 
with a server will send you back a 4-byte integer packed in network 
byte order that represents what time that machine thinks it is. 
The time returned, however, is in the number of seconds since 1900. 
You have to subtract the number of seconds between 1900 and 1970 
to feed that time to the C<localtime> or C<gmtime> conversion functions. 

    #!/usr/bin/perl 
    # clockdrift - compare another system's clock with this one 
    use strict; 
    use Socket; 

    my ($host, $him, $src, $port, $ipaddr, $ptime, $delta); 
    my $SECS_of_70_YEARS      = 2_208_988_800; 

    socket(MsgBox, PF_INET, SOCK_DGRAM, getprotobyname("udp"))  || die "socket: $!"; 
    $him = sockaddr_in(scalar(getservbyname("time", "udp")), inet_aton(shift || '127.1')); 
    defined(send(MsgBox, 0, 0, $him))                           || die "send: $!"; 
    defined($src = recv(MsgBox, $ptime, 4, 0))                  || die "recv: $!"; 
    ($port, $ipaddr) = sockaddr_in($src); 
    $host = gethostbyaddr($ipaddr, AF_INET); 
    my $delta = (unpack("N", $ptime) - $SECS_of_70_YEARS) - time(); 
    print "Clock on $host is $delta seconds ahead of this one.\n"; 

If the machine you're trying to contact isn't alive, or if its 
response is lost, you'll only realize this because your program 
will get stuck in the C<recv> waiting for an answer that will 
never come. 

=head2 See Also 

[* LOTS *] 

=head1 Setting up a UDP Server 

=head2 Problem 

You want to write a UDP server. 

=head2 Solution 

First C<bind> to the port the server is to be contacted 
on.  With IO::Socket, this is easily accomplished: 

    use IO::Socket; 
    $server = IO::Socket::INET->new(LocalPort => $server_port, 
                                    Proto     => "udp", 
        ) || die "Couldn't be a udp server on port $server_port : $@\n"; 

Then go into a loop receiving messages: 

    while ($him = $server->recv($datagram, $MAX_TO_READ, $flags)) { 
        # do something 
    } 

=head2 Discussion 

Life with UDP is much simpler than life with TCP.  Instead of accepting 
client connections one at a time and committing yourself to a long-time 
relationship, you just take messages from your clients as they come in. 
The C<recv> function returns the address of the sender, which you can 
then decode. 

Here's a small UDP-based server that just sits around waiting for 
messages.  Every time a message comes in, we send to whoever sent it the 
previous message, and save the old one. 

    #!/usr/bin/perl -w 
    # udpqotd - UDP message server 
    use strict; 
    use IO::Socket; 
    my($sock, $oldmsg, $newmsg, $hisaddr, $hishost, $MAXLEN, $PORTNO); 
    $MAXLEN = 1024; 
    $PORTNO = 5151; 
    $sock = IO::Socket::INET->new(LocalPort => $PORTNO, Proto => 'udp') 
                    || die "socket: $@"; 
    print "Awaiting UDP messages on port $PORTNO\n"; 
    $oldmsg = "This is the starting message."; 
    while ($sock->recv($newmsg, $MAXLEN)) { 
        my($port, $ipaddr) = sockaddr_in($sock->peername); 
        $hishost = gethostbyaddr($ipaddr, AF_INET); 
        print "Client $hishost said ``$newmsg''\n"; 
        $sock->send($oldmsg); 
        $oldmsg = "[$hishost] $newmsg"; 
    } 
    die "recv: $!"; 

This program is a little easier using IO::Socket than using the 
raw Socket module.  Notice we don't have to say where to send the 
message?  It's not as though we've connected; it's because the 
library kept track of who sent the last message and stored 
that information away on the $sock object.  The C<peername> 
method retrieved it for decoding. 

You can't use the I<telnet> program to talk to this server. 
You have to use a dedicate client.  Here's one. 

    #!/usr/bin/perl -w 
    # udpmsg - send a message to the udpquotd server 

    my($sock, $server_host, $msg, $port, $ipaddr, $hishost, 
       $MAXLEN, $PORTNO, $TIMEOUT, 
    ); 

    use IO::Socket; 
    use strict; 

    $MAXLEN  = 1024; 
    $PORTNO  = 5151; 
    $TIMEOUT = 5; 

    $server_host = shift; 
    $msg         = "@ARGV"; 
    $sock = IO::Socket::INET->new(Proto     => 'udp', 
                                  PeerPort  => $PORTNO, 
                                  PeerAddr  => $server_host, 
                                ); 
    $sock->send($msg) || die "send: $!"; 

    eval { 
        $SIG{ALRM} = sub { die "alarm time out" }; 
        alarm $TIMEOUT; 
        $sock->recv($msg, $MAXLEN)  || die "recv: $!"; 
        alarm 0; 
        1;  # return value from eval on normalcy 
    } || die "recv from $server_host timed out after $TIMEOUT seconds.\n"; 

    ($port, $ipaddr) = sockaddr_in($sock->peername); 
    $hishost = gethostbyaddr($ipaddr, AF_INET); 
    print "Server $hishost responded ``$msg''\n"; 

This time when we create the socket, we supply a peer host and 
port right at the start, allowing us to omit that information 
in the C<send>. 

We've added an C<alarm> timeout in case the server isn't responsive, 
maybe not even alive.  Because C<recv> is a blocking system call that 
there's no guarantee will ever return, we wrap it in a standard C<eval> 
block construct. 
 
 
  


