13.8. Eavesdropping

Sometimes you'd just like to listen in on the conversation between a browser and a server, like a gossip-hungry neighbor on a party line. Perhaps you're dealing with browsers whose caching behavior is questionable, and you don't want to guess what's really being fetched. You could use a connection-sniffing tool like tcpdump to monitor the traffic between the client and the server, but this generally requires superuser access to put the network interface into “promiscuous” mode. So try instead our proxylog tool:

#!/usr/bin/perl -w
use strict;

use Getopt::Std;
use IO::Socket;
use IO::Select;

sub usage
   {
   <<"EndUsage";
$0 lets you snoop on the conversation between a client
and server.

$0 -i clientport -o serverhost:port [-1] [-v]
        -i      incoming port to listen to
        -o      outgoing port to make connection to
        -1      only process one client request, then exit
        -v      verbose

Caveats:
     o  Does not handle multiple simultaneous connections.
     o  Reads are asynchronous, writes are synchronous.
     o  Messages larger than 64K will be segmented.
EndUsage
   }

use vars qw($opt_i $opt_o $opt_1 $opt_v);
getopts('i:o:1:v') and $opt_o or die usage;
my $proxy_port = $opt_i or die usage;
my ($server_host, $server_port) = $opt_o =~ /^(.+):(d+)$/
   or die usage;
my $verbose  = 1 if $opt_v;
my $only_one = 1 if $opt_1;

$SIG{TERM} = $SIG{INT} = $SIG{HUP} = &shutdown;

my $proxy = IO::Socket::INET->new(LocalPort => $proxy_port,
                                  Type      => SOCK_STREAM,
                                  Proto     => 'tcp',
                                  Reuse     => 1,
                                  Listen    => 1)
   or die "Can't listen on port $proxy_port: $!
";
print "[listening on port $proxy_port]
" if $verbose;

my ($client, $server);

OUTER:
while ($client = $proxy->accept)
   {
   my ($client_host, $client_port) =
         (gethostbyaddr($client->peeraddr, AF_INET)
           || $client->peerhost,
          $client->peerport);
   print "[connection from $client_host on $client_port]
"
      if $verbose;

   $server = IO::Socket::INET->new(PeerAddr => $server_host,
                                   PeerPort => $server_port,
                                   Proto    => 'tcp',
                                   Type     => SOCK_STREAM)
      or die "Can't connect to $server_host:$server_port
";
   print "[connected to server $server_host:$server_port]
"
      if $verbose;

   my $selector = IO::Select->new($client, $server);

CONNECTION:
   while (my @ready = $selector->can_read)
      {
      for my $sock (@ready)
         {
         my ($who, $target) = ($sock == $client
                               ? ('client', $server)
                               : ('server', $client));
         my $msgbuf;
         unless ($sock->sysread($msgbuf, 64 * 1024))
            {
            print "[$who closed connection]
" if $verbose;
            last CONNECTION;
            }
         (my $safebuf = $msgbuf) =~
            tr/11121540-176240-376/./cs;
         print "
===== U$whoE:
$safebuf";
         print "
" unless substr($safebuf, -1, 1) eq "
";
         $target->syswrite($msgbuf, length $msgbuf);
         }
      last OUTER if $only_one;
      }
   print "[closing connection]
" if $verbose;
   $client->shutdown(2) if $client;
   $server->shutdown(2) if $server;
   }

sub shutdown
   {
   print "[shutting down]
" if $verbose;
   $client->shutdown(2) if $client;
   $server->shutdown(2) if $server;
   $proxy ->shutdown(2);
   exit(0);
   }

Let's say that you want to see exactly what travels between the browser and server when you visit a URL like http://webhost/path/file.html. Run this script like so:

% proxylog -i 2222 -o webhost:80
				

Now the script is listening on port 2222. (You can pick any number you like—on a Unix system you need to be root to pick anything below 1024—but you want to avoid the numbers for any well-known services your computer might already use.)[2] Let's say that the machine on which you run proxylog is called listener.[3] Enter the URL http://listener:2222/path/file.html into your browser, and watch the transaction be printed out by proxylog:

[2] Like, for instance, 6667 (IRC), or 6699 (Napster).

[3] We've omitted domains like .com from the host names here, but you may need them when you run proxylog.

===== CLIENT:
GET / HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/4.72 [en] (Win98; I)
Pragma: no-cache
Host: 204.179.152.52:2222
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,
image/png, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8


===== SERVER:
HTTP/1.1 200 OK
Date: Tue, 01 Aug 2000 19:54:37 GMT
Server: Apache/1.3.12 (Unix)  (Red Hat/Linux) PHP/3.0.15
mod_perl/1.21
Last-Modified: Wed, 01 Mar 2000 18:37:44 GMT
ETag: "32ae2-9cf-38bd6378"
Accept-Ranges: bytes
Content-Length: 2511
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: text/html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
[...]

Any anchors or embedded URLs (such as images) that do not specify the Web server will also go through proxylog when they are fetched.[4]

[4] proxylog can be downloaded from www.perldebugged.com.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset