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