html/dns.pl


   1 # /DNS <nick>|<host>|<ip> ...
   2 # version 2.1.1
   3 # 
   4 # updated the script to fix a bug where the script would let
   5 # a trailing whitespace go through (ex: tab completion)
   6 # - inch <inch@stmpd.net>
   7 
   8 use strict;
   9 use Socket;
  10 use POSIX;
  11 
  12 use vars qw($VERSION %IRSSI); 
  13 $VERSION = "2.1.1";
  14 %IRSSI = (
  15     authors	=> "Timo \'cras\' Sirainen",
  16     contact	=> "tss\@iki.fi", 
  17     name	=> "dns",
  18     description	=> "/DNS <nick>|<host>|<ip> ...",
  19     license	=> "Public Domain",
  20     url		=> "http://irssi.org/",
  21     changed	=> "2002-03-04T22:47+0100"
  22 );
  23 
  24 my (%resolve_hosts, %resolve_nicks, %resolve_print); # resolve queues
  25 my $userhosts; # number of USERHOSTs currently waiting for reply
  26 my $lookup_waiting; # 1 if we're waiting a reply for host lookup
  27 
  28 # for the current host lookup
  29 my ($print_server, $print_host, $print_name, @print_ips);
  30 my ($input_skip_next, $input_query);
  31 
  32 my $pipe_tag;
  33 
  34 sub cmd_dns {
  35   my ($nicks, $server) = @_;
  36   return if !$nicks;
  37   $nicks =~ s/\s+$//; 
  38   # get list of nicks/hosts we want to know
  39   my $tag = !$server ? undef : $server->{tag};
  40   my $ask_nicks = "";
  41   my $print_error = 0;
  42   foreach my $nick (split(" ", $nicks)) {
  43     $nick = lc($nick);
  44     if ($nick =~ /[\.:]/) {
  45       # it's an IP or hostname
  46       $resolve_hosts{$nick} = $tag;
  47     } else {
  48       # it's nick
  49       if (!$print_error && (!$server || !$server->{connected})) {
  50 	$print_error = 1;
  51 	Irssi::print("Not connected to server");
  52       } else {
  53 	$resolve_nicks{$nick} = 1;
  54 	$ask_nicks .= "$nick ";
  55       }
  56     }
  57   }
  58 
  59   if ($ask_nicks ne "") {
  60     # send the USERHOST query
  61     $userhosts++;
  62     $server->redirect_event('userhost', 1, $ask_nicks, 0, 'redir dns failure', {
  63                             'event 302' => 'redir dns host',
  64                             '' => 'event empty' } );
  65     $server->send_raw("USERHOST :$nicks");
  66   }
  67 
  68   # ask the IPs/hostnames immediately
  69   host_lookup() if (!$lookup_waiting);
  70 }
  71 
  72 sub sig_failure {
  73   Irssi::print("Error getting hostname for nick");
  74   %resolve_nicks = () if (--$userhosts == 0);
  75 }
  76 
  77 sub sig_userhost {
  78   my ($server, $data) = @_;
  79   $data =~ s/^[^ ]* :?//;
  80   my @hosts = split(/ +/, $data);
  81 
  82   # move resolve_nicks -> resolve_hosts
  83   foreach my $host (@hosts) {
  84     if ($host =~ /^([^=\*]*)\*?=.(.*)@(.*)/) {
  85       my $nick = lc($1);
  86       my $user = $2;
  87       $host = lc($3);
  88 
  89       $resolve_hosts{$host} = $resolve_nicks{$nick};
  90       delete $resolve_nicks{$nick};
  91       $resolve_print{$host} = "[$nick!$user"."@"."$host]";
  92     }
  93   }
  94 
  95   if (--$userhosts == 0 && %resolve_nicks) {
  96     # unknown nicks - they didn't contain . or : so it can't be
  97     # IP or hostname.
  98     Irssi::print("Unknown nicks: ".join(' ', keys %resolve_nicks));
  99     %resolve_nicks = ();
 100   }
 101 
 102   host_lookup() if (!$lookup_waiting);
 103 }
 104 
 105 sub host_lookup {
 106   return if (!%resolve_hosts);
 107 
 108   my ($host) = keys %resolve_hosts;
 109   $print_server = $resolve_hosts{$host};
 110 
 111   $print_host = undef;
 112   $print_name = $resolve_print{$host};
 113   @print_ips = ();
 114 
 115   delete $resolve_hosts{$host};
 116   delete $resolve_print{$host};
 117 
 118   $input_query = $host;
 119   $input_skip_next = 0;
 120 
 121   # pipe is used to get the reply from child
 122   my ($rh, $wh);
 123   pipe($rh, $wh);
 124 
 125   # non-blocking host lookups with fork()ing
 126   my $pid = fork();
 127   if (!defined($pid)) {
 128     %resolve_hosts = ();
 129     %resolve_print = ();
 130     Irssi::print("Can't fork() - aborting");
 131     close($rh); close($wh);
 132     return;
 133   }
 134   $lookup_waiting++;
 135 
 136   if ($pid > 0) {
 137     # parent, wait for reply
 138     close($wh);
 139     Irssi::pidwait_add($pid);
 140     $pipe_tag = Irssi::input_add(fileno($rh), INPUT_READ, \&pipe_input, $rh);
 141     return;
 142   }
 143 
 144   my $text;
 145   eval {
 146     # child, do the lookup
 147     my $name = "";
 148     if ($host =~ /^[0-9\.]*$/) {
 149       # ip -> host
 150       $name = gethostbyaddr(inet_aton($host), AF_INET);
 151     } else {
 152       # host -> ip
 153       my @addrs = gethostbyname($host);
 154       if (@addrs) {
 155 	@addrs = map { inet_ntoa($_) } @addrs[4 .. $#addrs];
 156 	$name = join (" ", @addrs);
 157       }
 158     }
 159 
 160     $print_name = $input_query if !$print_name;
 161     if (!$name) {
 162       $text = "No information for $print_name";
 163     } else {
 164       $text = "$print_name: $name";
 165     }
 166   };
 167   $text = $! if (!$text);
 168 
 169   eval {
 170     # write the reply
 171     print($wh $text);
 172     close($wh);
 173   };
 174   POSIX::_exit(1);
 175 }
 176 
 177 sub pipe_input {
 178   my $rh = shift;
 179   my $text = <$rh>;
 180   close($rh);
 181 
 182   Irssi::input_remove($pipe_tag);
 183   $pipe_tag = -1;
 184 
 185   my $server = Irssi::server_find_tag($print_server);
 186   if ($server) {
 187     $server->print('', $text);
 188   } else {
 189     Irssi::print($text);
 190   }
 191 
 192   $lookup_waiting--;
 193   host_lookup();
 194 }
 195 
 196 Irssi::command_bind('dns', 'cmd_dns');
 197 Irssi::signal_add( {
 198         'redir dns failure' => \&sig_failure,
 199         'redir dns host' => \&sig_userhost } );