i2p 0.1.4
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.
- data/AUTHORS +1 -0
- data/CONTRIBUTORS +0 -0
- data/README +155 -0
- data/UNLICENSE +24 -0
- data/VERSION +1 -0
- data/lib/i2p.rb +177 -0
- data/lib/i2p/bob.rb +27 -0
- data/lib/i2p/bob/client.rb +489 -0
- data/lib/i2p/bob/tunnel.rb +303 -0
- data/lib/i2p/data/certificate.rb +75 -0
- data/lib/i2p/data/destination.rb +68 -0
- data/lib/i2p/data/key.rb +40 -0
- data/lib/i2p/data/key_pair.rb +65 -0
- data/lib/i2p/data/private_key.rb +14 -0
- data/lib/i2p/data/public_key.rb +14 -0
- data/lib/i2p/data/signing_private_key.rb +10 -0
- data/lib/i2p/data/signing_public_key.rb +10 -0
- data/lib/i2p/data/structure.rb +84 -0
- data/lib/i2p/hosts.rb +207 -0
- data/lib/i2p/sam.rb +40 -0
- data/lib/i2p/sam/client.rb +226 -0
- data/lib/i2p/sdk.jar +0 -0
- data/lib/i2p/sdk.rb +111 -0
- data/lib/i2p/streaming.jar +0 -0
- data/lib/i2p/streaming.rb +10 -0
- data/lib/i2p/version.rb +22 -0
- metadata +116 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
module I2P
|
2
|
+
##
|
3
|
+
# **I2P key pair data structure.**
|
4
|
+
#
|
5
|
+
# @see http://docs.i2p2.de/core/net/i2p/data/PrivateKeyFile.html
|
6
|
+
# @since 0.1.3
|
7
|
+
class KeyPair < Structure
|
8
|
+
BYTESIZE = 663 # minimum
|
9
|
+
|
10
|
+
##
|
11
|
+
# Reads a key pair from the given `input` stream.
|
12
|
+
#
|
13
|
+
# @param [IO, StringIO] input
|
14
|
+
# @return [KeyPair]
|
15
|
+
def self.read(input)
|
16
|
+
self.new(Destination.read(input), PrivateKey.read(input), SigningPrivateKey.read(input))
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# Initializes a new key pair instance.
|
21
|
+
#
|
22
|
+
# @param [Destination] destination
|
23
|
+
# @param [PrivateKey] private_key
|
24
|
+
# @param [SigningPrivateKey] signing_key
|
25
|
+
def initialize(destination, private_key, signing_key)
|
26
|
+
@destination = destination
|
27
|
+
@private_key = private_key
|
28
|
+
@signing_key = signing_key
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# @return [Destination]
|
33
|
+
attr_accessor :destination
|
34
|
+
|
35
|
+
##
|
36
|
+
# @return [PrivateKey]
|
37
|
+
attr_accessor :private_key
|
38
|
+
|
39
|
+
##
|
40
|
+
# @return [SigningPrivateKey]
|
41
|
+
attr_accessor :signing_key
|
42
|
+
|
43
|
+
##
|
44
|
+
# Returns the byte size of this key pair.
|
45
|
+
#
|
46
|
+
# @return [Integer]
|
47
|
+
def size
|
48
|
+
destination.size + private_key.size + signing_key.size
|
49
|
+
end
|
50
|
+
alias_method :bytesize, :size
|
51
|
+
|
52
|
+
##
|
53
|
+
# Returns the binary string representation of this key pair.
|
54
|
+
#
|
55
|
+
# @return [String]
|
56
|
+
def to_s
|
57
|
+
StringIO.open do |buffer|
|
58
|
+
buffer.write(destination.to_s)
|
59
|
+
buffer.write(private_key.to_s)
|
60
|
+
buffer.write(signing_key.to_s)
|
61
|
+
buffer.string
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module I2P
|
2
|
+
##
|
3
|
+
# **I2P private key data structure.**
|
4
|
+
#
|
5
|
+
# An I2P private key is a 2048-bit (256-byte) integer. The private key
|
6
|
+
# represents only the exponent, not the primes, which are constant and
|
7
|
+
# defined in the crypto spec.
|
8
|
+
#
|
9
|
+
# @see http://docs.i2p2.de/core/net/i2p/data/PrivateKey.html
|
10
|
+
# @since 0.1.3
|
11
|
+
class PrivateKey < Key
|
12
|
+
BYTESIZE = 256
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module I2P
|
2
|
+
##
|
3
|
+
# **I2P public key data structure.**
|
4
|
+
#
|
5
|
+
# An I2P public key is a 2048-bit (256-byte) integer. The public key
|
6
|
+
# represents only the exponent, not the primes, which are constant and
|
7
|
+
# defined in the crypto spec.
|
8
|
+
#
|
9
|
+
# @see http://docs.i2p2.de/core/net/i2p/data/PublicKey.html
|
10
|
+
# @since 0.1.3
|
11
|
+
class PublicKey < Key
|
12
|
+
BYTESIZE = 256
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module I2P
|
2
|
+
##
|
3
|
+
# **I2P data structure.**
|
4
|
+
#
|
5
|
+
# Defines a base class for I2P data structures.
|
6
|
+
#
|
7
|
+
# @see http://docs.i2p2.de/core/net/i2p/data/DataStructure.html
|
8
|
+
# @see http://docs.i2p2.de/core/net/i2p/data/DataStructureImpl.html
|
9
|
+
# @since 0.1.3
|
10
|
+
class Structure
|
11
|
+
##
|
12
|
+
# Parses a data structure from the given `base64` string.
|
13
|
+
#
|
14
|
+
# @param [String, #to_s] base64
|
15
|
+
# @return [Structure]
|
16
|
+
def self.parse(base64)
|
17
|
+
base64 = base64.dup
|
18
|
+
base64.gsub!('~', '/')
|
19
|
+
base64.gsub!('-', '+')
|
20
|
+
self.read(StringIO.new(base64.unpack('m').first))
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Reads a data structure from the given `input` stream.
|
25
|
+
#
|
26
|
+
# @param [IO, StringIO] input
|
27
|
+
# @return [Structure]
|
28
|
+
def self.read(input)
|
29
|
+
raise NotImplementedError.new("#{self}.read")
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Returns the byte size of this data structure.
|
34
|
+
#
|
35
|
+
# @return [Integer]
|
36
|
+
def size
|
37
|
+
if self.class.const_defined?(:BYTESIZE)
|
38
|
+
self.class.const_get(:BYTESIZE)
|
39
|
+
else
|
40
|
+
raise NotImplementedError.new("#{self.class}#size")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
alias_method :bytesize, :size
|
44
|
+
|
45
|
+
##
|
46
|
+
# Returns `true` if `other == self` and `other` has the same class as
|
47
|
+
# this data structure.
|
48
|
+
#
|
49
|
+
# @param [Object] other
|
50
|
+
# @return [Boolean]
|
51
|
+
def eql?(other)
|
52
|
+
other.is_a?(self.class) && self == other
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Returns `true` if `other` has the same binary string representation as
|
57
|
+
# this data structure.
|
58
|
+
#
|
59
|
+
# @param [Object] other
|
60
|
+
# @return [Boolean]
|
61
|
+
def ==(other)
|
62
|
+
to_s == other.to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Returns the Base64-encoded representation of this data structure.
|
67
|
+
#
|
68
|
+
# @return [String]
|
69
|
+
def to_base64
|
70
|
+
base64 = [to_s].pack('m').delete("\n")
|
71
|
+
base64.gsub!('/', '~')
|
72
|
+
base64.gsub!('+', '-')
|
73
|
+
base64
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# Returns the binary string representation of this data structure.
|
78
|
+
#
|
79
|
+
# @return [String]
|
80
|
+
def to_s
|
81
|
+
raise NotImplementedError.new("#{self.class}#to_s")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/i2p/hosts.rb
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
module I2P
|
2
|
+
##
|
3
|
+
# I2P address book parser.
|
4
|
+
#
|
5
|
+
# @example Opening the default hosts.txt file
|
6
|
+
# I2P::Hosts.open do |hosts|
|
7
|
+
# ...
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# @example Opening the given hosts.txt file
|
11
|
+
# I2P::Hosts.open("/path/to/hosts.txt") do |hosts|
|
12
|
+
# ...
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# @since 0.1.2
|
16
|
+
class Hosts
|
17
|
+
include Enumerable
|
18
|
+
|
19
|
+
DEFAULT_FILE = '~/.i2p/hosts.txt' # Unix only
|
20
|
+
|
21
|
+
##
|
22
|
+
# Looks up the I2P destination for `hostname`.
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# I2P::Hosts["forum.i2p"]
|
26
|
+
#
|
27
|
+
# @param [String, #to_s] hostname
|
28
|
+
# @return [Destination]
|
29
|
+
def self.[](hostname)
|
30
|
+
self.open { |hosts| hosts[hostname] }
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Opens a `hosts.txt` file for reading.
|
35
|
+
#
|
36
|
+
# @example Opening the default hosts.txt file
|
37
|
+
# I2P::Hosts.open do |hosts|
|
38
|
+
# ...
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# @example Opening the given hosts.txt file
|
42
|
+
# I2P::Hosts.open("/path/to/hosts.txt") do |hosts|
|
43
|
+
# ...
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# @param [String, #to_s] filename
|
47
|
+
# @param [Hash{Symbol => Object}] options
|
48
|
+
# @yield [hosts]
|
49
|
+
# @yieldparam [Hosts] hosts
|
50
|
+
# @return [Hosts]
|
51
|
+
def self.open(filename = DEFAULT_FILE, options = {}, &block)
|
52
|
+
hosts = self.new(filename, options)
|
53
|
+
block_given? ? block.call(hosts) : hosts
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [Hash]
|
57
|
+
attr_reader :options
|
58
|
+
|
59
|
+
# @return [Hash]
|
60
|
+
attr_reader :cache
|
61
|
+
|
62
|
+
# @return [String]
|
63
|
+
attr_reader :filename
|
64
|
+
|
65
|
+
##
|
66
|
+
# @param [String, #to_s] filename
|
67
|
+
# @param [Hash{Symbol => Object}] options
|
68
|
+
# @yield [hosts]
|
69
|
+
# @yieldparam [Hosts] hosts
|
70
|
+
def initialize(filename = DEFAULT_FILE, options = {}, &block)
|
71
|
+
@cache = {}
|
72
|
+
@filename = File.expand_path(filename.to_s)
|
73
|
+
@options = options.dup
|
74
|
+
block.call(self) if block_given?
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Returns `true` if `hosts.txt` doesn't contain any hostnames.
|
79
|
+
#
|
80
|
+
# @example
|
81
|
+
# hosts.empty?
|
82
|
+
#
|
83
|
+
# @return [Boolean]
|
84
|
+
def empty?
|
85
|
+
count.zero?
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Returns the number of hostnames in `hosts.txt`.
|
90
|
+
#
|
91
|
+
# @example
|
92
|
+
# hosts.count
|
93
|
+
#
|
94
|
+
# @return [Integer]
|
95
|
+
def count
|
96
|
+
each.count
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# Returns `true` if `hosts.txt` includes `value`. The `value` can be
|
101
|
+
# either a hostname or an I2P destination.
|
102
|
+
#
|
103
|
+
# @example
|
104
|
+
# hosts.include?("forum.i2p")
|
105
|
+
#
|
106
|
+
# @param [Destination, Regexp, #to_s] value
|
107
|
+
# @return [Boolean]
|
108
|
+
def include?(value)
|
109
|
+
case value
|
110
|
+
when Destination then each.any? { |k, v| value.eql?(v) }
|
111
|
+
when Regexp then each.any? { |k, v| value === k }
|
112
|
+
else each.any? { |k, v| value.to_s.eql?(k) }
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
##
|
117
|
+
# Returns the I2P destination for `hostname`.
|
118
|
+
#
|
119
|
+
# @example
|
120
|
+
# hosts["forum.i2p"]
|
121
|
+
#
|
122
|
+
# @param [String, #to_s] hostname
|
123
|
+
# @return [Destination]
|
124
|
+
def [](hostname)
|
125
|
+
@cache[hostname.to_s] ||= each_line.find do |line|
|
126
|
+
k, v = parse_line(line)
|
127
|
+
break Destination.parse(v) if hostname === k
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
##
|
132
|
+
# Enumerates the hostnames and I2P destinations in `hosts.txt`.
|
133
|
+
#
|
134
|
+
# @example
|
135
|
+
# hosts.each do |hostname, destination|
|
136
|
+
# ...
|
137
|
+
# end
|
138
|
+
#
|
139
|
+
# @yield [hostname, destination]
|
140
|
+
# @yieldparam [String] hostname
|
141
|
+
# @yieldparam [Destination] destination
|
142
|
+
# @return [Enumerator]
|
143
|
+
def each(&block)
|
144
|
+
if block_given?
|
145
|
+
each_line do |line|
|
146
|
+
k, v = parse_line(line)
|
147
|
+
block.call(k, Destination.parse(v))
|
148
|
+
end
|
149
|
+
end
|
150
|
+
enum_for(:each)
|
151
|
+
end
|
152
|
+
|
153
|
+
##
|
154
|
+
# Returns all hostname mappings as an array.
|
155
|
+
#
|
156
|
+
# @return [Array]
|
157
|
+
def to_a
|
158
|
+
each.inject([]) { |result, kv| result.push(kv) }
|
159
|
+
end
|
160
|
+
|
161
|
+
##
|
162
|
+
# Returns all hostname mappings as a hash.
|
163
|
+
#
|
164
|
+
# @return [Hash]
|
165
|
+
def to_hash
|
166
|
+
each.inject({}) { |result, (k, v)| result.merge!(k => v) }
|
167
|
+
end
|
168
|
+
|
169
|
+
##
|
170
|
+
# Returns all hostname mappings as a string.
|
171
|
+
#
|
172
|
+
# @return [String]
|
173
|
+
def to_s
|
174
|
+
each.inject([]) { |result, (k, v)| result.push([k, v.to_base64].join('=')) }.push('').join($/)
|
175
|
+
end
|
176
|
+
|
177
|
+
protected
|
178
|
+
|
179
|
+
##
|
180
|
+
# Enumerates each line in the `hosts.txt` file.
|
181
|
+
#
|
182
|
+
# @yield [line]
|
183
|
+
# @yieldparam [String] line
|
184
|
+
# @return [Enumerator]
|
185
|
+
def each_line(&block)
|
186
|
+
if block_given?
|
187
|
+
File.open(@filename, 'rb') do |file|
|
188
|
+
file.each_line do |line|
|
189
|
+
line, = line.split('#', 2) if line.include?(?#)
|
190
|
+
line.chomp!.strip!
|
191
|
+
block.call(line) unless line.empty?
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
enum_for(:each_line)
|
196
|
+
end
|
197
|
+
|
198
|
+
##
|
199
|
+
# Parses a hostname mapping line from `hosts.txt`.
|
200
|
+
#
|
201
|
+
# @param [String]
|
202
|
+
# @return [Array(String, String)]
|
203
|
+
def parse_line(line)
|
204
|
+
line.chomp.split('=', 2).map(&:strip)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
data/lib/i2p/sam.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module I2P
|
2
|
+
##
|
3
|
+
# **I2P Simple Anonymous Messaging (SAM) protocol.**
|
4
|
+
#
|
5
|
+
# This is an implementation of the SAM V3 protocol, available since I2P
|
6
|
+
# release [0.7.3](http://www.i2p2.de/release-0.7.3.html).
|
7
|
+
#
|
8
|
+
# Note that for security reasons, the SAM application bridge is not
|
9
|
+
# enabled by default in new I2P installations. To use `I2P::SAM`,
|
10
|
+
# you must first manually enable SAM in the router console's
|
11
|
+
# [client configuration](http://localhost:7657/configclients.jsp).
|
12
|
+
#
|
13
|
+
# Note also that I2P by default doesn't bring up the SAM bridge until 120
|
14
|
+
# seconds after router startup. This delay can be changed by editing the
|
15
|
+
# `~/.i2p/clients.config` configuration file.
|
16
|
+
#
|
17
|
+
# @see http://www.i2p2.de/applications.html
|
18
|
+
# @see http://www.i2p2.de/samv3.html
|
19
|
+
module SAM
|
20
|
+
PROTOCOL_VERSION = 3.0
|
21
|
+
DEFAULT_HOST = (ENV['I2P_SAM_HOST'] || '127.0.0.1').to_s
|
22
|
+
DEFAULT_PORT = (ENV['I2P_SAM_PORT'] || 7656).to_i
|
23
|
+
|
24
|
+
autoload :Client, 'i2p/sam/client'
|
25
|
+
|
26
|
+
##
|
27
|
+
# **I2P Simple Anonymous Messaging (SAM) protocol error conditions.**
|
28
|
+
class Error < StandardError # I2P_ERROR
|
29
|
+
class ProtocolNotSupported < Error; end # NOVERSION
|
30
|
+
class SessionIDNotValid < Error; end # INVALID_ID
|
31
|
+
class SessionIDAlreadyUsed < Error; end # DUPLICATED_ID
|
32
|
+
class DestinationAlreadyUsed < Error; end # DUPLICATED_DEST
|
33
|
+
class KeyNotValid < Error; end # INVALID_KEY
|
34
|
+
class KeyNotFound < Error; end # KEY_NOT_FOUND
|
35
|
+
class PeerNotReachable < Error; end # CANT_REACH_PEER
|
36
|
+
class PeerNotFound < Error; end # PEER_NOT_FOUND
|
37
|
+
class Timeout < Error; end # TIMEOUT
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|