summaryrefslogtreecommitdiffstats
path: root/scripts/dns.pl
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/dns.pl')
-rw-r--r--scripts/dns.pl236
1 files changed, 236 insertions, 0 deletions
diff --git a/scripts/dns.pl b/scripts/dns.pl
new file mode 100644
index 0000000..9e9f478
--- /dev/null
+++ b/scripts/dns.pl
@@ -0,0 +1,236 @@
+# /DNS <nick>|<host>|<ip> ...
+#
+# v2.2
+# add ipv6 support
+# v2.1.1
+# updated the script to fix a bug where the script would let
+# a trailing whitespace go through (ex: tab completion)
+# - inch <inch@stmpd.net>
+
+use strict;
+use Socket;
+use POSIX;
+
+use vars qw($VERSION %IRSSI);
+$VERSION = "2.2";
+%IRSSI = (
+ authors => "Timo \'cras\' Sirainen",
+ contact => "tss\@iki.fi",
+ name => "dns",
+ description => "/DNS <nick>|<host>|<ip> ...",
+ license => "Public Domain",
+ url => "http://irssi.org/",
+ changed => "2019-01-24"
+);
+
+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;
+ $nicks =~ s/\s+$//;
+ # 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 dns {
+ my ($host) =@_;
+ my %hints = (socktype => SOCK_STREAM);
+ my ($err, @res) = Socket::getaddrinfo($host, "http", \%hints);
+ my @res1;
+ if ($err ==0 ) {
+ foreach(@res) {
+ if ($_->{family}==AF_INET) {
+ my ($proto,$ip)=unpack_sockaddr_in($_->{addr});
+ push @res1, Socket::inet_ntop(AF_INET,$ip);
+ }
+ if ($_->{family}==AF_INET6) {
+ my ($proto,$ip)=unpack_sockaddr_in6($_->{addr});
+ push @res1, Socket::inet_ntop(AF_INET6,$ip);
+ }
+ }
+ return join(' ',@res1);
+ }
+}
+
+sub rdns {
+ my ($host) =@_;
+ my %hints = (socktype => SOCK_STREAM);
+ my ($err, @res) = Socket::getaddrinfo($host, "http", \%hints);
+ my @res1;
+ if ($err ==0 ) {
+ foreach(@res) {
+ my ($err, $hostname, $servicename) = Socket::getnameinfo $_->{addr};
+ if ($err ==0) {
+ push @res1, $hostname;
+ }
+ }
+ return join(' ',@res1);
+ }
+}
+
+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\.]*$/ || $host =~ m/^[0-9a-f:]*$/) {
+ # ip -> host
+ #$name = gethostbyaddr(inet_aton($host), AF_INET);
+ $name = rdns($host);
+ } else {
+ # host -> ip
+ $name = dns($host);
+ }
+
+ $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 } );
+
+# vim:set sw=2 ts=8: