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.
@@ -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
@@ -0,0 +1,7 @@
1
+ module Net
2
+ class Telnet
3
+ class RFC2217
4
+ VERSION = '0.0.2'
5
+ end
6
+ end
7
+ 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: []