1
+ require 'strscan'
2
+ require 'mutex_m'
1
3
require 'net/ssh'
2
4
require 'net/scp'
3
5
@@ -18,13 +20,101 @@ module SSHKit
18
20
module Backend
19
21
20
22
class Netssh < Abstract
23
+ class KnownHostsKeys
24
+ include Mutex_m
25
+
26
+ def initialize ( path )
27
+ super ( )
28
+ @path = File . expand_path ( path )
29
+ @hosts_keys = nil
30
+ end
31
+
32
+ def keys_for ( hostlist )
33
+ keys = hosts_keys || parse_file
34
+ hostlist . split ( ',' ) . each do |host |
35
+ if key_list = keys [ host ]
36
+ return key_list
37
+ end
38
+ end
39
+ [ ]
40
+ end
41
+
42
+ private
43
+
44
+ attr_reader :path
45
+ attr_accessor :hosts_keys
46
+
47
+ def parse_file
48
+ synchronize do
49
+ return keys if hosts_keys
50
+
51
+ return self . hosts_keys = { } unless File . readable? ( path )
52
+
53
+ new_keys = { }
54
+ File . open ( path ) do |file |
55
+ scanner = StringScanner . new ( "" )
56
+ file . each_line do |line |
57
+ scanner . string = line
58
+ hostlist , key = parse_line ( scanner )
59
+ next unless key
60
+
61
+ hostlist . each do |host |
62
+ ( new_keys [ host ] ||= [ ] ) << key
63
+ end
64
+ end
65
+ end
66
+ return self . hosts_keys = new_keys
67
+ end
68
+ end
69
+
70
+ def parse_line ( scanner )
71
+ scanner . skip ( /\s */ )
72
+ return if scanner . match? ( /$|#/ )
73
+
74
+ hostlist = scanner . scan ( /\S +/ ) . split ( ',' )
75
+ scanner . skip ( /\s */ )
76
+ type = scanner . scan ( /\S +/ )
77
+
78
+ return unless Net ::SSH ::KnownHosts ::SUPPORTED_TYPE . include? ( type )
79
+
80
+ scanner . skip ( /\s */ )
81
+ blob = scanner . rest . unpack ( "m*" ) . first
82
+ return hostlist , Net ::SSH ::Buffer . new ( blob ) . read_key
83
+ end
84
+ end
85
+
86
+ class KnownHosts
87
+ include Mutex_m
88
+
89
+ def initialize
90
+ super ( )
91
+ @files = { }
92
+ end
93
+
94
+ def search_for ( host , options = { } )
95
+ ::Net ::SSH ::KnownHosts . hostfiles ( options ) . map do |path |
96
+ known_hosts_file ( path ) . keys_for ( host )
97
+ end . flatten
98
+ end
99
+
100
+ def add ( *args )
101
+ ::Net ::SSH ::KnownHosts . add ( *args )
102
+ synchronize { @files = { } }
103
+ end
104
+
105
+ private
106
+
107
+ def known_hosts_file ( path )
108
+ @files [ path ] || synchronize { @files [ path ] ||= KnownHostsKeys . new ( path ) }
109
+ end
110
+ end
21
111
22
112
class Configuration
23
113
attr_accessor :connection_timeout , :pty
24
114
attr_writer :ssh_options
25
115
26
116
def ssh_options
27
- @ssh_options || { }
117
+ @ssh_options ||= { known_hosts : KnownHosts . new }
28
118
end
29
119
end
30
120
0 commit comments