opentofu 0.1.0 → 0.2.0
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 +4 -4
- data/.rubocop.yml +12 -9
- data/CHANGELOG.md +30 -1
- data/README.md +1 -1
- data/exe/tofu +38 -0
- data/lib/net/tofu/error.rb +12 -0
- data/lib/net/tofu/pub.rb +97 -0
- data/lib/net/tofu/request.rb +12 -54
- data/lib/net/tofu/response.rb +14 -4
- data/lib/net/tofu/socket.rb +34 -1
- data/lib/net/tofu/version.rb +4 -2
- data/lib/net/tofu.rb +6 -4
- data/lib/uri/gemini.rb +3 -0
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1af62b16fa0e24d835f5255f5fc81526831f2ebbb06730ca257940057ff5e4b9
|
4
|
+
data.tar.gz: 2adbc048e86b177b764cfc9ec874f99142e814eb16de722779207e425cb87452
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa68f3a49de9f18d8a3a700757fd974528d1a503f562084d9e28385ac8aaadb4f6398fcd3e271643dc595b9308cdf91520cd9e9cb829113647025d458b68bb28
|
7
|
+
data.tar.gz: 12fe5263618be4a044d585b5f573248550ec8a91318e80c444e77158e79c18832390f3439e772cd4cb4ed0bb891128426667688aae1915bf00a7001828848e8d
|
data/.rubocop.yml
CHANGED
@@ -1,16 +1,19 @@
|
|
1
1
|
AllCops:
|
2
2
|
TargetRubyVersion: 2.6
|
3
3
|
|
4
|
+
Layout/HeredocIndentation:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
Metrics/AbcSize:
|
8
|
+
Enabled: true
|
9
|
+
Max: 25
|
10
|
+
|
4
11
|
Metrics/MethodLength:
|
5
12
|
Enabled: true
|
6
13
|
Max: 18
|
7
|
-
Exclude:
|
8
|
-
- "lib/net/tofu/response.rb"
|
9
14
|
|
10
|
-
Style/
|
11
|
-
Enabled:
|
12
|
-
Exclude:
|
13
|
-
- "lib/net/tofu/request.rb"
|
15
|
+
Style/Next:
|
16
|
+
Enabled: false
|
14
17
|
|
15
18
|
Style/Semicolon:
|
16
19
|
Enabled: true
|
@@ -18,9 +21,7 @@ Style/Semicolon:
|
|
18
21
|
- "lib/net/tofu/response.rb"
|
19
22
|
|
20
23
|
Style/SingleLineMethods:
|
21
|
-
Enabled:
|
22
|
-
Exclude:
|
23
|
-
- "lib/net/tofu/response.rb"
|
24
|
+
Enabled: false
|
24
25
|
|
25
26
|
Style/StringLiterals:
|
26
27
|
Enabled: true
|
@@ -32,3 +33,5 @@ Style/StringLiteralsInInterpolation:
|
|
32
33
|
|
33
34
|
Layout/LineLength:
|
34
35
|
Max: 120
|
36
|
+
Exclude:
|
37
|
+
- "test/test_net_tofu_pub.rb"
|
data/CHANGELOG.md
CHANGED
@@ -5,7 +5,36 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
-
## [0.
|
8
|
+
## [0.2.0] - 2023-07-29
|
9
|
+
|
10
|
+
### Added
|
11
|
+
|
12
|
+
- Certificate checking and pinning logic.
|
13
|
+
- Tests for certificate and pinning logic.
|
14
|
+
|
15
|
+
### Fixed
|
16
|
+
|
17
|
+
- Tests involving fetching data, as they need to trust the host now.
|
18
|
+
|
19
|
+
## [0.1.1] - 2023-07-28
|
20
|
+
|
21
|
+
### Added
|
22
|
+
|
23
|
+
- Tests.
|
24
|
+
- The `simplecov` gem to view testing coverage.
|
25
|
+
- A new error, for if the server doesn't send any data.
|
26
|
+
- The Linux platform to the bundle platform.
|
27
|
+
|
28
|
+
### Fixed
|
29
|
+
|
30
|
+
- Complexity issues in the Response class from the linter.
|
31
|
+
|
32
|
+
### Removed
|
33
|
+
|
34
|
+
- The `@@current_host` class variable from Request. Clients should handle keeping track of the current host, Tofu should just fetch data.
|
35
|
+
- The complicated logic used to parse host names from the Request class, as per the above point.
|
36
|
+
|
37
|
+
## [0.1.0] - 2023-07-27
|
9
38
|
|
10
39
|
First release. Still lots of improvements to be made.
|
11
40
|
|
data/README.md
CHANGED
data/exe/tofu
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "optparse"
|
4
|
+
require "net/tofu"
|
5
|
+
|
6
|
+
OptionParser.new do |p|
|
7
|
+
p.banner = "Usage: tofu [options]"
|
8
|
+
|
9
|
+
p.on("-rHOST", "--remove=HOST", "Host to remove from the ~/.tofu/known_hosts file") do |host|
|
10
|
+
Net::Tofu::Pub.db_exist?
|
11
|
+
|
12
|
+
lines = File.read(Net::Tofu::Pub::DB).split("\n")
|
13
|
+
|
14
|
+
idx = nil
|
15
|
+
lines.each_with_index do |l, i|
|
16
|
+
h = l.split[0]
|
17
|
+
if host == h
|
18
|
+
idx = i
|
19
|
+
break
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
lines.delete_at(idx) unless idx.nil?
|
24
|
+
|
25
|
+
f = File.open(Net::Tofu::Pub::DB, "w")
|
26
|
+
f.write lines.join("\n")
|
27
|
+
f.write "\n" unless lines.nil? || lines.empty?
|
28
|
+
|
29
|
+
exit 0
|
30
|
+
ensure
|
31
|
+
f&.close
|
32
|
+
end
|
33
|
+
|
34
|
+
p.on("-V", "--version", "Print the version then quit") do
|
35
|
+
puts "tofu v#{Net::Tofu::VERSION}"
|
36
|
+
exit 0
|
37
|
+
end
|
38
|
+
end.parse!
|
data/lib/net/tofu/error.rb
CHANGED
@@ -3,6 +3,9 @@
|
|
3
3
|
module Net
|
4
4
|
module Tofu
|
5
5
|
class Response
|
6
|
+
# Raised when a server doesn't send any data.
|
7
|
+
class NoServerResponseError < StandardError; end
|
8
|
+
|
6
9
|
# Raised when a server sends an invalid header.
|
7
10
|
class InvalidHeaderError < StandardError; end
|
8
11
|
|
@@ -23,5 +26,14 @@ module Net
|
|
23
26
|
# Raised when a request contains an invalid URI.
|
24
27
|
class InvalidURIError < StandardError; end
|
25
28
|
end
|
29
|
+
|
30
|
+
# Raised if the host in a get request doesn't have a cached public key.
|
31
|
+
class UnknownHostError < StandardError; end
|
32
|
+
|
33
|
+
# Raised if the host returns an invalid certificate (usually, it means it is out of date).
|
34
|
+
class InvalidCertificateError < StandardError; end
|
35
|
+
|
36
|
+
# Raised if the host returns a different public key.
|
37
|
+
class PublicKeyMismatchError < StandardError; end
|
26
38
|
end
|
27
39
|
end
|
data/lib/net/tofu/pub.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Net
|
4
|
+
module Tofu
|
5
|
+
# Intermediate container for an X509 certificate public key.
|
6
|
+
# Manages Geminispace public certificates.
|
7
|
+
class Pub
|
8
|
+
DIR = File.expand_path("~/.tofu")
|
9
|
+
DB = File.expand_path("~/.tofu/known_hosts")
|
10
|
+
|
11
|
+
# @return [String] The host associated with the certificate public key.
|
12
|
+
attr_reader :host
|
13
|
+
|
14
|
+
# @return [String] The certificate public key associated with the host.
|
15
|
+
attr_reader :key
|
16
|
+
|
17
|
+
# @return [OpenSSL::X509::Certificate] The X509 certificate.
|
18
|
+
attr_reader :x509
|
19
|
+
|
20
|
+
# Constructor for the cert type.
|
21
|
+
# @param host [String] Host to associate with the certificate public key.
|
22
|
+
# @param data [String] Certificate public key to associate with the host.
|
23
|
+
def initialize(host, data)
|
24
|
+
@host = host
|
25
|
+
@key = data
|
26
|
+
@key = format_hosts(data) if data.start_with?("-----BEGIN CERTIFICATE-----")
|
27
|
+
@x509 = OpenSSL::X509::Certificate.new(to_x509)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Lookup a host in the known_hosts table.
|
31
|
+
# @param host [String] the hostname to lookup
|
32
|
+
# @return [Cert] An instance of this class if found, or nil if not found.
|
33
|
+
def self.lookup(host)
|
34
|
+
db_exist?
|
35
|
+
|
36
|
+
f = File.read(DB)
|
37
|
+
includes = nil
|
38
|
+
f.lines.each_with_index do |l, i|
|
39
|
+
if l.split[0].include? host
|
40
|
+
@line = i
|
41
|
+
includes = l.split[1]
|
42
|
+
break
|
43
|
+
end
|
44
|
+
end
|
45
|
+
return nil if includes.nil? || includes.empty?
|
46
|
+
|
47
|
+
new(host, includes)
|
48
|
+
end
|
49
|
+
|
50
|
+
def line
|
51
|
+
@line ||= nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# Pin a certificate public key to the known_hosts file
|
55
|
+
def pin
|
56
|
+
Net::Tofu::Pub.db_exist?
|
57
|
+
|
58
|
+
f = File.open(DB, "a")
|
59
|
+
f.puts self
|
60
|
+
ensure
|
61
|
+
f&.close
|
62
|
+
end
|
63
|
+
|
64
|
+
# Conver the certificate public key to a properly formatted X509 string.
|
65
|
+
def to_x509
|
66
|
+
lines = @key.chars.each_slice(64).map(&:join)
|
67
|
+
"-----BEGIN CERTIFICATE-----\n#{lines.join("\n")}\n-----END CERTIFICATE-----"
|
68
|
+
end
|
69
|
+
|
70
|
+
# Hosts file format.
|
71
|
+
def to_s
|
72
|
+
"#{@host} #{@key}"
|
73
|
+
end
|
74
|
+
|
75
|
+
# Compare certificate public keys
|
76
|
+
def ==(other)
|
77
|
+
@key == other.key
|
78
|
+
end
|
79
|
+
|
80
|
+
# Check if known_hosts file exists, try to create it if it doesn't.
|
81
|
+
def self.db_exist?
|
82
|
+
return true if File.exist?(DB)
|
83
|
+
|
84
|
+
FileUtils.mkdir_p(DIR) unless File.directory?(DIR)
|
85
|
+
FileUtils.touch(DB)
|
86
|
+
true
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# Strip header, tail, and newlines from base64 encoded certificate public key.
|
92
|
+
def format_hosts(key)
|
93
|
+
key.gsub("-----BEGIN CERTIFICATE-----", "").gsub("-----END CERTIFICATE-----", "").gsub("\n", "")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/lib/net/tofu/request.rb
CHANGED
@@ -4,8 +4,6 @@ module Net
|
|
4
4
|
module Tofu
|
5
5
|
# Stores a client request to a Gemini server.
|
6
6
|
class Request
|
7
|
-
SCHEME = "gemini"
|
8
|
-
|
9
7
|
MAX_URI_BYTESIZE = 1024
|
10
8
|
|
11
9
|
# @return [URI] The full URI object of the request.
|
@@ -35,18 +33,13 @@ module Net
|
|
35
33
|
# Constructor for the request type.
|
36
34
|
# @param host [String] A host string, optionally with the gemini:// scheme.
|
37
35
|
# @param port [Integer] Optional parameter to specify the server port to connect to.
|
38
|
-
def initialize(host, port: nil)
|
39
|
-
|
40
|
-
|
36
|
+
def initialize(host, port: nil, trust: false)
|
37
|
+
@uri = URI(host)
|
38
|
+
@uri.port = port unless port.nil?
|
41
39
|
|
42
|
-
@host = host
|
43
|
-
@port = port unless port.nil?
|
44
|
-
determine_host
|
45
40
|
parse_head
|
46
41
|
parse_tail
|
47
42
|
|
48
|
-
puts format
|
49
|
-
|
50
43
|
# Make sure the URI isn't too large
|
51
44
|
if format.bytesize > MAX_URI_BYTESIZE
|
52
45
|
raise InvalidURIError,
|
@@ -54,7 +47,7 @@ module Net
|
|
54
47
|
end
|
55
48
|
|
56
49
|
# Create a socket
|
57
|
-
@sock = Socket.new(@host, @port)
|
50
|
+
@sock = Socket.new(@host, @port, trust)
|
58
51
|
end
|
59
52
|
|
60
53
|
# Format the URI for sending over a socket to a Gemini server.
|
@@ -73,63 +66,28 @@ module Net
|
|
73
66
|
|
74
67
|
private
|
75
68
|
|
76
|
-
# Parses the host and the path, and sets the current_host.
|
77
|
-
def determine_host
|
78
|
-
puts "current: #{@@current_host}"
|
79
|
-
@uri = URI(@host)
|
80
|
-
|
81
|
-
unless @uri.host.nil? || @uri.host.empty?
|
82
|
-
@host = @uri.host
|
83
|
-
@@current_host = @host
|
84
|
-
return
|
85
|
-
end
|
86
|
-
|
87
|
-
return if @uri.path.nil? || @uri.path.empty?
|
88
|
-
return unless @uri.host.nil? || @uri.host.empty?
|
89
|
-
|
90
|
-
if @uri.path.start_with?("/")
|
91
|
-
raise InvalidURIError, "No host specified" if @@current_host.nil? || @@current_host.empty?
|
92
|
-
|
93
|
-
unless @@current_host.nil? || @@current_host.empty?
|
94
|
-
@uri.host = @@current_host
|
95
|
-
@host = @uri.host
|
96
|
-
@@current_host = @host
|
97
|
-
end
|
98
|
-
|
99
|
-
return
|
100
|
-
end
|
101
|
-
|
102
|
-
paths = @uri.path.split("/")
|
103
|
-
puts paths
|
104
|
-
|
105
|
-
@uri.host = paths[0]
|
106
|
-
@host = @uri.host
|
107
|
-
@@current_host = @host
|
108
|
-
@uri.path = nil if paths.length == 1
|
109
|
-
return unless paths.length > 1
|
110
|
-
|
111
|
-
@uri.path = paths[1..].join("/")
|
112
|
-
@uri.path = "/#{uri.path}"
|
113
|
-
end
|
114
|
-
|
115
69
|
# Parses the scheme, the host, and the port for the request.
|
116
70
|
def parse_head
|
117
71
|
# Check if a scheme was specified, if not, default to gemini://
|
118
72
|
# Also set the port if this happens
|
119
73
|
if @uri.scheme.nil? || @uri.scheme.empty?
|
120
|
-
@uri.scheme =
|
121
|
-
@uri.port = URI::Gemini::DEFAULT_PORT
|
74
|
+
@uri.scheme = URI::Gemini::DEFAULT_SCHEME
|
75
|
+
@uri.port = URI::Gemini::DEFAULT_PORT if @uri.port.nil?
|
122
76
|
end
|
123
77
|
|
78
|
+
# Check if a host was specified.
|
79
|
+
raise InvalidURIError, "Request does not contain a host" if @uri.host.nil? || @uri.host.empty?
|
80
|
+
|
124
81
|
# Set member parts
|
125
82
|
@scheme = @uri.scheme
|
83
|
+
@host = @uri.host
|
126
84
|
@port = @uri.port
|
127
85
|
|
128
86
|
# Check if a scheme is present that isn't gemini://
|
129
|
-
return if @uri.scheme ==
|
87
|
+
return if @uri.scheme == URI::Gemini::DEFAULT_SCHEME
|
130
88
|
|
131
89
|
raise InvalidSchemeError,
|
132
|
-
"Request uses an invalid scheme (has: #{@uri.scheme}, wants: #{
|
90
|
+
"Request uses an invalid scheme (has: #{@uri.scheme}, wants: #{URI::Gemini::DEFAULT_SCHEME}"
|
133
91
|
end
|
134
92
|
|
135
93
|
# Parses the path, the query, and the fragment for the request.
|
data/lib/net/tofu/response.rb
CHANGED
@@ -83,6 +83,8 @@ module Net
|
|
83
83
|
|
84
84
|
# Splits up the responseinto a header and a body.
|
85
85
|
def parse
|
86
|
+
raise NoServerResponseError if @data.nil? || @data.empty?
|
87
|
+
|
86
88
|
# Extract the header and parse it
|
87
89
|
a = @data.split("\n")
|
88
90
|
@header = a[0].strip
|
@@ -108,6 +110,7 @@ module Net
|
|
108
110
|
# Parse the meta
|
109
111
|
@meta = ""
|
110
112
|
@meta = a[1..].join(" ") if a.length >= 2
|
113
|
+
@meta.strip!
|
111
114
|
parse_meta
|
112
115
|
end
|
113
116
|
|
@@ -144,14 +147,21 @@ module Net
|
|
144
147
|
def parse_meta
|
145
148
|
# Make sure the meta isn't too large
|
146
149
|
if @meta.bytesize > MAX_META_BYTESIZE
|
147
|
-
raise InvalidMetaError,
|
148
|
-
|
150
|
+
raise InvalidMetaError, <<-TXT
|
151
|
+
The server sent a meta that was too large, should be #{MAX_META_BYTESIZE} bytes,
|
152
|
+
instead is #{@meta.bytesize} bytes
|
153
|
+
TXT
|
149
154
|
end
|
150
155
|
|
151
156
|
if @status_maj == TEMPORARY_FAILURE || @status_maj == PERMANENT_FAILURE || @status_maj == REQUEST_CERTIFICATE
|
152
157
|
return
|
153
158
|
end
|
154
159
|
|
160
|
+
# Handle extra checks based on the @status type
|
161
|
+
handle_type
|
162
|
+
end
|
163
|
+
|
164
|
+
def handle_type
|
155
165
|
# Make sure meta exists (i.e. has length)
|
156
166
|
# This satisfies the INPUT and SUCCESS
|
157
167
|
unless @meta.length.positive?
|
@@ -169,10 +179,10 @@ module Net
|
|
169
179
|
raise InvalidRedirectError, "The redirect link does not have a scheme" if uri.scheme.nil? || uri.scheme.empty?
|
170
180
|
|
171
181
|
# Make sure the URI scheme is 'gemini'
|
172
|
-
return if uri.scheme ==
|
182
|
+
return if uri.scheme == URI::Gemini::DEFAULT_SCHEME
|
173
183
|
|
174
184
|
raise InvalidRedirectError,
|
175
|
-
"The redirect link has an invalid scheme (has: #{uri.scheme}, wants:
|
185
|
+
"The redirect link has an invalid scheme (has: #{uri.scheme}, wants: #{URI::Gemini::DEFAULT_SCHEME})"
|
176
186
|
end
|
177
187
|
end
|
178
188
|
end
|
data/lib/net/tofu/socket.rb
CHANGED
@@ -7,9 +7,10 @@ module Net
|
|
7
7
|
# Constructor for the socket type.
|
8
8
|
# @param host [String] Hostname of the server to connect to.
|
9
9
|
# @param port [Integer] Server port to connect to (typically 1965).
|
10
|
-
def initialize(host, port)
|
10
|
+
def initialize(host, port, trust)
|
11
11
|
@host = host
|
12
12
|
@port = port
|
13
|
+
@trust = trust
|
13
14
|
@sock = OpenSSL::SSL::SSLSocket.open(@host, @port, context: generate_secure_context)
|
14
15
|
end
|
15
16
|
|
@@ -17,6 +18,7 @@ module Net
|
|
17
18
|
def connect
|
18
19
|
@sock.hostname = @host
|
19
20
|
@sock.connect
|
21
|
+
validate_certs
|
20
22
|
end
|
21
23
|
|
22
24
|
# Try and retrieve data from a request.
|
@@ -49,6 +51,37 @@ module Net
|
|
49
51
|
ctx.options |= OpenSSL::SSL::OP_IGNORE_UNEXPECTED_EOF
|
50
52
|
ctx
|
51
53
|
end
|
54
|
+
|
55
|
+
# Validate a remote certificate with a local one
|
56
|
+
def validate_certs
|
57
|
+
remote = Pub.new(@host, @sock.peer_cert.to_pem)
|
58
|
+
local = Pub.lookup(@host)
|
59
|
+
|
60
|
+
raise UnknownHostError if local.nil? && !@trust
|
61
|
+
|
62
|
+
remote.pin if local.nil? && @trust
|
63
|
+
local = Pub.lookup(@host)
|
64
|
+
|
65
|
+
unless local == remote
|
66
|
+
raise PublicKeyMismatchError, <<-TXT
|
67
|
+
The remote host has a different certificate than what is cached locally.
|
68
|
+
This could be because a new certificate was issued, or because of a MITM attack.
|
69
|
+
Please verify the new public certificate, then you may run:
|
70
|
+
|
71
|
+
tofu -r #{@host}
|
72
|
+
|
73
|
+
Once you remove the old certificate public key, you will be able to add the new one.
|
74
|
+
TXT
|
75
|
+
end
|
76
|
+
|
77
|
+
validate_timestamp(remote)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Validate certificate timestamps
|
81
|
+
def validate_timestamp(remote)
|
82
|
+
raise InvalidCertificateError, "Certificate is not valid yet" if remote.x509.not_before > Time.now.utc
|
83
|
+
raise InvalidCertificateError, "Certificate is no longer valid" if remote.x509.not_after < Time.now.utc
|
84
|
+
end
|
52
85
|
end
|
53
86
|
end
|
54
87
|
end
|
data/lib/net/tofu/version.rb
CHANGED
data/lib/net/tofu.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "tofu/error"
|
4
|
+
require_relative "tofu/pub"
|
4
5
|
require_relative "tofu/request"
|
5
6
|
require_relative "tofu/response"
|
6
7
|
require_relative "tofu/socket"
|
7
8
|
require_relative "tofu/version"
|
8
9
|
|
10
|
+
require "fileutils"
|
9
11
|
require "openssl"
|
10
12
|
require "stringio"
|
11
13
|
require "uri/gemini"
|
@@ -13,8 +15,8 @@ require "uri/gemini"
|
|
13
15
|
module Net
|
14
16
|
# Top level module for Geminispace requests.
|
15
17
|
module Tofu
|
16
|
-
def self.get(uri, trust:
|
17
|
-
req = Request.new(uri)
|
18
|
+
def self.get(uri, trust: false)
|
19
|
+
req = Request.new(uri, trust: trust)
|
18
20
|
req.gets
|
19
21
|
|
20
22
|
return req.resp.body if req.resp.success?
|
@@ -22,8 +24,8 @@ module Net
|
|
22
24
|
req.resp.meta
|
23
25
|
end
|
24
26
|
|
25
|
-
def self.get_response(uri, trust:
|
26
|
-
req = Request.new(uri)
|
27
|
+
def self.get_response(uri, trust: false)
|
28
|
+
req = Request.new(uri, trust: trust)
|
27
29
|
req.gets
|
28
30
|
|
29
31
|
req.resp
|
data/lib/uri/gemini.rb
CHANGED
@@ -35,6 +35,9 @@ module URI # :nodoc:
|
|
35
35
|
# A Default port of 1965 for URI::Gemini.
|
36
36
|
DEFAULT_PORT = 1965
|
37
37
|
|
38
|
+
# A Default scheme of gemini for URI::Gemini.
|
39
|
+
DEFAULT_SCHEME = "gemini"
|
40
|
+
|
38
41
|
# An Array of the available components for URI::Gemini.
|
39
42
|
COMPONENT = %i[scheme host port
|
40
43
|
path query fragment].freeze
|
metadata
CHANGED
@@ -1,21 +1,22 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opentofu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rory Dudley
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-07-
|
11
|
+
date: 2023-07-29 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
OpenTOFU is a client and certificate pinning library for Geminispace, derived from
|
15
15
|
the 'trust on first use' authentication scheme (https://en.wikipedia.org/wiki/Trust_on_first_use).
|
16
16
|
email:
|
17
17
|
- rory.dudley@gmail.com
|
18
|
-
executables:
|
18
|
+
executables:
|
19
|
+
- tofu
|
19
20
|
extensions: []
|
20
21
|
extra_rdoc_files: []
|
21
22
|
files:
|
@@ -24,8 +25,10 @@ files:
|
|
24
25
|
- LICENSE.txt
|
25
26
|
- README.md
|
26
27
|
- Rakefile
|
28
|
+
- exe/tofu
|
27
29
|
- lib/net/tofu.rb
|
28
30
|
- lib/net/tofu/error.rb
|
31
|
+
- lib/net/tofu/pub.rb
|
29
32
|
- lib/net/tofu/request.rb
|
30
33
|
- lib/net/tofu/response.rb
|
31
34
|
- lib/net/tofu/socket.rb
|
@@ -39,7 +42,7 @@ metadata:
|
|
39
42
|
allowed_push_host: https://rubygems.org
|
40
43
|
homepage_uri: https://github.com/pinecat/opentofu
|
41
44
|
source_code_uri: https://github.com/pinecat/opentofu
|
42
|
-
changelog_uri: https://github.com/pinecat/opentofu
|
45
|
+
changelog_uri: https://github.com/pinecat/opentofu/blob/master/CHANGELOG.md
|
43
46
|
post_install_message:
|
44
47
|
rdoc_options: []
|
45
48
|
require_paths:
|