libtls 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.yardopts +3 -0
- data/Gemfile +4 -0
- data/LICENSE +13 -0
- data/README.md +279 -0
- data/Rakefile +9 -0
- data/lib/libtls.rb +9 -0
- data/lib/libtls/client.rb +195 -0
- data/lib/libtls/config.rb +112 -0
- data/lib/libtls/exn.rb +22 -0
- data/lib/libtls/raw.rb +759 -0
- data/lib/libtls/server.rb +128 -0
- data/lib/libtls/version.rb +8 -0
- data/libtls.gemspec +30 -0
- data/spec/fixtures/mike-burns.pem +116 -0
- data/spec/fixtures/theca.pem +13 -0
- data/spec/fixtures/thecert.crt +13 -0
- data/spec/fixtures/thecsr.csr +11 -0
- data/spec/fixtures/thekey.key +15 -0
- data/spec/fixtures/thekey.key.protected +18 -0
- data/spec/oo/client_spec.rb +28 -0
- data/spec/oo/server_spec.rb +75 -0
- data/spec/support/fixtures.rb +10 -0
- metadata +135 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1566c4cd86920d7e089b2087c3b98f62b5d43217
|
4
|
+
data.tar.gz: 3618c5cc4bcd47205a61664c8b52bbeba6bc91df
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fc0d68830f69516be58bf616d81ab44b6bad1e30c9e6882b96dca089fe8a3bf8d6784e45a501098770fecde8922637a3ba6cac7aee083e4a4f1b573df37da50f
|
7
|
+
data.tar.gz: 74d0efa0af199bb1f938e8ee6a70a69d55915108d82c7a3d638c01750ee9d1fa7edb4c8ed66f9071497575db15d43d69e7fbf31ca842e8d5896661eca9731701
|
data/.gitignore
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (c) 2015 Mike Burns <mike@mike-burns.com>
|
2
|
+
|
3
|
+
Permission to use, copy, modify, and distribute this software for any
|
4
|
+
purpose with or without fee is hereby granted, provided that the above
|
5
|
+
copyright notice and this permission notice appear in all copies.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
8
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
9
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
10
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
11
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
12
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
13
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,279 @@
|
|
1
|
+
# libtls for Ruby
|
2
|
+
|
3
|
+
This is a set of libtls bindings for Ruby, plus a nice object-oriented layer
|
4
|
+
atop the bindings.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
This gem depends on the libtls library. Make sure you either run OpenBSD or
|
9
|
+
have [libressl-portable] installed.
|
10
|
+
|
11
|
+
Once libtls itself is installed, add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'libtls'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install libtls
|
24
|
+
|
25
|
+
[libressl-portable]: http://www.libressl.org/releases.html
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
This library provides the API on two levels: the raw C functions, and a nice
|
30
|
+
object-oriented layer atop it.
|
31
|
+
|
32
|
+
### Raw
|
33
|
+
|
34
|
+
The raw functions are as follows; see `tls_init`(3) for more information on what
|
35
|
+
they do:
|
36
|
+
|
37
|
+
- `LibTLS::Raw.tls_init`
|
38
|
+
- `LibTLS::Raw.tls_error`
|
39
|
+
- `LibTLS::Raw.tls_config_new`
|
40
|
+
- `LibTLS::Raw.tls_config_free`
|
41
|
+
- `LibTLS::Raw.tls_config_parse_protocols`
|
42
|
+
- `LibTLS::Raw.tls_config_set_ca_file`
|
43
|
+
- `LibTLS::Raw.tls_config_set_ca_path`
|
44
|
+
- `LibTLS::Raw.tls_config_set_ca_mem`
|
45
|
+
- `LibTLS::Raw.tls_config_set_cert_file`
|
46
|
+
- `LibTLS::Raw.tls_config_set_cert_mem`
|
47
|
+
- `LibTLS::Raw.tls_config_set_ciphers`
|
48
|
+
- `LibTLS::Raw.tls_config_set_dheparams`
|
49
|
+
- `LibTLS::Raw.tls_config_set_ecdhecurve`
|
50
|
+
- `LibTLS::Raw.tls_config_set_key_file`
|
51
|
+
- `LibTLS::Raw.tls_config_set_key_mem`
|
52
|
+
- `LibTLS::Raw.tls_config_set_protocols`
|
53
|
+
- `LibTLS::Raw.tls_config_set_verify_depth`
|
54
|
+
- `LibTLS::Raw.tls_config_clear_keys`
|
55
|
+
- `LibTLS::Raw.tls_config_insecure_noverifycert`
|
56
|
+
- `LibTLS::Raw.tls_config_insecure_noverifyname`
|
57
|
+
- `LibTLS::Raw.tls_config_verify`
|
58
|
+
- `LibTLS::Raw.tls_load_file`
|
59
|
+
- `LibTLS::Raw.tls_client`
|
60
|
+
- `LibTLS::Raw.tls_server`
|
61
|
+
- `LibTLS::Raw.tls_configure`
|
62
|
+
- `LibTLS::Raw.tls_reset`
|
63
|
+
- `LibTLS::Raw.tls_close`
|
64
|
+
- `LibTLS::Raw.tls_free`
|
65
|
+
- `LibTLS::Raw.tls_connect`
|
66
|
+
- `LibTLS::Raw.tls_connect_fds`
|
67
|
+
- `LibTLS::Raw.tls_connect_servername`
|
68
|
+
- `LibTLS::Raw.tls_connect_socket`
|
69
|
+
- `LibTLS::Raw.tls_accept_fds`
|
70
|
+
- `LibTLS::Raw.tls_accept_socket`
|
71
|
+
- `LibTLS::Raw.tls_read`
|
72
|
+
- `LibTLS::Raw.tls_write`
|
73
|
+
|
74
|
+
Of particular note are those functions which take a pointer (`tls_read`,
|
75
|
+
`tls_write`, `tls_accept_socket`, and others). These must have an instance of
|
76
|
+
`FFI::MemoryPointer` passed to them:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
FFI::MemoryPointer.new(:size_t) do |outlen|
|
80
|
+
FFI::MemoryPointer.new(:uchar, 1024, true) do |buf|
|
81
|
+
|
82
|
+
ret = LibTLS::Raw.tls_read(client, buf, 1024, outlen)
|
83
|
+
|
84
|
+
if ret < 0
|
85
|
+
raise "tls_read: #{LibTLS::Raw.tls_error(client)}"
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
Additionally, instance of Ruby's `Socket` object must be converted to their
|
93
|
+
file descriptor before interfacing with the C function. The `tls_accept_socket`
|
94
|
+
function combines the `FFI::MemoryPointer` requirement with this file
|
95
|
+
descriptor requirement:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
cctx_ptr = FFI::MemoryPointer.new(:pointer)
|
99
|
+
|
100
|
+
if tls_accept_socket(server, cctx_ptr, socket.fileno) == -1
|
101
|
+
raise "tls_accept_socket: #{LibTLS::Raw.tls_error(server)}"
|
102
|
+
end
|
103
|
+
|
104
|
+
cctx = cctx_ptr.read_pointer
|
105
|
+
```
|
106
|
+
|
107
|
+
Constants from `tls.h` are manually re-exposed under the `LibTLS::Raw`
|
108
|
+
namespace:
|
109
|
+
|
110
|
+
- `LibTLS::Raw::TLS_API`
|
111
|
+
- `LibTLS::Raw::TLS_PROTOCOL_TLSv1_0`
|
112
|
+
- `LibTLS::Raw::TLS_PROTOCOL_TLSv1_1`
|
113
|
+
- `LibTLS::Raw::TLS_PROTOCOL_TLSv1_2`
|
114
|
+
- `LibTLS::Raw::TLS_PROTOCOL_TLSv1`
|
115
|
+
- `LibTLS::Raw::TLS_PROTOCOLS_ALL`
|
116
|
+
- `LibTLS::Raw::TLS_PROTOCOLS_DEFAULT`
|
117
|
+
- `LibTLS::Raw::TLS_READ_AGAIN`
|
118
|
+
- `LibTLS::Raw::TLS_WRITE_AGAIN`
|
119
|
+
|
120
|
+
### Object-Oriented Wrapper
|
121
|
+
|
122
|
+
An object-oriented wrapper is provided. Here is an example of a client:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
# Get the contents of the Web page hosted at https://#{hostname}:443#{path} .
|
126
|
+
def get(hostname, path)
|
127
|
+
# The return value: nil, or a string.
|
128
|
+
content = nil
|
129
|
+
|
130
|
+
# TLS configuration. The key is formed from the series of tls_config_set_*
|
131
|
+
# functions; the value is either the scalar value (int or string), or an
|
132
|
+
# array of the multiple values. For example, ca_mem takes an array with the
|
133
|
+
# FFI::MemoryPointer and the length of that pointer.
|
134
|
+
config = {
|
135
|
+
ciphers: "DES-CBC3-SHA",
|
136
|
+
protocols: LibTLS::Raw::TLS_PROTOCOLS_ALL
|
137
|
+
}
|
138
|
+
|
139
|
+
# Create a new libtls client. The block is then immediately run, and then the
|
140
|
+
# memory free'd.
|
141
|
+
LibTLS::Client.new(configure: config) do |client|
|
142
|
+
# Connect to the server on port 443. When the block finishes, disconnect.
|
143
|
+
content = client.connect("mike-burns.com", 443) do |c|
|
144
|
+
# Send a string to the server; in this case, a HTTP request.
|
145
|
+
c.write(http_get(hostname, path))
|
146
|
+
# Read all the data from the server, and return it. The return value of
|
147
|
+
# this block is the return value of Client#connect.
|
148
|
+
c.read
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Return the content.
|
153
|
+
content
|
154
|
+
end
|
155
|
+
|
156
|
+
# Generate a HTTP request string.
|
157
|
+
def http_get(hostname, path)
|
158
|
+
["GET #{path} HTTP/1.1",
|
159
|
+
"User-Agent: libtls.rb/0.1",
|
160
|
+
"Host: #{hostname}"].join("\r\n") +
|
161
|
+
"\r\n"
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
And here is an example of a simple echo server:
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
# Reply to the socket's clients with their own string.
|
169
|
+
def echo_server(socket)
|
170
|
+
# Encrypt communications using the key and cert as generated by e.g.
|
171
|
+
# LibreSSL.
|
172
|
+
config = {
|
173
|
+
key_file: "thekey.key",
|
174
|
+
cert_file: "thecert.crt"
|
175
|
+
}
|
176
|
+
|
177
|
+
# Create and configure a new server object. The block is then immediately
|
178
|
+
# run, and then the memory is free'd.
|
179
|
+
LibTLS::Server.new(configure: config) do |server|
|
180
|
+
# Block until a client connects on client_socket.
|
181
|
+
client_socket, _ = socket.accept
|
182
|
+
|
183
|
+
# Loop forever; this allows another client to connect after this one.
|
184
|
+
loop do
|
185
|
+
# Handle the TLS handshake on the client socket. This takes a block,
|
186
|
+
# which is run immediately after the handshake has completed
|
187
|
+
# successfully. After the block finishes, disconnect and clean up. The
|
188
|
+
# block takes an opened client object.
|
189
|
+
server.accept(client_socket) do |c|
|
190
|
+
# Loop so that the client can write until they disconnect.
|
191
|
+
loop do
|
192
|
+
# Read the entirety of the client's string.
|
193
|
+
str = c.read
|
194
|
+
# Write exactly what the client sent.
|
195
|
+
c.write(str)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
```
|
202
|
+
|
203
|
+
The underlying `struct tls *` object is exposed through the `#ctx` method; it
|
204
|
+
can be passed to any `LibTLS::Raw` method, for example.
|
205
|
+
|
206
|
+
These methods can raise instances of `LibTLS::UnknownCError` and
|
207
|
+
`LibTLS::CError`. Instances of the first are raised when we do not have access
|
208
|
+
to the underlying issue, and instances of the second attempt to include the
|
209
|
+
error string from libtls.
|
210
|
+
|
211
|
+
## Contributing
|
212
|
+
|
213
|
+
As contributors and maintainers of this project, we will respect all people
|
214
|
+
who contribute in any fashion. We are committed to making participation in this
|
215
|
+
project a harassment-free experience for everyone, regardless of who they are.
|
216
|
+
|
217
|
+
The project maintainers have the right and responsibility to remove, edit, or
|
218
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
219
|
+
that are not aligned to this code of conduct. Project maintainers who do not
|
220
|
+
follow the code of conduct may be removed from the project team.
|
221
|
+
|
222
|
+
Instances of unacceptable behavior may be reported by [opening an
|
223
|
+
issue][issues] or contacting [Mike Burns](mailto:mike@mike-burns.com)
|
224
|
+
([PGP key][Mike PGP key]).
|
225
|
+
|
226
|
+
[issues]: https://github.com/mike-burns/libtls.rb/issues
|
227
|
+
[Mike PGP key]: http://pgp.mit.edu/pks/lookup?op=get&search=0x3E6761F72846B014
|
228
|
+
|
229
|
+
### To contribute a feature
|
230
|
+
|
231
|
+
1. Fork it ( https://github.com/mike-burns/libtls.rb/fork )
|
232
|
+
2. Make sure the tests pass (`rake`)
|
233
|
+
3. Create your feature branch (`git checkout -b my-new-feature`)
|
234
|
+
4. Commit your changes (`git commit -am 'Add some feature'`)
|
235
|
+
5. Push to the branch (`git push origin my-new-feature`)
|
236
|
+
6. Make sure the tests pass (`rake`)
|
237
|
+
7. Make sure documentation is complete (`yard`)
|
238
|
+
8. Create a new Pull Request
|
239
|
+
|
240
|
+
*Feature requests without patches will be closed*.
|
241
|
+
|
242
|
+
### To report a security issue
|
243
|
+
|
244
|
+
If the issue should be kept quiet for security reasons, email
|
245
|
+
[Mike Burns](mailto:mike@mike-burns.com) directly. His PGP key id is
|
246
|
+
[0x2846b014][Mike PGP key], fingerprint:
|
247
|
+
|
248
|
+
5FD8 2CE6 A646 3285 538F
|
249
|
+
C3A5 3E67 61F7 2846 B014
|
250
|
+
|
251
|
+
## Credits
|
252
|
+
|
253
|
+
libtls for Ruby is by [Mike Burns]. It is released under the
|
254
|
+
[ISC license][LICENSE].
|
255
|
+
|
256
|
+
It would have been impossible to make this library so quickly without the
|
257
|
+
knowledge gained on [erltls] with [Rebecca Meritz].
|
258
|
+
|
259
|
+
GNU help was provided by [Matt Horan].
|
260
|
+
|
261
|
+
The [ffi] gem has also proven crucial to this project; thanks to
|
262
|
+
[Wayne Meissner, et al.][ffi credits], for their amazing work on that.
|
263
|
+
|
264
|
+
The code of conduct is adapted from the [Contributor Covenant],
|
265
|
+
[version 1.1.0][coc110].
|
266
|
+
|
267
|
+
[Donate to the OpenBSD Foundation][donate]. Without them, none of this would
|
268
|
+
exist.
|
269
|
+
|
270
|
+
[Mike Burns]: https://mike-burns.com
|
271
|
+
[Rebecca Meritz]: http://rebecca.meritz.com/
|
272
|
+
[Matt Horan]: https://matthoran.com/
|
273
|
+
[LICENSE]: LICENSE
|
274
|
+
[donate]: http://www.openbsdfoundation.org/donations.html
|
275
|
+
[ffi]: https://github.com/ffi/ffi/wiki
|
276
|
+
[ffi credits]: https://github.com/ffi/ffi/#credits
|
277
|
+
[erltls]: https://github.com/meritz-burns/erltls
|
278
|
+
[Contributor Covenant]: http://contributor-covenant.org
|
279
|
+
[coc110]: http://contributor-covenant.org/version/1/1/0/
|
data/Rakefile
ADDED
data/lib/libtls.rb
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'libtls/config'
|
2
|
+
require 'libtls/raw'
|
3
|
+
|
4
|
+
module LibTLS
|
5
|
+
##
|
6
|
+
# This class represents a TLS client connecting to a server. Here is a sample
|
7
|
+
# HTTPS session; this #get method will produce the content at the specified
|
8
|
+
# path on the hostname:
|
9
|
+
#
|
10
|
+
# def get(hostname, path)
|
11
|
+
# content = nil
|
12
|
+
# config = { ca_file: '/etc/ssl/cert.pem' }
|
13
|
+
#
|
14
|
+
# LibTLS::Client.new(configure: config) do |client|
|
15
|
+
# content = client.connect("mike-burns.com", 443) do |c|
|
16
|
+
# c.write(http_get(hostname, path))
|
17
|
+
# c.read
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# content
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# def http_get(hostname, path)
|
25
|
+
# ["GET #{path} HTTP/1.1",
|
26
|
+
# "User-Agent: libtls.rb/0.1",
|
27
|
+
# "Host: #{hostname}"].join("\r\n") +
|
28
|
+
# "\r\n"
|
29
|
+
# end
|
30
|
+
class Client
|
31
|
+
##
|
32
|
+
# The FFI wrapper around the struct tls object
|
33
|
+
#
|
34
|
+
# This is only useful for calling any of the {LibTLS::Raw} methods.
|
35
|
+
attr_reader :ctx
|
36
|
+
|
37
|
+
##
|
38
|
+
# Construct a new [Client] instance
|
39
|
+
#
|
40
|
+
# Once constructed, it runs the block. When the block finishes, it calls
|
41
|
+
# {#finish}.
|
42
|
+
#
|
43
|
+
# @param configure [Hash] a mapping from setting name to value. The setting
|
44
|
+
# name is any of {LibTLS::Config::VALID_SET_CONFIGS}; the value is either a
|
45
|
+
# scalar value passed through to the C function, or an array of values. For
|
46
|
+
# example:
|
47
|
+
# { ca_file: 'ca.pem', key_mem: [key_ptr, 48] }
|
48
|
+
# @yieldparam [Client] self an initialized and configured instance of self
|
49
|
+
# @raise [LibTLS::UnknownCError] if +tls_init+ or +tls_client+ fails
|
50
|
+
# @raise [LibTLS::CError] if +tls_configure+ fails
|
51
|
+
def initialize(configure:, &block)
|
52
|
+
if LibTLS::Raw.tls_init < 0
|
53
|
+
raise LibTLS::UnknownCError, "tls_init"
|
54
|
+
end
|
55
|
+
|
56
|
+
@config = Config.new(configure)
|
57
|
+
|
58
|
+
if (@ctx = LibTLS::Raw.tls_client).null?
|
59
|
+
raise LibTLS::UnknownCError, "tls_client"
|
60
|
+
end
|
61
|
+
|
62
|
+
if LibTLS::Raw::tls_configure(ctx, @config.as_raw) < 0
|
63
|
+
raise LibTLS::CError, "tls_configure: #{LibTLS::Raw.tls_error(ctx)}"
|
64
|
+
end
|
65
|
+
|
66
|
+
if block
|
67
|
+
begin
|
68
|
+
block.call(self)
|
69
|
+
ensure
|
70
|
+
self.finish
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# Open a connection with the server
|
77
|
+
#
|
78
|
+
# This method negotiates the TLS connection with the +hostname+, at the
|
79
|
+
# +port+. Once connected, it passes the connected client to the block. Once
|
80
|
+
# the block finishes, it calls {OpenedClient#close} on the connection.
|
81
|
+
#
|
82
|
+
# @param hostname [String] the server to connect to, as an IPv4 address, an
|
83
|
+
# IPv6 address, or anything that can be resolved by +getaddrinfo+.
|
84
|
+
# @param port [#to_s] the port on the server to connect to
|
85
|
+
# @yieldparam [OpenedClient] client a connected client
|
86
|
+
# @raise [LibTLS::CError] if the +tls_connect+ fails
|
87
|
+
# @return the result of the block
|
88
|
+
def connect(hostname, port, &block)
|
89
|
+
opened_client = nil
|
90
|
+
|
91
|
+
begin
|
92
|
+
if LibTLS::Raw.tls_connect(ctx, hostname, port.to_s) < 0
|
93
|
+
raise LibTLS::CError, "tls_connect: #{LibTLS::Raw.tls_error(ctx)}"
|
94
|
+
end
|
95
|
+
|
96
|
+
opened_client = OpenedClient.new(ctx)
|
97
|
+
block.call(opened_client)
|
98
|
+
ensure
|
99
|
+
opened_client && opened_client.close
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# Release any memory held on to by the C library
|
105
|
+
#
|
106
|
+
# This method must be called either implicitly by passing a block to
|
107
|
+
# {#initialize}, or explicitly by you.
|
108
|
+
def finish
|
109
|
+
@config.free
|
110
|
+
LibTLS::Raw.tls_free(ctx)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
##
|
117
|
+
# A TLS client connected to a server
|
118
|
+
#
|
119
|
+
# This class must be instantiated only by {LibTLS::Client#connect} and
|
120
|
+
# {LibTLS::Server#accept}.
|
121
|
+
#
|
122
|
+
# When finished, {#close} must be called. This is implicitly handled for you by
|
123
|
+
# passing a block to the methods mentioned above.
|
124
|
+
class OpenedClient
|
125
|
+
READ_LEN = 1024
|
126
|
+
private_constant :READ_LEN
|
127
|
+
|
128
|
+
##
|
129
|
+
# The FFI wrapper around the struct tls object
|
130
|
+
#
|
131
|
+
# This is only useful for calling any of the {LibTLS::Raw} methods.
|
132
|
+
attr_reader :ctx
|
133
|
+
|
134
|
+
##
|
135
|
+
# @api private
|
136
|
+
def initialize(ctx)
|
137
|
+
@ctx = ctx
|
138
|
+
end
|
139
|
+
|
140
|
+
##
|
141
|
+
# Close this connection
|
142
|
+
#
|
143
|
+
# This method must be called either implicitly by passing a block to
|
144
|
+
# {LibTLS::Client#connect} or {LibTLS::Server#accept}, or explicitly by you.
|
145
|
+
def close
|
146
|
+
if LibTLS::Raw.tls_close(ctx) < 0
|
147
|
+
raise LibTLS::CError, "tls_close: #{LibTLS::Raw.tls_error(ctx)}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
##
|
152
|
+
# Write the string to the connection
|
153
|
+
#
|
154
|
+
# @param [String] str the string to write
|
155
|
+
# @raise [LibTLS::CError] if +tls_write+ fails
|
156
|
+
def write(str)
|
157
|
+
FFI::MemoryPointer.new(:size_t) do |outlen|
|
158
|
+
FFI::MemoryPointer.new(:uchar, str.length + 1) do |str_ptr|
|
159
|
+
str_ptr.put_string(0, str)
|
160
|
+
|
161
|
+
if LibTLS::Raw.tls_write(ctx, str_ptr, str.length, outlen) < 0
|
162
|
+
raise LibTLS::CError, "tls_write: #{LibTLS::Raw.tls_error(ctx)}"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
##
|
169
|
+
# Read a string from the connection
|
170
|
+
#
|
171
|
+
# @raise [LibTLS::CError] if +tls_read+ fails
|
172
|
+
# @return [String] the accumulated buffer
|
173
|
+
def read
|
174
|
+
str = ""
|
175
|
+
|
176
|
+
FFI::MemoryPointer.new(:size_t) do |outlen|
|
177
|
+
FFI::MemoryPointer.new(:uchar, READ_LEN, true) do |buf|
|
178
|
+
loop do
|
179
|
+
if LibTLS::Raw.tls_read(ctx, buf, READ_LEN, outlen) < 0
|
180
|
+
raise LibTLS::CError, "tls_read: #{LibTLS::Raw.tls_error(ctx)}"
|
181
|
+
end
|
182
|
+
|
183
|
+
str += buf.get_string(0, outlen.get_int(0))
|
184
|
+
|
185
|
+
if READ_LEN > outlen.get_int(0)
|
186
|
+
break
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
str
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|