# /DNS <nick>|<host>|<ip> ... use Irssi; use strict; use Socket; use POSIX; use vars qw($VERSION %IRSSI); $VERSION = "2.1"; %IRSSI = ( authors => 'Timo Sirainen', name => 'dns', description => '/DNS <nick>|<host>|<ip> ...', license => 'Public Domain', changed => 'Sun Mar 10 23:23 EET 2002' ); my (%resolve_hosts, %resolve_nicks, %resolve_print); # resolve queues my $userhosts; # number of USERHOSTs currently waiting for reply my $lookup_waiting; # 1 if we're waiting a reply for host lookup # for the current host lookup my ($print_server, $print_host, $print_name, @print_ips); my ($input_skip_next, $input_query); my $pipe_tag; sub cmd_dns { my ($nicks, $server) = @_; return if !$nicks; # get list of nicks/hosts we want to know my $tag = !$server ? undef : $server->{tag}; my $ask_nicks = ""; my $print_error = 0; foreach my $nick (split(" ", $nicks)) { $nick = lc($nick); if ($nick =~ /[\.:]/) { # it's an IP or hostname $resolve_hosts{$nick} = $tag; } else { # it's nick if (!$print_error && (!$server || !$server->{connected})) { $print_error = 1; Irssi::print("Not connected to server"); } else { $resolve_nicks{$nick} = 1; $ask_nicks .= "$nick "; } } } if ($ask_nicks ne "") { # send the USERHOST query $userhosts++; $server->redirect_event('userhost', 1, $ask_nicks, 0, 'redir dns failure', { 'event 302' => 'redir dns host', '' => 'event empty' } ); $server->send_raw("USERHOST :$nicks"); } # ask the IPs/hostnames immediately host_lookup() if (!$lookup_waiting); } sub sig_failure { Irssi::print("Error getting hostname for nick"); %resolve_nicks = () if (--$userhosts == 0); } sub sig_userhost { my ($server, $data) = @_; $data =~ s/^[^ ]* :?//; my @hosts = split(/ +/, $data); # move resolve_nicks -> resolve_hosts foreach my $host (@hosts) { if ($host =~ /^([^=\*]*)\*?=.(.*)@(.*)/) { my $nick = lc($1); my $user = $2; $host = lc($3); $resolve_hosts{$host} = $resolve_nicks{$nick}; delete $resolve_nicks{$nick}; $resolve_print{$host} = "[$nick!$user"."@"."$host]"; } } if (--$userhosts == 0 && %resolve_nicks) { # unknown nicks - they didn't contain . or : so it can't be # IP or hostname. Irssi::print("Unknown nicks: ".join(' ', keys %resolve_nicks)); %resolve_nicks = (); } host_lookup() if (!$lookup_waiting); } sub host_lookup { return if (!%resolve_hosts); my ($host) = keys %resolve_hosts; $print_server = $resolve_hosts{$host}; $print_host = undef; $print_name = $resolve_print{$host}; @print_ips = (); delete $resolve_hosts{$host}; delete $resolve_print{$host}; $input_query = $host; $input_skip_next = 0; # pipe is used to get the reply from child my ($rh, $wh); pipe($rh, $wh); # non-blocking host lookups with fork()ing my $pid = fork(); if (!defined($pid)) { %resolve_hosts = (); %resolve_print = (); Irssi::print("Can't fork() - aborting"); close($rh); close($wh); return; } $lookup_waiting++; if ($pid > 0) { # parent, wait for reply close($wh); Irssi::pidwait_add($pid); $pipe_tag = Irssi::input_add(fileno($rh), INPUT_READ, \&pipe_input, $rh); return; } my $text; eval { # child, do the lookup my $name = ""; if ($host =~ /^[0-9\.]*$/) { # ip -> host $name = gethostbyaddr(inet_aton($host), AF_INET); } else { # host -> ip my @addrs = gethostbyname($host); if (@addrs) { @addrs = map { inet_ntoa($_) } @addrs[4 .. $#addrs]; $name = join (" ", @addrs); } } $print_name = $input_query if !$print_name; if (!$name) { $text = "No information for $print_name"; } else { $text = "$print_name: $name"; } }; $text = $! if (!$text); eval { # write the reply print($wh $text); close($wh); }; POSIX::_exit(1); } sub pipe_input { my $rh = shift; my $text = <$rh>; close($rh); Irssi::input_remove($pipe_tag); $pipe_tag = -1; my $server = Irssi::server_find_tag($print_server); if ($server) { $server->print('', $text); } else { Irssi::print($text); } $lookup_waiting--; host_lookup(); } Irssi::command_bind('dns', 'cmd_dns'); Irssi::signal_add( { 'redir dns failure' => \&sig_failure, 'redir dns host' => \&sig_userhost } );