net-telnet-rfc2217 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/net-telnet-rfc2217.rb +1 -0
- data/lib/net/telnet/rfc2217.rb +219 -0
- data/lib/net/telnet/rfc2217/telnet.rb +80 -0
- data/lib/net/telnet/rfc2217/version.rb +7 -0
- metadata +88 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f0b5b2b5e014b1fc9906ae0be1e7d33fb5d3971cce0b4eda74332e6a1ee24a9e
|
4
|
+
data.tar.gz: b56a17ffee69e60c4c8f805fd5fb16a0195a95719ed3b93a4fc5417c1dbb0e2f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1b4b82e214841d59828c733e9c15a65d2e623ee807f42df97353054564cb6bd7991f6900182ecd35cedebf83876c9388ecdce8574639a86b0e4d237e3e8f0a96
|
7
|
+
data.tar.gz: 35da8ce422ca87cfc75ad2c4461582d9ec54f8335f74bb381fe178d12890bae4f5034e7338488187c07cdfb6a900e2eb89f95909c7cdd536a33581062b0c5861
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'net/telnet/rfc2217'
|
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'net/telnet'
|
2
|
+
require 'byebug'
|
3
|
+
|
4
|
+
# have to patch Telnet to allow us to read control sequences
|
5
|
+
require 'net/telnet/rfc2217/telnet'
|
6
|
+
Net::Telnet.prepend(Net::Telnet::RFC2217::TelnetExtensions)
|
7
|
+
|
8
|
+
module Net
|
9
|
+
class Telnet
|
10
|
+
class RFC2217
|
11
|
+
class WaitReadable < RuntimeError
|
12
|
+
include ::IO::WaitReadable
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :telnet
|
16
|
+
|
17
|
+
COM_PORT_OPTION = 44.chr
|
18
|
+
|
19
|
+
SET_BAUDRATE = 1.chr
|
20
|
+
SET_DATASIZE = 2.chr
|
21
|
+
SET_PARITY = 3.chr
|
22
|
+
SET_STOPSIZE = 4.chr
|
23
|
+
SET_CONTROL = 5.chr
|
24
|
+
NOTIFY_LINESTATE = 6.chr
|
25
|
+
NOTIFY_MODEMSTATE = 7.chr
|
26
|
+
FLOWCONTROL_SUSPEND = 8.chr
|
27
|
+
SET_LINESTATE_MASK = 10.chr
|
28
|
+
SET_MODEMSTATE_MASK = 11.chr
|
29
|
+
PURGE_DATA = 12.chr
|
30
|
+
|
31
|
+
SET_BAUDRATE_RESPONSE = 101.chr
|
32
|
+
SET_DATASIZE_RESPONSE = 102.chr
|
33
|
+
SET_PARITY_RESPONSE = 103.chr
|
34
|
+
SET_STOPSIZE_RESPONSE = 104.chr
|
35
|
+
SET_CONTROL_RESPONSE = 105.chr
|
36
|
+
NOTIFY_LINESTATE_RESPONSE = 106.chr
|
37
|
+
NOTIFY_MODEMSTATE_RESPONSE = 107.chr
|
38
|
+
FLOWCONTROL_SUSPEND_RESPONSE = 108.chr
|
39
|
+
SET_LINESTATE_MASK_RESPONSE = 110.chr
|
40
|
+
SET_MODEMSTATE_MASK_RESPONSE = 111.chr
|
41
|
+
PURGE_DATA_RESPONSE = 112.chr
|
42
|
+
|
43
|
+
NONE = 1
|
44
|
+
ODD = 2
|
45
|
+
EVEN = 3
|
46
|
+
MARK = 4
|
47
|
+
SPACE = 5
|
48
|
+
|
49
|
+
SERIAL_PORT_PARAMS = %w{baud data_bits stop_bits parity}.freeze
|
50
|
+
|
51
|
+
class << self
|
52
|
+
def open(options)
|
53
|
+
sp = new(options)
|
54
|
+
if block_given?
|
55
|
+
begin
|
56
|
+
yield sp
|
57
|
+
ensure
|
58
|
+
sp.close
|
59
|
+
end
|
60
|
+
nil
|
61
|
+
else
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def initialize(options = {}, &block)
|
68
|
+
set_modem_params(options.slice(*SERIAL_PORT_PARAMS))
|
69
|
+
|
70
|
+
options['Binmode'] = true
|
71
|
+
options['Telnetmode'] = false
|
72
|
+
@telnet = Telnet.new(options, &block)
|
73
|
+
telnet.write(IAC + WILL + COM_PORT_OPTION)
|
74
|
+
sock.flush
|
75
|
+
@buffer = ''
|
76
|
+
readpartial(0)
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_modem_params
|
80
|
+
@modem_params
|
81
|
+
end
|
82
|
+
|
83
|
+
def set_modem_params(modem_params)
|
84
|
+
@modem_params = modem_params.dup
|
85
|
+
@modem_params['baud'] = 115200 unless @modem_params.key?('baud')
|
86
|
+
@modem_params['data_bits'] = 8 unless @modem_params.key?('data_bits')
|
87
|
+
@modem_params['stop_bits'] = 1 unless @modem_params.key?('stop_bits')
|
88
|
+
unless @modem_params.key?('parity')
|
89
|
+
@modem_params['parity'] = (data_bits == 8 ? NONE : EVEN)
|
90
|
+
end
|
91
|
+
write_modem_params if telnet
|
92
|
+
end
|
93
|
+
|
94
|
+
def baud
|
95
|
+
get_modem_params['baud']
|
96
|
+
end
|
97
|
+
|
98
|
+
def data_bits
|
99
|
+
get_modem_params['data_bits']
|
100
|
+
end
|
101
|
+
|
102
|
+
def stop_bits
|
103
|
+
get_modem_params['stop_bits']
|
104
|
+
end
|
105
|
+
|
106
|
+
def parity
|
107
|
+
get_modem_params['parity']
|
108
|
+
end
|
109
|
+
|
110
|
+
def sock
|
111
|
+
@telnet.sock
|
112
|
+
end
|
113
|
+
|
114
|
+
def read(length, outbuf = '')
|
115
|
+
readpartial(length, outbuf)
|
116
|
+
while outbuf.length < length
|
117
|
+
outbuf.concat(length - outbuf.length)
|
118
|
+
end
|
119
|
+
outbuf
|
120
|
+
end
|
121
|
+
|
122
|
+
def readpartial(length, outbuf = '')
|
123
|
+
loop do
|
124
|
+
# 0 is special and means 'read at least one control sequence'
|
125
|
+
break if length != 0 || @buffer.length != 0
|
126
|
+
|
127
|
+
data = sock.sysread([length - @buffer.length, 64 * 1024].max)
|
128
|
+
|
129
|
+
# avoid getting caught in the middle of a control sequence
|
130
|
+
while (data[-1] == IAC && sock.wait_readable(0))
|
131
|
+
data.concat(sock.sysread(16))
|
132
|
+
end
|
133
|
+
|
134
|
+
saw_control = false
|
135
|
+
data = @telnet.preprocess(data) do |control|
|
136
|
+
saw_control = true
|
137
|
+
if DO[0] == control[0] && COM_PORT_OPTION == control[1]
|
138
|
+
# start negotiation
|
139
|
+
write_modem_params
|
140
|
+
true
|
141
|
+
elsif DONT[0] == control[0] && COM_PORT_OPTION == control[1]
|
142
|
+
raise "Serial port control not supported"
|
143
|
+
elsif (WILL[0] == control[0] || DONT[0] == control[0]) && OPT_ECHO == control[1]
|
144
|
+
# just ignore echo requests
|
145
|
+
true
|
146
|
+
else
|
147
|
+
false
|
148
|
+
end
|
149
|
+
end
|
150
|
+
@buffer.concat(data)
|
151
|
+
break if length == 0 && saw_control
|
152
|
+
end
|
153
|
+
|
154
|
+
length = [length, @buffer.length].min
|
155
|
+
outbuf.replace(@buffer[0...length])
|
156
|
+
@buffer = @buffer[length..-1]
|
157
|
+
outbuf
|
158
|
+
end
|
159
|
+
|
160
|
+
def read_nonblock(length, outbuf = '', options = {})
|
161
|
+
if outbuf == ({ exception: false })
|
162
|
+
options = outbuf
|
163
|
+
outbuf = ''
|
164
|
+
end
|
165
|
+
loop do
|
166
|
+
result = wait_readable(0)
|
167
|
+
if result == nil
|
168
|
+
raise WaitReadable unless options[:exception] == false
|
169
|
+
return :wait_readable
|
170
|
+
end
|
171
|
+
# we have to try to consume control characters first
|
172
|
+
readpartial(0)
|
173
|
+
# and then only do a real read if we have something
|
174
|
+
return readpartial(length, outbuf) unless @buffer.empty?
|
175
|
+
# otherwise loop and see if there's more there
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def wait_readable(timeout = nil)
|
180
|
+
return true unless @buffer.empty?
|
181
|
+
result = sock.wait_readable(timeout)
|
182
|
+
result = self if result == sock
|
183
|
+
result
|
184
|
+
end
|
185
|
+
|
186
|
+
def ungetbyte(b)
|
187
|
+
@buffer.insert(0, b.chr)
|
188
|
+
end
|
189
|
+
|
190
|
+
def ungetc(c)
|
191
|
+
@buffer.insert(0, c)
|
192
|
+
end
|
193
|
+
|
194
|
+
def write(string)
|
195
|
+
string = string.gsub(/#{IAC}/no, IAC + IAC)
|
196
|
+
telnet.write(string)
|
197
|
+
end
|
198
|
+
|
199
|
+
def flush
|
200
|
+
sock.flush
|
201
|
+
end
|
202
|
+
|
203
|
+
def close
|
204
|
+
telnet.close
|
205
|
+
end
|
206
|
+
|
207
|
+
private
|
208
|
+
|
209
|
+
def write_modem_params
|
210
|
+
telnet.write(
|
211
|
+
IAC + SB + COM_PORT_OPTION + SET_BAUDRATE + [baud].pack("N") + IAC + SE +
|
212
|
+
IAC + SB + COM_PORT_OPTION + SET_DATASIZE + data_bits.chr + IAC + SE +
|
213
|
+
IAC + SB + COM_PORT_OPTION + SET_STOPSIZE + stop_bits.chr + IAC + SE +
|
214
|
+
IAC + SB + COM_PORT_OPTION + SET_PARITY + parity.chr + IAC + SE)
|
215
|
+
sock.flush
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Net
|
2
|
+
class Telnet
|
3
|
+
class RFC2217
|
4
|
+
module TelnetExtensions
|
5
|
+
def preprocess(string)
|
6
|
+
# combine CR+NULL into CR
|
7
|
+
string = string.gsub(/#{CR}#{NULL}/no, CR) if @options["Telnetmode"]
|
8
|
+
|
9
|
+
# combine EOL into "\n"
|
10
|
+
string = string.gsub(/#{EOL}/no, "\n") unless @options["Binmode"]
|
11
|
+
|
12
|
+
# remove NULL
|
13
|
+
string = string.gsub(/#{NULL}/no, '') unless @options["Binmode"]
|
14
|
+
|
15
|
+
string.gsub(/#{IAC}(
|
16
|
+
[#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]|
|
17
|
+
[#{DO}#{DONT}#{WILL}#{WONT}]
|
18
|
+
[#{OPT_BINARY}-#{OPT_NEW_ENVIRON}#{44.chr}#{OPT_EXOPL}]|
|
19
|
+
#{SB}[^#{IAC}]*#{IAC}#{SE}
|
20
|
+
)/xno) do
|
21
|
+
if block_given? && IAC != $1
|
22
|
+
# already handled; don't handle it ourselves
|
23
|
+
next '' if yield $1
|
24
|
+
end
|
25
|
+
|
26
|
+
if IAC == $1 # handle escaped IAC characters
|
27
|
+
IAC
|
28
|
+
elsif AYT == $1 # respond to "IAC AYT" (are you there)
|
29
|
+
self.write("nobody here but us pigeons" + EOL)
|
30
|
+
''
|
31
|
+
elsif DO[0] == $1[0] # respond to "IAC DO x"
|
32
|
+
if OPT_BINARY[0] == $1[1]
|
33
|
+
unless @telnet_option["BINARY"] == true
|
34
|
+
@telnet_option["BINARY"] = true
|
35
|
+
self.write(IAC + WILL + OPT_BINARY)
|
36
|
+
end
|
37
|
+
else
|
38
|
+
self.write(IAC + WONT + $1[1..1])
|
39
|
+
end
|
40
|
+
''
|
41
|
+
elsif DONT[0] == $1[0] # respond to "IAC DON'T x" with "IAC WON'T x"
|
42
|
+
self.write(IAC + WONT + $1[1..1])
|
43
|
+
''
|
44
|
+
elsif WILL[0] == $1[0] # respond to "IAC WILL x"
|
45
|
+
if OPT_BINARY[0] == $1[1]
|
46
|
+
self.write(IAC + DO + OPT_BINARY)
|
47
|
+
elsif OPT_ECHO[0] == $1[1]
|
48
|
+
self.write(IAC + DO + OPT_ECHO)
|
49
|
+
elsif OPT_SGA[0] == $1[1]
|
50
|
+
unless @telnet_option["SGA"]
|
51
|
+
@telnet_option["SGA"] = true
|
52
|
+
self.write(IAC + DO + OPT_SGA)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
self.write(IAC + DONT + $1[1..1])
|
56
|
+
end
|
57
|
+
''
|
58
|
+
elsif WONT[0] == $1[0] # respond to "IAC WON'T x"
|
59
|
+
if OPT_ECHO[0] == $1[1]
|
60
|
+
self.write(IAC + DONT + OPT_ECHO)
|
61
|
+
elsif OPT_SGA[0] == $1[1]
|
62
|
+
unless @telnet_option["SGA"] == false
|
63
|
+
@telnet_option["SGA"] = false
|
64
|
+
self.write(IAC + DONT + OPT_SGA)
|
65
|
+
end
|
66
|
+
else
|
67
|
+
self.write(IAC + DONT + $1[1..1])
|
68
|
+
end
|
69
|
+
''
|
70
|
+
elsif SB[0] == $1[0] # yield sub option
|
71
|
+
''
|
72
|
+
else
|
73
|
+
''
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end # preprocess
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: net-telnet-rfc2217
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cody Cutrer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-05-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: net-telnet
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: byebug
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '9.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '9.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '13.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '13.0'
|
55
|
+
description:
|
56
|
+
email: cody@cutrer.com'
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files: []
|
60
|
+
files:
|
61
|
+
- lib/net-telnet-rfc2217.rb
|
62
|
+
- lib/net/telnet/rfc2217.rb
|
63
|
+
- lib/net/telnet/rfc2217/telnet.rb
|
64
|
+
- lib/net/telnet/rfc2217/version.rb
|
65
|
+
homepage: https://github.com/ccutrer/net-telnet-2217
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata: {}
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubygems_version: 3.0.3
|
85
|
+
signing_key:
|
86
|
+
specification_version: 4
|
87
|
+
summary: Library for getting an IO-like object for a remote serial port
|
88
|
+
test_files: []
|