ronin-support-web 0.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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