ronin-support-web 0.1.0.rc1

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,360 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-support-web - A web support library for ronin-rb.
4
+ #
5
+ # Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
6
+ #
7
+ # ronin-support-web is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-support-web is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-support-web. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/support/web/websocket/client'
22
+ require 'ronin/support/web/websocket/server'
23
+ require 'ronin/support/web/websocket/mixin'
24
+
25
+ require 'ronin/support/network/tcp'
26
+ require 'ronin/support/network/ssl'
27
+
28
+ module Ronin
29
+ module Support
30
+ module Web
31
+ #
32
+ # WebSocket helper methods.
33
+ #
34
+ module WebSocket
35
+ # @!macro [new] ssl_kwargs
36
+ # @option ssl [1, 1.1, 1.2, String, Symbol, nil] :version
37
+ # The SSL version to use.
38
+ #
39
+ # @option ssl [Symbol, Boolean] :verify
40
+ # Specifies whether to verify the SSL certificate.
41
+ # May be one of the following:
42
+ #
43
+ # * `:none`
44
+ # * `:peer`
45
+ # * `:fail_if_no_peer_cert`
46
+ # * `:client_once`
47
+ #
48
+ # @option ssl [Crypto::Key::RSA, OpenSSL::PKey::RSA, nil] :key
49
+ # The RSA key to use for the SSL context.
50
+ #
51
+ # @option ssl [String] :key_file
52
+ # The path to the SSL `.key` file.
53
+ #
54
+ # @option ssl [Crypto::Cert, OpenSSL::X509::Certificate, nil] :cert
55
+ # The X509 certificate to use for the SSL context.
56
+ #
57
+ # @option ssl [String] :cert_file
58
+ # The path to the SSL `.crt` file.
59
+ #
60
+ # @option ssl [String] :ca_bundle
61
+ # Path to the CA certificate file or directory.
62
+
63
+ # @!macro [new] client_kwargs
64
+ # @option kwargs [String, nil] :bind_host
65
+ # The optional host to bind the server socket to.
66
+ #
67
+ # @option kwargs [Integer, nil] :bind_port
68
+ # The optioanl port to bind the server socket to. If not
69
+ # specified, it will default to the port of the URL.
70
+ #
71
+ # @!macro ssl_kwargs
72
+
73
+ #
74
+ # Tests whether the WebSocket is open.
75
+ #
76
+ # @param [String, URI::WS, URI::WSS] url
77
+ # The `ws://` or `wss://` URL to connect to.
78
+ #
79
+ # @param [Hash{Symbol => Object}] ssl
80
+ # Additional keyword arguments for
81
+ # `Ronin::Support::Network::SSL.connect`.
82
+ #
83
+ # @param [Hash{Symbol => Object}] kwargs
84
+ # Additional keyword arguments for {Client#initialize}.
85
+ #
86
+ # @!macro client_kwargs
87
+ #
88
+ # @return [Boolean, nil]
89
+ # Specifies whether the WebSocket is open.
90
+ # If the connection was not accepted, `nil` will be returned.
91
+ #
92
+ # @api public
93
+ #
94
+ def self.open?(url, ssl: {}, **kwargs)
95
+ uri = URI(url)
96
+ host = uri.host
97
+ port = uri.port
98
+
99
+ case uri.scheme
100
+ when 'ws'
101
+ Support::Network::TCP.open?(host,port,**kwargs)
102
+ when 'wss'
103
+ Support::Network::SSL.open?(host,port,**kwargs,**ssl)
104
+ else
105
+ raise(ArgumentError,"unsupported WebSocket URI scheme: #{url.inspect}")
106
+ end
107
+ end
108
+
109
+ # Connects to a websocket.
110
+ #
111
+ # @param [String, URI::WS, URI::WSS] url
112
+ # The `ws://` or `wss://` URL to connect to.
113
+ #
114
+ # @param [Hash{Symbol => Object}] ssl
115
+ # Additional keyword arguments for
116
+ # `Ronin::Support::Network::SSL.connect`.
117
+ #
118
+ # @param [Hash{Symbol => Object}] kwargs
119
+ # Additional keyword arguments for {Client#initialize}.
120
+ #
121
+ # @!macro client_kwargs
122
+ #
123
+ # @yield [websocket]
124
+ # If a block is given, then it will be passed the WebSocket
125
+ # connection. Once the block has returned, the WebSocket connection
126
+ # will be closed.
127
+ #
128
+ # @yieldparam [Client] websocket
129
+ # The WebSocket connection.
130
+ #
131
+ # @return [Client]
132
+ # The WebSocket connection.
133
+ #
134
+ # @example Connecting to a WebSocket server:
135
+ # websocket_connect('ws://websocket-echo.com')
136
+ # # => #<Ronin::Support::Web::WebSocket::Client: ...>
137
+ #
138
+ # @example Creating a temporary WebSocket connection:
139
+ # websocket_connect('ws://websocket-echo.com') do |websocket|
140
+ # # ...
141
+ # end
142
+ #
143
+ # @api public
144
+ #
145
+ def self.connect(url, ssl: {}, **kwargs)
146
+ client = Client.new(url, ssl: ssl, **kwargs)
147
+
148
+ if block_given?
149
+ yield client
150
+ client.close
151
+ else
152
+ client
153
+ end
154
+ end
155
+
156
+ #
157
+ # Connects to the WebSocket and sends the data.
158
+ #
159
+ # @param [String] data
160
+ # The data to send to the WebSocet.
161
+ #
162
+ # @param [String, URI::WS, URI::WSS] url
163
+ # The `ws://` or `wss://` URL to connect to.
164
+ #
165
+ # @param [:text, :binary, :ping, :pong, :close] type
166
+ # The data frame type.
167
+ #
168
+ # @param [Hash{Symbol => Object}] ssl
169
+ # Additional keyword arguments for
170
+ # `Ronin::Support::Network::SSL.connect`.
171
+ #
172
+ # @param [Hash{Symbol => Object}] kwargs
173
+ # Additional keyword arguments for {Client#initialize}.
174
+ #
175
+ # @!macro client_kwargs
176
+ #
177
+ # @yield [websocket]
178
+ # If a block is given, then it will be passed the WebSocket
179
+ # connection. Once the block has returned, the WebSocket connection
180
+ # will be closed.
181
+ #
182
+ # @yieldparam [Client] websocket
183
+ # The WebSocket connection.
184
+ #
185
+ # @return [Client]
186
+ # The WebSocket connection.
187
+ #
188
+ # @api public
189
+ #
190
+ def self.connect_and_send(data,url, type: :text, ssl: {}, **kwargs)
191
+ client = connect(url, ssl: ssl, **kwargs)
192
+ client.send(data, type: type)
193
+
194
+ yield client if block_given?
195
+ return client
196
+ end
197
+
198
+ #
199
+ # Connects to the WebSocket, sends the data, and closes the connection.
200
+ #
201
+ # @param [String] data
202
+ # The data to send to the WebSocet.
203
+ #
204
+ # @param [String, URI::WS, URI::WSS] url
205
+ # The `ws://` or `wss://` URL to connect to.
206
+ #
207
+ # @param [:text, :binary, :ping, :pong, :close] type
208
+ # The data frame type.
209
+ #
210
+ # @param [Hash{Symbol => Object}] ssl
211
+ # Additional keyword arguments for
212
+ # `Ronin::Support::Network::SSL.connect`.
213
+ #
214
+ # @param [Hash{Symbol => Object}] kwargs
215
+ # Additional keyword arguments for {Client#initialize}.
216
+ #
217
+ # @!macro client_kwargs
218
+ #
219
+ # @return [true]
220
+ #
221
+ # @api public
222
+ #
223
+ def self.send(data,url, type: :text, ssl: {}, **kwargs)
224
+ connect(url, ssl: ssl, **kwargs) do |client|
225
+ client.send(data, type: type)
226
+ end
227
+
228
+ return true
229
+ end
230
+
231
+ # @!macro [new] server_kwargs
232
+ # @option kwargs [String, nil] :bind_host
233
+ # The optional host to bind the server socket to.
234
+ #
235
+ # @option kwargs [Integer, nil] :bind_port
236
+ # The optioanl port to bind the server socket to. If not
237
+ # specified, it will default to the port of the URL.
238
+ #
239
+ # @option kwargs [Integer] :backlog (5)
240
+ # The maximum backlog of pending connections.
241
+ #
242
+ # @!macro ssl_kwargs
243
+
244
+ #
245
+ # Starts a WebSocket server.
246
+ #
247
+ # @param [String, URI::WS, URI::WSS] url
248
+ # The `ws://` or `wss://` URL to connect to.
249
+ #
250
+ # @param [Hash{Symbol => Object}] ssl
251
+ # Additional keyword arguments for
252
+ # `Ronin::Support::Network::SSL.server`.
253
+ #
254
+ # @param [Hash{Symbol => Object}] kwargs
255
+ # Additional keyword arguments for {Server#initialize}.
256
+ #
257
+ # @!macro server_kwargs
258
+ #
259
+ # @yield [server]
260
+ # If a block is given, then it will be passed the WebSocket server.
261
+ # Once the block has returned, the WebSocket server will be closed.
262
+ #
263
+ # @yieldparam [Server] server
264
+ # The WebSocket server.
265
+ #
266
+ # @return [Server]
267
+ # The WebSocket server.
268
+ #
269
+ # @api public
270
+ #
271
+ def self.server(url, ssl: {}, **kwargs)
272
+ server = Server.new(url, ssl: ssl, **kwargs)
273
+
274
+ if block_given?
275
+ yield server
276
+ server.close
277
+ else
278
+ server
279
+ end
280
+ end
281
+
282
+ #
283
+ # Creates a new WebSocket server listening on a given host and port,
284
+ # accepting clients in a loop.
285
+ #
286
+ # @param [String, URI::WS, URI::WSS] url
287
+ # The `ws://` or `wss://` URL to connect to.
288
+ #
289
+ # @param [Hash{Symbol => Object}] ssl
290
+ # Additional keyword arguments for
291
+ # `Ronin::Support::Network::SSL.server`.
292
+ #
293
+ # @param [Hash{Symbol => Object}] kwargs
294
+ # Additional keyword arguments for {Server#initialize}.
295
+ #
296
+ # @!macro server_kwargs
297
+ #
298
+ # @yield [client]
299
+ # The given block will be passed the newly connected WebSocket
300
+ # client. After the block has finished, the WebSocket client will be
301
+ # closed.
302
+ #
303
+ # @yieldparam [Server::Client] client
304
+ # A newly connected WebSocket client.
305
+ #
306
+ # @return [nil]
307
+ #
308
+ # @api public
309
+ #
310
+ def self.server_loop(url, ssl: {}, **kwargs)
311
+ server(url, ssl: ssl, **kwargs) do |server|
312
+ loop do
313
+ client = server.accept
314
+
315
+ yield client if block_given?
316
+ client.close
317
+ end
318
+ end
319
+ end
320
+
321
+ #
322
+ # Opens a WebSocket server, accepts a single connection, yields it to
323
+ # the given block, then closes both the connection and the server.
324
+ #
325
+ # @param [String, URI::WS, URI::WSS] url
326
+ # The `ws://` or `wss://` URL to connect to.
327
+ #
328
+ # @param [Hash{Symbol => Object}] ssl
329
+ # Additional keyword arguments for
330
+ # `Ronin::Support::Network::SSL.server`.
331
+ #
332
+ # @param [Hash{Symbol => Object}] kwargs
333
+ # Additional keyword arguments for {Server#initialize}.
334
+ #
335
+ # @!macro server_kwargs
336
+ #
337
+ # @yield [client]
338
+ # The given block will be passed the newly connected WebSocket
339
+ # client. After the block has finished, the WebSocket client will be
340
+ # closed.
341
+ #
342
+ # @yieldparam [Server::Client] client
343
+ # A newly connected WebSocket client.
344
+ #
345
+ # @return [nil]
346
+ #
347
+ # @api public
348
+ #
349
+ def self.accept(url, ssl: {}, **kwargs)
350
+ server(url, ssl: ssl, **kwargs) do |server|
351
+ client = server.accept
352
+
353
+ yield client if block_given?
354
+ client.close
355
+ end
356
+ end
357
+ end
358
+ end
359
+ end
360
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-support-web - A web support library for ronin-rb.
4
+ #
5
+ # Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
6
+ #
7
+ # ronin-support-web is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-support-web is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-support-web. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/support/web/xml'
22
+
23
+ module Ronin
24
+ module Support
25
+ module Web
26
+ module XML
27
+ #
28
+ # Provides helper methods for working with XML.
29
+ #
30
+ # @api public
31
+ #
32
+ module Mixin
33
+ #
34
+ # Parses the body of a document into a HTML document object.
35
+ #
36
+ # @param [String, IO] xml
37
+ # The XML to parse.
38
+ #
39
+ # @yield [doc]
40
+ # If a block is given, it will be passed the newly created document
41
+ # object.
42
+ #
43
+ # @yieldparam [Nokogiri::XML::Document] doc
44
+ # The new XML document object.
45
+ #
46
+ # @return [Nokogiri::XML::Document]
47
+ # The new HTML document object.
48
+ #
49
+ # @see http://rubydoc.info/gems/nokogiri/Nokogiri/XML/Document
50
+ # @see XML.parse
51
+ #
52
+ def xml_parse(xml,&block)
53
+ XML.parse(xml,&block)
54
+ end
55
+
56
+ #
57
+ # Opens an XML file.
58
+ #
59
+ # @param [String] path
60
+ # The path to the XML file.
61
+ #
62
+ # @yield [doc]
63
+ # If a block is given, it will be passed the newly created document
64
+ # object.
65
+ #
66
+ # @yieldparam [Nokogiri::XML::Document] doc
67
+ # The new XML document object.
68
+ #
69
+ # @return [Nokogiri::XML::Document]
70
+ # The parsed XML file.
71
+ #
72
+ # @example
73
+ # doc = XML.open('index.xml')
74
+ # # => #<Nokogiri::XML::Document:...>
75
+ #
76
+ # @see http://rubydoc.info/gems/nokogiri/Nokogiri/XML/Document
77
+ # @see XML.open
78
+ #
79
+ def xml_open(path,&block)
80
+ XML.open(path,&block)
81
+ end
82
+
83
+ alias open_xml xml_open
84
+
85
+ #
86
+ # Creates a new `Nokogiri::XML::Builder`.
87
+ #
88
+ # @yield []
89
+ # The block that will be used to construct the XML document.
90
+ #
91
+ # @return [Nokogiri::XML::Builder]
92
+ # The new XML builder object.
93
+ #
94
+ # @example
95
+ # xml_build do
96
+ # root {
97
+ # foo(id: 'bar')
98
+ # }
99
+ # end
100
+ #
101
+ # @see http://rubydoc.info/gems/nokogiri/Nokogiri/XML/Builder
102
+ # @see XML.build
103
+ #
104
+ def xml_build(&block)
105
+ XML.build(&block)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-support-web - A web support library for ronin-rb.
4
+ #
5
+ # Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
6
+ #
7
+ # ronin-support-web is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-support-web is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-support-web. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'nokogiri'
22
+
23
+ module Ronin
24
+ module Support
25
+ module Web
26
+ #
27
+ # XML helper methods.
28
+ #
29
+ module XML
30
+ #
31
+ # Parses the body of a document into a HTML document object.
32
+ #
33
+ # @param [String, IO] xml
34
+ # The XML to parse.
35
+ #
36
+ # @yield [doc]
37
+ # If a block is given, it will be passed the newly created document
38
+ # object.
39
+ #
40
+ # @yieldparam [Nokogiri::XML::Document] doc
41
+ # The new XML document object.
42
+ #
43
+ # @return [Nokogiri::XML::Document]
44
+ # The new HTML document object.
45
+ #
46
+ # @see http://rubydoc.info/gems/nokogiri/Nokogiri/XML/Document
47
+ #
48
+ # @api public
49
+ #
50
+ def self.parse(xml)
51
+ doc = Nokogiri::XML.parse(xml)
52
+ yield doc if block_given?
53
+ return doc
54
+ end
55
+
56
+ #
57
+ # Opens an XML file.
58
+ #
59
+ # @param [String] path
60
+ # The path to the XML file.
61
+ #
62
+ # @yield [doc]
63
+ # If a block is given, it will be passed the newly created document
64
+ # object.
65
+ #
66
+ # @yieldparam [Nokogiri::XML::Document] doc
67
+ # The new XML document object.
68
+ #
69
+ # @return [Nokogiri::XML::Document]
70
+ # The parsed XML file.
71
+ #
72
+ # @example
73
+ # doc = XML.open('data.xml')
74
+ # # => #<Nokogiri::XML::Document:...>
75
+ #
76
+ # @api public
77
+ #
78
+ def self.open(path)
79
+ doc = Nokogiri::XML(File.open(path))
80
+ yield doc if block_given?
81
+ return doc
82
+ end
83
+
84
+ #
85
+ # Creates a new `Nokogiri::XML::Builder`.
86
+ #
87
+ # @yield []
88
+ # The block that will be used to construct the XML document.
89
+ #
90
+ # @return [Nokogiri::XML::Builder]
91
+ # The new XML builder object.
92
+ #
93
+ # @example
94
+ # XML.build do
95
+ # root {
96
+ # foo(id: 'bar')
97
+ # }
98
+ # end
99
+ #
100
+ # @see http://rubydoc.info/gems/nokogiri/Nokogiri/XML/Builder
101
+ #
102
+ # @api public
103
+ #
104
+ def self.build(&block)
105
+ Nokogiri::XML::Builder.new(&block)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-support-web - A web support library for ronin-rb.
4
+ #
5
+ # Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
6
+ #
7
+ # ronin-support-web is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-support-web is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-support-web. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/support/web/xml'
22
+ require 'ronin/support/web/html'
23
+ require 'ronin/support/web/mixin'
24
+ require 'ronin/support/web/version'
25
+
26
+ module Ronin
27
+ module Support
28
+ #
29
+ # Top-level namespace for `ronin-support-web`.
30
+ #
31
+ # ## Example
32
+ #
33
+ # require 'ronin/support/web'
34
+ # include Ronin::Support::Web
35
+ #
36
+ # html_parse "<html>...</html>"
37
+ # # => #<Nokogiri::HTML::Document: ...>
38
+ #
39
+ module Web
40
+ include Mixin
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gemspec = YAML.load_file('gemspec.yml')
7
+
8
+ gem.name = gemspec.fetch('name')
9
+ gem.version = gemspec.fetch('version') do
10
+ lib_dir = File.join(File.dirname(__FILE__),'lib')
11
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
12
+
13
+ require 'ronin/support/web/version'
14
+ Ronin::Support::Web::VERSION
15
+ end
16
+
17
+ gem.summary = gemspec['summary']
18
+ gem.description = gemspec['description']
19
+ gem.licenses = Array(gemspec['license'])
20
+ gem.authors = Array(gemspec['authors'])
21
+ gem.email = gemspec['email']
22
+ gem.homepage = gemspec['homepage']
23
+ gem.metadata = gemspec['metadata'] if gemspec['metadata']
24
+
25
+ glob = ->(patterns) { gem.files & Dir[*patterns] }
26
+
27
+ gem.files = `git ls-files`.split($/)
28
+ gem.files = glob[gemspec['files']] if gemspec['files']
29
+ gem.files += Array(gemspec['generated_files'])
30
+ # exclude test files from the packages gem
31
+ gem.files -= glob[gemspec['test_files'] || 'spec/{**/}*']
32
+
33
+ gem.executables = gemspec.fetch('executables') do
34
+ glob['bin/*'].map { |path| File.basename(path) }
35
+ end
36
+
37
+ gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
38
+ gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
39
+
40
+ gem.require_paths = Array(gemspec.fetch('require_paths') {
41
+ %w[ext lib].select { |dir| File.directory?(dir) }
42
+ })
43
+
44
+ gem.requirements = gemspec['requirements']
45
+ gem.required_ruby_version = gemspec['required_ruby_version']
46
+ gem.required_rubygems_version = gemspec['required_rubygems_version']
47
+ gem.post_install_message = gemspec['post_install_message']
48
+
49
+ split = ->(string) { string.split(/,\s*/) }
50
+
51
+ if gemspec['dependencies']
52
+ gemspec['dependencies'].each do |name,versions|
53
+ gem.add_dependency(name,split[versions])
54
+ end
55
+ end
56
+
57
+ if gemspec['development_dependencies']
58
+ gemspec['development_dependencies'].each do |name,versions|
59
+ gem.add_development_dependency(name,split[versions])
60
+ end
61
+ end
62
+ end