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