1 | #! &PERL& -w |
---|
2 | # -*- perl -*- |
---|
3 | ###################################################################### |
---|
4 | # make-ssh-known-hosts.pl -- Make ssh-known-hosts file |
---|
5 | # Copyright (c) 1995 Tero Kivinen |
---|
6 | # All Rights Reserved. |
---|
7 | # |
---|
8 | # Make-ssh-known-hosts is distributed in the hope that it will be |
---|
9 | # useful, but WITHOUT ANY WARRANTY. No author or distributor accepts |
---|
10 | # responsibility to anyone for the consequences of using it or for |
---|
11 | # whether it serves any particular purpose or works at all, unless he |
---|
12 | # says so in writing. Refer to the GNU General Public License for full |
---|
13 | # details. |
---|
14 | # |
---|
15 | # Everyone is granted permission to copy, modify and redistribute |
---|
16 | # make-ssh-known-hosts, but only under the conditions described in |
---|
17 | # the GNU General Public License. A copy of this license is supposed to |
---|
18 | # have been given to you along with make-ssh-known-hosts so you can |
---|
19 | # know your rights and responsibilities. It should be in a file named |
---|
20 | # gnu-COPYING-GPL. Among other things, the copyright notice and this notice |
---|
21 | # must be preserved on all copies. |
---|
22 | ###################################################################### |
---|
23 | # Program: make-ssh-known-hosts.pl |
---|
24 | # $Source: /afs/dev.mit.edu/source/repository/third/ssh/make-ssh-known-hosts.pl,v $ |
---|
25 | # Author : $Author: danw $ |
---|
26 | # |
---|
27 | # (C) Tero Kivinen 1995 <Tero.Kivinen@hut.fi> |
---|
28 | # |
---|
29 | # Creation : 19:52 Jun 27 1995 kivinen |
---|
30 | # Last Modification : 00:07 Jul 8 1998 kivinen |
---|
31 | # Last check in : $Date: 1999-03-08 17:42:54 $ |
---|
32 | # Revision number : $Revision: 1.1.1.3 $ |
---|
33 | # State : $State: Exp $ |
---|
34 | # Version : 1.343 |
---|
35 | # Edit time : 242 min |
---|
36 | # |
---|
37 | # Description : Make ssh-known-host file from dns data. |
---|
38 | # |
---|
39 | # $Log: not supported by cvs2svn $ |
---|
40 | # Revision 1.6 1998/07/08 00:44:23 kivinen |
---|
41 | # Fixed to understand bind 8 nslookup output. |
---|
42 | # |
---|
43 | # Revision 1.5 1998/04/30 01:53:33 kivinen |
---|
44 | # Moved kill before close and added sending SIGINT first and |
---|
45 | # then 1 second sleep before sending SIGKILL. |
---|
46 | # |
---|
47 | # Revision 1.4 1998/04/17 00:39:19 kivinen |
---|
48 | # Changed to close ssh program filedescriptor before killing it. |
---|
49 | # Removed ^ from the password matching prompt. |
---|
50 | # |
---|
51 | # Revision 1.3 1997/04/17 04:21:27 kivinen |
---|
52 | # Changed to use 3des by default. |
---|
53 | # |
---|
54 | # Revision 1.2 1997/03/26 07:14:01 kivinen |
---|
55 | # Added EWOULDBLOCK. |
---|
56 | # |
---|
57 | # Revision 1.1.1.1 1996/02/18 21:38:10 ylo |
---|
58 | # Imported ssh-1.2.13. |
---|
59 | # |
---|
60 | # Revision 1.4 1995/10/02 01:23:45 ylo |
---|
61 | # Ping packet size fixes from Kivinen. |
---|
62 | # |
---|
63 | # Revision 1.3 1995/08/29 22:37:39 ylo |
---|
64 | # Now uses GlobalKnownHostsFile and UserKnownHostsFile. |
---|
65 | # |
---|
66 | # Revision 1.2 1995/07/15 13:26:37 ylo |
---|
67 | # Changes from kivinen. |
---|
68 | # |
---|
69 | # Revision 1.1.1.1 1995/07/12 22:41:05 ylo |
---|
70 | # Imported ssh-1.0.0. |
---|
71 | # |
---|
72 | # |
---|
73 | # |
---|
74 | # If you have any useful modifications or extensions please send them to |
---|
75 | # Tero.Kivinen@hut.fi |
---|
76 | # |
---|
77 | ###################################################################### |
---|
78 | # initialization |
---|
79 | |
---|
80 | require 5.000; |
---|
81 | use Getopt::Long; |
---|
82 | use FileHandle; |
---|
83 | use POSIX; |
---|
84 | use Socket; |
---|
85 | use Fcntl; |
---|
86 | |
---|
87 | $version = ' $Id: make-ssh-known-hosts.pl,v 1.1.1.3 1999-03-08 17:42:54 danw Exp $ '; |
---|
88 | |
---|
89 | $command_line = "$0 "; |
---|
90 | foreach $a (@ARGV) { |
---|
91 | $command_line .= $a . " "; |
---|
92 | } |
---|
93 | STDERR->autoflush(1); |
---|
94 | |
---|
95 | ###################################################################### |
---|
96 | # default values for options |
---|
97 | |
---|
98 | $debug = 5; |
---|
99 | $defserver = ''; |
---|
100 | $bell='\a'; |
---|
101 | $public_key = '/etc/ssh_host_key.pub'; |
---|
102 | $private_ssh_known_hosts = "/tmp/ssh_known_hosts$$"; |
---|
103 | $timeout = 60; |
---|
104 | $ping_timeout = 3; |
---|
105 | $passwordtimeout = undef; |
---|
106 | $trustdaemon = 1; |
---|
107 | $domainnamesplit = 0; |
---|
108 | $recursive = 1; |
---|
109 | |
---|
110 | ###################################################################### |
---|
111 | # Programs and their options |
---|
112 | |
---|
113 | $nslookup = "nslookup"; |
---|
114 | |
---|
115 | $ssh="ssh -a -c 3des -x -o 'ConnectionAttempts 1' -o 'FallBackToRsh no' -o 'GlobalKnownHostsFile /dev/null' -o 'KeepAlive yes' -o 'StrictHostKeyChecking no' -o 'UserKnownHostsFile $private_ssh_known_hosts'"; |
---|
116 | $sshdisablepasswordoption="-o 'BatchMode yes' -o 'PasswordAuthentication no'"; |
---|
117 | |
---|
118 | ###################################################################### |
---|
119 | # Cleanup and initialization |
---|
120 | |
---|
121 | unlink($private_ssh_known_hosts); |
---|
122 | $sockaddr = 'S n a4 x8'; |
---|
123 | ($junk, $junk, $sshport) = getservbyname("ssh", "tcp"); |
---|
124 | if (!defined($sshport)) { |
---|
125 | $sshport = 22; |
---|
126 | } |
---|
127 | ($tcpprotoname, $junk, $tcpproto) = getprotobyname('tcp'); |
---|
128 | defined($tcpprotoname) || die "getprotobyname : $!"; |
---|
129 | |
---|
130 | ###################################################################### |
---|
131 | # Parse options |
---|
132 | |
---|
133 | GetOptions("initialdns=s", "server=s", "subdomains=s", |
---|
134 | "debug=i", "timeout=i", "passwordtimeout=i", |
---|
135 | "trustdaemon!", "domainnamesplit", "silent", |
---|
136 | "nslookup=s", "pingtimeout=i", "recursive!", |
---|
137 | "keyscan", |
---|
138 | "ssh=s") |
---|
139 | || die "Getopt : $!"; |
---|
140 | |
---|
141 | if (defined($opt_initialdns)) { $defserver = $opt_initialdns; } |
---|
142 | |
---|
143 | if (defined($opt_server)) { $server = $opt_server; } |
---|
144 | |
---|
145 | if (defined($opt_subdomains)) { @subdomains = split(/,/, $opt_subdomains); } |
---|
146 | |
---|
147 | if (defined($opt_debug)) { $debug = $opt_debug; } |
---|
148 | |
---|
149 | if (defined($opt_timeout)) { $timeout = $opt_timeout; } |
---|
150 | |
---|
151 | if (defined($opt_pingtimeout)) { $ping_timeout = $opt_pingtimeout; } |
---|
152 | |
---|
153 | if (defined($opt_passwordtimeout)) { |
---|
154 | $passwordtimeout = $opt_passwordtimeout; |
---|
155 | $sshdisablepasswordoption = ''; |
---|
156 | } |
---|
157 | |
---|
158 | if (defined($opt_trustdaemon)) { $trustdaemon = $opt_trustdaemon; } |
---|
159 | |
---|
160 | if (defined($opt_recursive)) { $recursive = $opt_recursive; } |
---|
161 | |
---|
162 | if (defined($opt_domainnamesplit)) { $domainnamesplit = $opt_domainnamesplit; } |
---|
163 | |
---|
164 | if (defined($opt_silent)) { $bell = ''; } |
---|
165 | |
---|
166 | if (defined($opt_nslookup)) { $nslookup = $opt_nslookup; } |
---|
167 | |
---|
168 | if (defined($opt_ssh)) { $ssh = $opt_ssh; } else { |
---|
169 | $ssh = "$ssh $sshdisablepasswordoption"; |
---|
170 | } |
---|
171 | |
---|
172 | if ($#ARGV == 0) { |
---|
173 | $domain = "\L$ARGV[0]\E"; |
---|
174 | $grep_yes = '.*'; |
---|
175 | $grep_no = '^$'; |
---|
176 | } elsif ($#ARGV == 1) { |
---|
177 | $domain = "\L$ARGV[0]\E"; |
---|
178 | $grep_yes = $ARGV[1]; |
---|
179 | $grep_no = '^$'; |
---|
180 | } elsif ($#ARGV == 2) { |
---|
181 | $domain = "\L$ARGV[0]\E"; |
---|
182 | $grep_yes = $ARGV[1]; |
---|
183 | $grep_no = $ARGV[2]; |
---|
184 | } else { |
---|
185 | print(STDERR "$0 [--initialdns initial_dns_server] [--server dns_server] [--subdomains sub.sub.domain,sub.sub,sub,] [--debug debug_level] [--timeout ssh_exec_timeout_in_secs] [--pingtimeout ping_timeout_in_secs] [--passwordtimeout timeout_for_password_in_secs] [--notrustdaemon] [--norecursive] [--domainnamesplit] [--silent] [--keyscan] [--nslookup path_to_nslookup] [--ssh path_to_ssh] full.domain [ host_info_take_regexp [ host_info_remove_regex ]]\n"); |
---|
186 | exit(1); |
---|
187 | } |
---|
188 | |
---|
189 | ###################################################################### |
---|
190 | # Check that ssh program exists |
---|
191 | |
---|
192 | if (system("$ssh > /dev/null 2>&1 ") != 256) { |
---|
193 | print(STDERR "Error: Could not run ssh program ($ssh): $!\nError: Try giving the path to it with --ssh option\n"); |
---|
194 | exit(1); |
---|
195 | } |
---|
196 | |
---|
197 | ###################################################################### |
---|
198 | # Generate subdomains list |
---|
199 | |
---|
200 | if (!$domainnamesplit) { |
---|
201 | debug(6, "Auto splitting host entries"); |
---|
202 | } elsif (!defined(@subdomains)) { |
---|
203 | debug(6, "Generating subdomain list"); |
---|
204 | |
---|
205 | # split domain to pieces |
---|
206 | @domain_pieces = split(/\./, $domain); |
---|
207 | |
---|
208 | # add empty domain part |
---|
209 | push(@subdomains, ''); |
---|
210 | |
---|
211 | # add rest parts, except the one before full domain name |
---|
212 | $entry=''; |
---|
213 | for(; $#domain_pieces > 1; ) { |
---|
214 | $entry .= "." . shift(@domain_pieces); |
---|
215 | push(@subdomains, $entry); |
---|
216 | } |
---|
217 | |
---|
218 | # add full domain name |
---|
219 | push(@subdomains, ".$domain"); |
---|
220 | debug(5, "Subdomain list: " . join(',', @subdomains)); |
---|
221 | } else { |
---|
222 | debug(5, "Using given subdomain list:" . join(',', @subdomains)); |
---|
223 | } |
---|
224 | |
---|
225 | ###################################################################### |
---|
226 | # finding SOA entry for domain |
---|
227 | |
---|
228 | @other_servers = (); |
---|
229 | if (!defined($server)) { |
---|
230 | debug(6, "Finding DNS database SOA entry"); |
---|
231 | |
---|
232 | ($server, @other_servers) = find_soa($domain, $defserver); |
---|
233 | |
---|
234 | if (!defined($server)) { |
---|
235 | print(STDERR "Error: Could not find DNS SOA entry from default dns server\nError: Try giving the initial nameserver with --initialdns option\n"); |
---|
236 | exit(1); |
---|
237 | } else { |
---|
238 | debug(5, "DNS server found : $server"); |
---|
239 | } |
---|
240 | } else { |
---|
241 | debug(5, "Using given DNS server : $server"); |
---|
242 | } |
---|
243 | |
---|
244 | ###################################################################### |
---|
245 | # Print header |
---|
246 | |
---|
247 | ($name, $junk, $junk, $junk, $junk, $junk, $gecos) = getpwuid($<); |
---|
248 | $gecos =~ s/,.*$//g; |
---|
249 | |
---|
250 | if (!defined($opt_keyscan)) { |
---|
251 | print(STDOUT "# This file is generated with make-ssh-known-hosts.pl\n"); |
---|
252 | print(STDOUT "#$version\n"); |
---|
253 | print(STDOUT "# with command line :\n"); |
---|
254 | print(STDOUT "# $command_line\n"); |
---|
255 | print(STDOUT "#\n"); |
---|
256 | print(STDOUT "# The script was run by $gecos ($name) at " . localtime() . "\n"); |
---|
257 | print(STDOUT "# using perl ($^X) version $].\n"); |
---|
258 | } |
---|
259 | |
---|
260 | ###################################################################### |
---|
261 | # Get DNS database list from server |
---|
262 | |
---|
263 | do { |
---|
264 | $domains_done{$domain} = 1; |
---|
265 | delete $domains_waiting{$domain}; |
---|
266 | |
---|
267 | $hostcnt = 0; |
---|
268 | $cnamecnt = 0; |
---|
269 | $lines = 0; |
---|
270 | $soa = 0; |
---|
271 | undef %host; |
---|
272 | undef %cname; |
---|
273 | undef %hostdata; |
---|
274 | |
---|
275 | dnsagain: |
---|
276 | debug(1, "Getting DNS database for $domain from server $server"); |
---|
277 | open(DNS, "echo ls -d $domain | nslookup - $server 2>&1 |") || |
---|
278 | die "Error: Could not start nslookup to make dns list : $!\nError: Try giving --nslookup option and telling the path to nslookup program\n"; |
---|
279 | |
---|
280 | while(<DNS>) { |
---|
281 | $lines++; |
---|
282 | chomp; |
---|
283 | undef $hostname if/^\s*$/; |
---|
284 | if (/^\s{0,1}([a-zA-Z0-9-]\S*)/) { |
---|
285 | $hostname = "\L$1\E"; |
---|
286 | } |
---|
287 | next unless defined $hostname; |
---|
288 | if (/^.*\s(SOA)\s+(.*)\s*$/ || $hostname eq "SOA") { |
---|
289 | undef $soa if(/^.*\s(SOA)\s+(.*)\s*$/); |
---|
290 | $data = $_ if ($hostname eq "SOA"); |
---|
291 | $data = $2 unless $hostname eq "SOA"; |
---|
292 | $data =~ s/\s*;.*$//; |
---|
293 | $data =~ s/^\s+//; |
---|
294 | if( defined $soa ) { |
---|
295 | $soa .= " \L$data\E"; |
---|
296 | } else { |
---|
297 | $soa = "\L$data\E"; |
---|
298 | } |
---|
299 | $hostname = "SOA"; |
---|
300 | } elsif (/^.*\s(A|CNAME|NS)\s+(.*)\s*$/) { |
---|
301 | $host = $hostname; |
---|
302 | $field = "\L$1\E"; |
---|
303 | $data = "\L$2\E"; |
---|
304 | debug(70, "Line = /$host/$field/$data/"); |
---|
305 | if ($host !~ /\.$/) { |
---|
306 | $host .= ".$domain"; |
---|
307 | } else { |
---|
308 | $host =~ s/\.$//g; |
---|
309 | } |
---|
310 | if ($field eq "a") { |
---|
311 | if ($host =~ /$domain$/) { |
---|
312 | if (defined($host{$host})) { |
---|
313 | $host{$host} .= ",$data"; |
---|
314 | } else { |
---|
315 | $host{$host} = "$data"; |
---|
316 | $hostcnt++; |
---|
317 | } |
---|
318 | debug(30, "$host A == $host{$host}"); |
---|
319 | } |
---|
320 | } elsif ($field eq "cname") { |
---|
321 | if ($data !~ /\.$/ && ! /^\s/ ) { |
---|
322 | $data .= ".$domain"; |
---|
323 | } else { |
---|
324 | $data =~ s/\.$//g; |
---|
325 | } |
---|
326 | if ($host =~ /$domain$/) { |
---|
327 | if (defined($cname{$data})) { |
---|
328 | $cname{$data} .= ",$host"; |
---|
329 | } else { |
---|
330 | $cname{$data} = "$host"; |
---|
331 | $cnamecnt++; |
---|
332 | } |
---|
333 | debug(30, "$host CNAME $data"); |
---|
334 | $junk = $data; |
---|
335 | $data = $host; |
---|
336 | $host = $junk; |
---|
337 | } |
---|
338 | } elsif ($field eq "ns") { |
---|
339 | if (!defined($domains_done{$host})) { |
---|
340 | if (!defined($domains_waiting{$host})) { |
---|
341 | debug(10, "Adding subdomain $host to domains list, with NS $data"); |
---|
342 | $domains_waiting{$host} = $data; |
---|
343 | push(@domains_waiting, $host); |
---|
344 | } else { |
---|
345 | debug(10, "Adding NS $data for domain $host"); |
---|
346 | $domains_waiting{$host} .= ",$data"; |
---|
347 | } |
---|
348 | } |
---|
349 | } |
---|
350 | if (!defined($hostdata{$host})) { |
---|
351 | $hostdata{$host} = "$host\n$field=$data\n"; |
---|
352 | } else { |
---|
353 | $hostdata{$host} .= "$field=$data\n"; |
---|
354 | } |
---|
355 | } |
---|
356 | } |
---|
357 | close(DNS); |
---|
358 | if ($hostcnt == 0 && $cnamecnt == 0) { |
---|
359 | if ($#other_servers != -1) { |
---|
360 | $server = shift(@other_servers); |
---|
361 | goto dnsagain; |
---|
362 | } |
---|
363 | } |
---|
364 | debug(1, "Found $hostcnt hosts, $cnamecnt CNAMEs (total $lines lines)"); |
---|
365 | if (!defined($opt_keyscan)) { |
---|
366 | print(STDOUT "#\n"); |
---|
367 | print(STDOUT "# Domain = $domain, server = $server\n"); |
---|
368 | print(STDOUT "# Found $hostcnt hosts, $cnamecnt CNAMEs (total $lines lines)\n"); |
---|
369 | print(STDOUT "# SOA = $soa\n"); |
---|
370 | print(STDOUT "#\n"); |
---|
371 | } |
---|
372 | |
---|
373 | ###################################################################### |
---|
374 | # Loop through hosts and try to connect to hosts |
---|
375 | |
---|
376 | foreach $i (sort (keys %host)) { |
---|
377 | debug(50, "Host = $i, Hostdata = $hostdata{$i}"); |
---|
378 | if ($hostdata{$i} =~ /$grep_yes/im && |
---|
379 | $hostdata{$i} !~ /$grep_no/im && |
---|
380 | $i !~ /^localhost\./ && |
---|
381 | $host{$i} !~ /^127.0.0.1$|^127.0.0.1,|,127.0.0.1$|,127.0.0.1,/) { |
---|
382 | debug(2, "Trying host $i"); |
---|
383 | |
---|
384 | @hostnames = (); |
---|
385 | if (defined($cname{$i})) { |
---|
386 | expand($i, \@hostnames, \@subdomains); |
---|
387 | foreach $j (split(/,/, $cname{$i})) { |
---|
388 | expand($j, \@hostnames, \@subdomains); |
---|
389 | } |
---|
390 | } else { |
---|
391 | expand($i, \@hostnames, \@subdomains); |
---|
392 | } |
---|
393 | foreach $j (split(/,/, $host{$i})) { |
---|
394 | push(@hostnames, $j); |
---|
395 | } |
---|
396 | $hostnames = join(',', (@hostnames)); |
---|
397 | |
---|
398 | if (defined($opt_keyscan)) { |
---|
399 | printf(STDOUT "$host{$i}\t$hostnames\n"); |
---|
400 | } elsif (try_ping($i, $host{$i})) { |
---|
401 | $trusted = 1; |
---|
402 | $err = 'Timeout expired'; |
---|
403 | $ssh_key = try_ssh("$i"); |
---|
404 | if (!defined($ssh_key)) { |
---|
405 | $ssh_key = find_host_from_known_hosts($i); |
---|
406 | $trusted = 0; |
---|
407 | } |
---|
408 | if (defined($ssh_key)) { |
---|
409 | if ($trusted) { |
---|
410 | debug(2, "Ssh to $i succeded"); |
---|
411 | } else { |
---|
412 | debug(2, "Ssh to $i failed, using local known_hosts entry"); |
---|
413 | } |
---|
414 | debug(4, "adding entries : $hostnames"); |
---|
415 | $ssh_key =~ s/root@//i; |
---|
416 | if (!$trusted && !$trustdaemon) { |
---|
417 | print(STDOUT "# $hostnames $ssh_key\n"); |
---|
418 | } else { |
---|
419 | print(STDOUT "$hostnames $ssh_key\n"); |
---|
420 | } |
---|
421 | } else { |
---|
422 | debug(2, "ssh failed : $err"); |
---|
423 | } |
---|
424 | } else { |
---|
425 | debug(2, "ping failed"); |
---|
426 | } |
---|
427 | } else { |
---|
428 | debug(10, "Skipped host $i"); |
---|
429 | } |
---|
430 | } |
---|
431 | again: |
---|
432 | $domain = shift(@domains_waiting); |
---|
433 | if (defined($domain)) { |
---|
434 | $server = $domains_waiting{$domain}; |
---|
435 | @other_servers = split(',', $server); |
---|
436 | $server = shift(@other_servers); |
---|
437 | ($server, @other_servers) = find_soa($domain, $server); |
---|
438 | if(!defined($server)) { |
---|
439 | debug(1, "Skipping domain $domain because no DNS SOA entry found"); |
---|
440 | $domains_done{$domain} = 1; |
---|
441 | delete $domains_waiting{$domain}; |
---|
442 | goto again; |
---|
443 | } |
---|
444 | } |
---|
445 | } while ($recursive && defined($domain)); |
---|
446 | |
---|
447 | unlink($private_ssh_known_hosts); |
---|
448 | exit (0); |
---|
449 | |
---|
450 | ###################################################################### |
---|
451 | # try_ping -- try to ping to host and return 1 if success |
---|
452 | # $success = try_ping($host, $list_ip_addrs); |
---|
453 | |
---|
454 | sub try_ping { |
---|
455 | my($host, $ipaddrs) = @_; |
---|
456 | my(@ipaddrs, $ipaddr, $serv, $ip); |
---|
457 | my($rin, $rout, $win, $wout, $nfound, $tmout, $buf, $len, $ret, $err); |
---|
458 | |
---|
459 | $buf = ''; |
---|
460 | debug(51,"Trying to ping host $host"); |
---|
461 | @ipaddrs = split(/,/, $ipaddrs); |
---|
462 | |
---|
463 | while ($ipaddr = shift(@ipaddrs)) { |
---|
464 | |
---|
465 | debug(55,"Trying ipaddr $ipaddr"); |
---|
466 | |
---|
467 | #initialize socket |
---|
468 | socket(PING, PF_INET, SOCK_STREAM, $tcpproto) || |
---|
469 | die "socket failed : $!"; |
---|
470 | setsockopt(PING, SOL_SOCKET, SO_REUSEADDR, 1) || |
---|
471 | die "setsockopt failed : $!"; |
---|
472 | PING->autoflush(1); |
---|
473 | fcntl(PING, F_SETFL, fcntl(PING, F_GETFL, 0) | POSIX::O_NONBLOCK) || |
---|
474 | die "fcntl failed : $!"; |
---|
475 | |
---|
476 | $ip = pack('C4', split(/\./, $ipaddr, 4)); |
---|
477 | $serv = pack($sockaddr, AF_INET, $sshport, $ip); |
---|
478 | |
---|
479 | again: |
---|
480 | # try connect |
---|
481 | $ret = connect(PING, $serv); |
---|
482 | $err = $!; |
---|
483 | if (!$ret) { |
---|
484 | debug(60, "Connect failed : $err"); |
---|
485 | if ($err == EINTR) { |
---|
486 | goto again; |
---|
487 | } |
---|
488 | # socket not yet connected, wait for result, it will |
---|
489 | # wake up for writing when done |
---|
490 | $tmout = $ping_timeout; |
---|
491 | |
---|
492 | $rin = ''; |
---|
493 | $win = ''; |
---|
494 | vec($rin, fileno(PING), 1) = 1; |
---|
495 | vec($win, fileno(PING), 1) = 1; |
---|
496 | debug(60, "Waiting in select, rin = " . unpack('H*', $rin) . |
---|
497 | ", win = " . unpack('H*', $win)); |
---|
498 | ($nfound) = select($rout = $rin, $wout = $win, undef, $tmout); |
---|
499 | $err = $!; |
---|
500 | debug(80, "Select returned $nfound, rout = " . unpack('H*', $rout) . |
---|
501 | ", wout = " . unpack('H*', $wout)); |
---|
502 | if ($nfound != 0) { |
---|
503 | # connect done, read the status with sysread |
---|
504 | $ret = sysread(PING, $buf, 1); |
---|
505 | $err = $!; |
---|
506 | if (defined($ret) || $err == EAGAIN || $err == EWOULDBLOCK) { |
---|
507 | debug(60, "Select ok, read ok ($err), returning ok"); |
---|
508 | # connection done, return ok |
---|
509 | shutdown(PING, 2); |
---|
510 | close(PING); |
---|
511 | return 1; |
---|
512 | } else { |
---|
513 | # connection failed, try next ipaddr |
---|
514 | debug(60, "Select ok, read failed : $err, trying next"); |
---|
515 | close(PING); |
---|
516 | } |
---|
517 | } else { |
---|
518 | # timeout exceeded, try next ipaddr |
---|
519 | debug(60, "Select failed : $err, trying next"); |
---|
520 | close(PING); |
---|
521 | } |
---|
522 | } else { |
---|
523 | # connect succeeded, return ok. |
---|
524 | debug(60, "Connect ok, returning ok"); |
---|
525 | shutdown(PING, 2); |
---|
526 | close(PING); |
---|
527 | return 1; |
---|
528 | } |
---|
529 | } |
---|
530 | debug(60, "Returning fail"); |
---|
531 | return 0; |
---|
532 | } |
---|
533 | |
---|
534 | ###################################################################### |
---|
535 | # try_ssh -- try ssh connection to host and return ssh_key if success |
---|
536 | # if failure return undef, and set $err string to contain error message. |
---|
537 | # $ssh_key = try_ssh($host); |
---|
538 | |
---|
539 | sub try_ssh { |
---|
540 | my($host) = @_; |
---|
541 | my($buf, $ret, $pos, $pid, $rin, $nfound, $tmout); |
---|
542 | |
---|
543 | $pid = open(SSH, "$ssh $host cat $public_key 2>&1 |"); |
---|
544 | $err = undef; |
---|
545 | |
---|
546 | if ($pid == 0) { |
---|
547 | $err = "could not open ssh connection to host"; |
---|
548 | return undef; |
---|
549 | } |
---|
550 | $ret = 1; |
---|
551 | $pos = 0; |
---|
552 | $buf = ''; |
---|
553 | $tmout = $timeout; |
---|
554 | debug(10, "Starting ssh select loop"); |
---|
555 | loop: |
---|
556 | while (1) { |
---|
557 | |
---|
558 | $rin = ''; |
---|
559 | vec($rin, fileno(SSH), 1) = 1; |
---|
560 | ($nfound, $tmout) = select($rin, undef, undef, $tmout); |
---|
561 | |
---|
562 | # Timeout |
---|
563 | if ($nfound <= 0) { |
---|
564 | debug(20, "Ssh select timed out"); |
---|
565 | kill(2, $pid); sleep(1); kill(9, $pid); |
---|
566 | close(SSH); |
---|
567 | $err = "Timeout expired"; |
---|
568 | return undef; |
---|
569 | } |
---|
570 | |
---|
571 | $ret = sysread(SSH, $buf, 256, $pos); |
---|
572 | # EOF or error |
---|
573 | if ($ret <= 0) { |
---|
574 | # Yes, close the pipe and return |
---|
575 | close(SSH); |
---|
576 | debug(20, "Ssh select closed status = $?"); |
---|
577 | $err = "No reply from ssh"; |
---|
578 | return undef; |
---|
579 | } |
---|
580 | $pos += $ret; |
---|
581 | while ($buf =~ /^(.*)\n\r?([\000-\377]*)$/) { |
---|
582 | $_ = $1; |
---|
583 | $buf = $2; |
---|
584 | $pos = length($buf); |
---|
585 | debug(20, "Ssh select loop, line = \"$_\""); |
---|
586 | if (/^connection.*refused/i) { |
---|
587 | $err = "connection refused"; |
---|
588 | } elsif (/^permission/i) { |
---|
589 | $err = "permission denied"; |
---|
590 | } elsif (/$public_key.*no\s+file/i) { |
---|
591 | $err = "$public_key file not found"; |
---|
592 | } elsif (/$public_key.*permission\s+denied/i) { |
---|
593 | $err = "$public_key file permission denied"; |
---|
594 | } elsif (/^\d+\s+\d+\s+\d/) { |
---|
595 | kill(2, $pid); sleep(1); kill(9, $pid); |
---|
596 | close(SSH); |
---|
597 | return $_; |
---|
598 | } |
---|
599 | if (defined($err)) { |
---|
600 | kill(2, $pid); sleep(1); kill(9, $pid); |
---|
601 | close(SSH); |
---|
602 | return undef; |
---|
603 | } |
---|
604 | } |
---|
605 | if ($buf =~ /password: $/i) { |
---|
606 | if (defined($passwordtimeout)) { |
---|
607 | $tmout = $passwordtimeout; |
---|
608 | print(STDERR "$bell\n\rPassword: "); |
---|
609 | if ($tmout == 0) { |
---|
610 | $tmout = undef; |
---|
611 | } |
---|
612 | } else { |
---|
613 | $tmout = 0; |
---|
614 | } |
---|
615 | $buf = ''; |
---|
616 | $pos = 0; |
---|
617 | } |
---|
618 | } |
---|
619 | } |
---|
620 | |
---|
621 | ###################################################################### |
---|
622 | # find_hosts_from_known_hosts -- find host key from private known_hosts file |
---|
623 | # $ssh_key = find_host_from_known_hosts($host); |
---|
624 | |
---|
625 | sub find_host_from_known_hosts { |
---|
626 | my($host) = @_; |
---|
627 | open(KNOWNHOSTS, "<$private_ssh_known_hosts") || return undef; |
---|
628 | while(<KNOWNHOSTS>) { |
---|
629 | @_ = split(/\s+/, $_); |
---|
630 | if ($_[0] =~ /^$host$|^$host,|,$host$/) { |
---|
631 | shift(@_); |
---|
632 | close(KNOWNHOSTS); |
---|
633 | return join(' ', @_); |
---|
634 | } |
---|
635 | } |
---|
636 | close(KNOWNHOSTS); |
---|
637 | return undef; |
---|
638 | } |
---|
639 | |
---|
640 | ###################################################################### |
---|
641 | # expand -- insert expanded hostnames to hostnames table |
---|
642 | # expand($hostname, \@hostnames, \@subdomains); |
---|
643 | |
---|
644 | sub expand { |
---|
645 | my($host, $hostnames, $subdomains) = @_; |
---|
646 | my($newhost, $sub, $entry); |
---|
647 | |
---|
648 | if (!$domainnamesplit) { |
---|
649 | my(@domain_pieces); |
---|
650 | |
---|
651 | # split domain to pieces |
---|
652 | @domain_pieces = split(/\./, $host); |
---|
653 | |
---|
654 | # add rest parts, except the one before full domain name |
---|
655 | $entry = shift(@domain_pieces); |
---|
656 | |
---|
657 | debug(20, "Adding autosplit entry $entry"); |
---|
658 | push(@$hostnames, $entry); |
---|
659 | |
---|
660 | for(; $#domain_pieces > 1; ) { |
---|
661 | $entry .= "." . shift(@domain_pieces); |
---|
662 | debug(20, "Adding autosplit entry $entry"); |
---|
663 | push(@$hostnames, $entry); |
---|
664 | } |
---|
665 | # add full domain name |
---|
666 | debug(20, "Adding autosplit entry $host"); |
---|
667 | push(@$hostnames, $host); |
---|
668 | } else { |
---|
669 | if ($host =~ /^(.*)$domain$/i) { |
---|
670 | $newhost = $1; |
---|
671 | $newhost =~ s/\.$//g; |
---|
672 | foreach $sub (@$subdomains) { |
---|
673 | $entry = $newhost . $sub; |
---|
674 | $entry =~ s/^\.//g; |
---|
675 | if ($entry ne '') { |
---|
676 | debug(20, "Adding entry $entry"); |
---|
677 | push(@$hostnames, $entry); |
---|
678 | } |
---|
679 | } |
---|
680 | } |
---|
681 | } |
---|
682 | } |
---|
683 | |
---|
684 | ###################################################################### |
---|
685 | # Print debug text |
---|
686 | # debug(text_debug_level, string) |
---|
687 | |
---|
688 | sub debug { |
---|
689 | my($level, $str) = @_; |
---|
690 | if ($debug > $level) { |
---|
691 | print(STDERR "$0:debug[$level]: $str\n"); |
---|
692 | } |
---|
693 | } |
---|
694 | |
---|
695 | ###################################################################### |
---|
696 | # find_soa -- find soa entry for domain |
---|
697 | # ($soa_origin, @other_servers) = find_soa($domain, $initial_server) |
---|
698 | |
---|
699 | sub find_soa { |
---|
700 | my($domain, $initial_server) = @_; |
---|
701 | my($field, $data, $server, @other_servers); |
---|
702 | |
---|
703 | open(DNS, "$nslookup -type=soa $domain $initial_server 2>&1 |") || |
---|
704 | die "Error: Could not start nslookup to find SOA entry for $domain : $!\nError: Try giving the path to it with --nslookup option\n"; |
---|
705 | |
---|
706 | while (<DNS>) { |
---|
707 | if (/^[^=]*origin\s*=\s*(.*)/) { |
---|
708 | $server = $1; |
---|
709 | debug(10, "Found origin : $1"); |
---|
710 | } elsif (/^[^=]*nameserver\s*=\s*(.*)\s*$/) { |
---|
711 | push(@other_servers, $1); |
---|
712 | debug(10, "Found nameserver : $1"); |
---|
713 | } |
---|
714 | } |
---|
715 | close(DNS); |
---|
716 | return($server, @other_servers); |
---|
717 | } |
---|
718 | |
---|
719 | ###################################################################### |
---|
720 | # make_perl_happy -- use some symbols, so perl doesn't complain so much |
---|
721 | # make_perl_happy(); |
---|
722 | |
---|
723 | sub make_perl_happy { |
---|
724 | if (0) { |
---|
725 | print $opt_silent; |
---|
726 | } |
---|
727 | } |
---|
728 | |
---|
729 | 1; |
---|