2
= $RCSfile$ -- SSL/TLS enhancement for Net::Telnet.
5
'OpenSSL for Ruby 2' project
6
Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
10
This program is licenced under the same licence as Ruby.
11
(See the file 'LICENCE'.)
14
$Id: telnets.rb 11708 2007-02-12 23:01:19Z shyouhei $
16
2001/11/06: Contiributed to Ruby/OpenSSL project.
20
This class will initiate SSL/TLS session automaticaly if the server
21
sent OPT_STARTTLS. Some options are added for SSL/TLS.
23
host = Net::Telnet::new({
24
"Host" => "localhost",
26
## follows are new options.
27
'CertFile' => "user.crt",
28
'KeyFile' => "user.key",
29
'CAFile' => "/some/where/certs/casert.pem",
30
'CAPath' => "/some/where/caserts",
31
'VerifyMode' => SSL::VERIFY_PEER,
32
'VerifyCallback' => verify_proc
35
Or, the new options ('Cert', 'Key' and 'CACert') are available from
36
Michal Rokos's OpenSSL module.
38
cert_data = File.open("user.crt"){|io| io.read }
39
pkey_data = File.open("user.key"){|io| io.read }
40
cacert_data = File.open("your_ca.pem"){|io| io.read }
41
host = Net::Telnet::new({
42
"Host" => "localhost",
44
'Cert' => OpenSSL::X509::Certificate.new(cert_data)
45
'Key' => OpenSSL::PKey::RSA.new(pkey_data)
46
'CACert' => OpenSSL::X509::Certificate.new(cacert_data)
47
'CAFile' => "/some/where/certs/casert.pem",
48
'CAPath' => "/some/where/caserts",
49
'VerifyMode' => SSL::VERIFY_PEER,
50
'VerifyCallback' => verify_proc
53
This class is expected to be a superset of usual Net::Telnet.
63
OPT_STARTTLS = 46.chr # "\056" # "\x2e" # Start TLS
64
TLS_FOLLOWS = 1.chr # "\001" # "\x01" # FOLLOWS (for STARTTLS)
66
alias preprocess_orig preprocess
70
def preprocess(string)
71
# combine CR+NULL into CR
72
string = string.gsub(/#{CR}#{NULL}/no, CR) if @options["Telnetmode"]
74
# combine EOL into "\n"
75
string = string.gsub(/#{EOL}/no, "\n") unless @options["Binmode"]
78
[#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]|
79
[#{DO}#{DONT}#{WILL}#{WONT}][#{OPT_BINARY}-#{OPT_EXOPL}]|
80
#{SB}[#{OPT_BINARY}-#{OPT_EXOPL}]
81
(#{IAC}#{IAC}|[^#{IAC}])+#{IAC}#{SE}
83
if IAC == $1 # handle escaped IAC characters
85
elsif AYT == $1 # respond to "IAC AYT" (are you there)
86
self.write("nobody here but us pigeons" + EOL)
88
elsif DO[0] == $1[0] # respond to "IAC DO x"
89
if OPT_BINARY[0] == $1[1]
90
@telnet_option["BINARY"] = true
91
self.write(IAC + WILL + OPT_BINARY)
92
elsif OPT_STARTTLS[0] == $1[1]
93
self.write(IAC + WILL + OPT_STARTTLS)
94
self.write(IAC + SB + OPT_STARTTLS + TLS_FOLLOWS + IAC + SE)
96
self.write(IAC + WONT + $1[1..1])
99
elsif DONT[0] == $1[0] # respond to "IAC DON'T x" with "IAC WON'T x"
100
self.write(IAC + WONT + $1[1..1])
102
elsif WILL[0] == $1[0] # respond to "IAC WILL x"
103
if OPT_BINARY[0] == $1[1]
104
self.write(IAC + DO + OPT_BINARY)
105
elsif OPT_ECHO[0] == $1[1]
106
self.write(IAC + DO + OPT_ECHO)
107
elsif OPT_SGA[0] == $1[1]
108
@telnet_option["SGA"] = true
109
self.write(IAC + DO + OPT_SGA)
111
self.write(IAC + DONT + $1[1..1])
114
elsif WONT[0] == $1[0] # respond to "IAC WON'T x"
115
if OPT_ECHO[0] == $1[1]
116
self.write(IAC + DONT + OPT_ECHO)
117
elsif OPT_SGA[0] == $1[1]
118
@telnet_option["SGA"] = false
119
self.write(IAC + DONT + OPT_SGA)
121
self.write(IAC + DONT + $1[1..1])
124
elsif SB[0] == $1[0] # respond to "IAC SB xxx IAC SE"
125
if OPT_STARTTLS[0] == $1[1] && TLS_FOLLOWS[0] == $2[0]
126
@sock = OpenSSL::SSL::SSLSocket.new(@sock)
127
@sock.cert = @options['Cert'] unless @sock.cert
128
@sock.key = @options['Key'] unless @sock.key
129
@sock.ca_cert = @options['CACert']
130
@sock.ca_file = @options['CAFile']
131
@sock.ca_path = @options['CAPath']
132
@sock.timeout = @options['Timeout']
133
@sock.verify_mode = @options['VerifyMode']
134
@sock.verify_callback = @options['VerifyCallback']
135
@sock.verify_depth = @options['VerifyDepth']
146
alias waitfor_org waitfor
149
time_out = @options["Timeout"]
150
waittime = @options["Waittime"]
152
if options.kind_of?(Hash)
153
prompt = if options.has_key?("Match")
155
elsif options.has_key?("Prompt")
157
elsif options.has_key?("String")
158
Regexp.new( Regexp.quote(options["String"]) )
160
time_out = options["Timeout"] if options.has_key?("Timeout")
161
waittime = options["Waittime"] if options.has_key?("Waittime")
172
@rest = '' unless @rest
174
until(prompt === line and not IO::select([@sock], nil, nil, waittime))
175
unless IO::select([@sock], nil, nil, time_out)
176
raise TimeoutError, "timed-out; wait for the next data"
179
c = @rest + @sock.sysread(1024 * 1024)
180
@dumplog.log_dump('<', c) if @options.has_key?("Dump_log")
181
if @options["Telnetmode"]
188
when DO[0], DONT[0], WILL[0], WONT[0]
189
throw :next unless c[pos+2]
192
ret = detect_sub_negotiation(c, pos)
193
throw :next unless ret
208
buf = preprocess(c[0...pos])
211
@log.print(buf) if @options.has_key?("Output_log")
213
yield buf if block_given?
214
rescue EOFError # End of file reached
217
yield nil if block_given?
227
def detect_sub_negotiation(data, pos)
228
return nil if data.length < pos+6 # IAC SB x param IAC SE
233
if data[pos+1] == SE[0]