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 } );