mirror of
https://github.com/go-i2p/go-i2p.git
synced 2025-07-04 21:55:18 -04:00
i2np structs
This commit is contained in:
163
lib/i2np/build_request_record.go
Normal file
163
lib/i2np/build_request_record.go
Normal file
@ -0,0 +1,163 @@
|
||||
package i2np
|
||||
|
||||
import (
|
||||
"github.com/hkparker/go-i2p/lib/common"
|
||||
"github.com/hkparker/go-i2p/lib/tunnel"
|
||||
)
|
||||
|
||||
/*
|
||||
I2P I2NP BuildRequestRecord
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
ElGamal and AES encrypted:
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| encrypted data... |
|
||||
~ ~
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|
||||
encrypted_data :: ElGamal and AES encrypted data
|
||||
length -> 528
|
||||
|
||||
total length: 528
|
||||
|
||||
ElGamal encrypted:
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| toPeer |
|
||||
+ +
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| encrypted data... |
|
||||
~ ~
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|
||||
toPeer :: First 16 bytes of the SHA-256 Hash of the peer's RouterIdentity
|
||||
length -> 16 bytes
|
||||
|
||||
encrypted_data :: ElGamal-2048 encrypted data (see notes)
|
||||
length -> 512
|
||||
|
||||
total length: 528
|
||||
|
||||
Cleartext:
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| receive_tunnel | our_ident |
|
||||
+----+----+----+----+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +----+----+----+----+
|
||||
| | next_tunnel |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| next_ident |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| layer_key |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| iv_key |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| reply_key |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| reply_iv |
|
||||
+ +
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|flag| request_time | send_msg_id
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| |
|
||||
+----+ +
|
||||
| 29 bytes padding |
|
||||
+ +
|
||||
| |
|
||||
+ +----+----+
|
||||
| |
|
||||
+----+----+----+----+----+----+
|
||||
|
||||
receive_tunnel :: TunnelId
|
||||
length -> 4 bytes
|
||||
|
||||
our_ident :: Hash
|
||||
length -> 32 bytes
|
||||
|
||||
next_tunnel :: TunnelId
|
||||
length -> 4 bytes
|
||||
|
||||
next_ident :: Hash
|
||||
length -> 32 bytes
|
||||
|
||||
layer_key :: SessionKey
|
||||
length -> 32 bytes
|
||||
|
||||
iv_key :: SessionKey
|
||||
length -> 32 bytes
|
||||
|
||||
reply_key :: SessionKey
|
||||
length -> 32 bytes
|
||||
|
||||
reply_iv :: data
|
||||
length -> 16 bytes
|
||||
|
||||
flag :: Integer
|
||||
length -> 1 byte
|
||||
|
||||
request_time :: Integer
|
||||
length -> 4 bytes
|
||||
Hours since the epoch, i.e. current time / 3600
|
||||
|
||||
send_message_id :: Integer
|
||||
length -> 4 bytes
|
||||
|
||||
padding :: Data
|
||||
length -> 29 bytes
|
||||
source -> random
|
||||
|
||||
total length: 222
|
||||
*/
|
||||
|
||||
type BuildRequestRecordELGamalAES [528]byte
|
||||
type BuildRequestRecordELGamal [528]byte
|
||||
|
||||
type BuildRequestRecord struct {
|
||||
ReceiveTunnel tunnel.TunnelID
|
||||
OurIdent common.Hash
|
||||
NextTunnel tunnel.TunnelID
|
||||
NextIdent common.Hash
|
||||
LayerKey common.SessionKey
|
||||
IVKey common.SessionKey
|
||||
ReplyKey common.SessionKey
|
||||
ReplyIV [16]byte
|
||||
flag int
|
||||
RequestTime int
|
||||
SendMessageID int
|
||||
Padding [29]byte
|
||||
}
|
48
lib/i2np/build_response_record.go
Normal file
48
lib/i2np/build_response_record.go
Normal file
@ -0,0 +1,48 @@
|
||||
package i2np
|
||||
|
||||
import (
|
||||
"github.com/hkparker/go-i2p/lib/common"
|
||||
)
|
||||
|
||||
/*
|
||||
I2P I2NP BuildResponseRecord
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
Encrypted:
|
||||
|
||||
bytes 0-527 :: AES-encrypted record (note: same size as BuildRequestRecord)
|
||||
|
||||
Unencrypted:
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ SHA-256 Hash of following bytes +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| random data... |
|
||||
~ ~
|
||||
| |
|
||||
+ +----+
|
||||
| | ret|
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|
||||
bytes 0-31 :: SHA-256 Hash of bytes 32-527
|
||||
bytes 32-526 :: random data
|
||||
byte 527 :: reply
|
||||
|
||||
total length: 528
|
||||
*/
|
||||
|
||||
type BuildResponseRecordELGamalAES [528]byte
|
||||
type BuildResponseRecordELGamal [528]byte
|
||||
|
||||
type BuildResponseRecord struct {
|
||||
Hash common.Hash
|
||||
Padding [495]byte
|
||||
Reply byte
|
||||
}
|
24
lib/i2np/data.go
Normal file
24
lib/i2np/data.go
Normal file
@ -0,0 +1,24 @@
|
||||
package i2np
|
||||
|
||||
/*
|
||||
I2P I2NP Data
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
+----+----+----+----+----+-//-+
|
||||
| length | data... |
|
||||
+----+----+----+----+----+-//-+
|
||||
|
||||
length ::
|
||||
4 bytes
|
||||
length of the payload
|
||||
|
||||
data ::
|
||||
$length bytes
|
||||
actual payload of this message
|
||||
*/
|
||||
|
||||
type Data struct {
|
||||
Length int
|
||||
Data []byte
|
||||
}
|
149
lib/i2np/database_lookup.go
Normal file
149
lib/i2np/database_lookup.go
Normal file
@ -0,0 +1,149 @@
|
||||
package i2np
|
||||
|
||||
import (
|
||||
"github.com/hkparker/go-i2p/lib/common"
|
||||
)
|
||||
|
||||
/*
|
||||
I2P I2NP DatabaseLookup
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| SHA256 hash as the key to look up |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| SHA256 hash of the routerInfo |
|
||||
+ who is asking, or the gateway to +
|
||||
| send the reply to |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|flag| reply_tunnelId | size | |
|
||||
+----+----+----+----+----+----+----+ +
|
||||
| SHA256 of $key1 to exclude |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +----+
|
||||
| | |
|
||||
+----+----+----+----+----+----+----+ +
|
||||
| SHA256 of $key2 to exclude |
|
||||
+ +
|
||||
~ ~
|
||||
+ +----+
|
||||
| | |
|
||||
+----+----+----+----+----+----+----+ +
|
||||
| |
|
||||
+ +
|
||||
| Session key if reply encryption |
|
||||
+ was requested +
|
||||
| |
|
||||
+ +----+
|
||||
| |tags|
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| |
|
||||
+ +
|
||||
| Session tags if reply encryption |
|
||||
+ was requested +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|
||||
key ::
|
||||
32 bytes
|
||||
SHA256 hash of the object to lookup
|
||||
|
||||
from ::
|
||||
32 bytes
|
||||
if deliveryFlag == 0, the SHA256 hash of the routerInfo entry this
|
||||
request came from (to which the reply should be
|
||||
sent)
|
||||
if deliveryFlag == 1, the SHA256 hash of the reply tunnel gateway (to
|
||||
which the reply should be sent)
|
||||
|
||||
flags ::
|
||||
1 byte
|
||||
bit order: 76543210
|
||||
bit 0: deliveryFlag
|
||||
0 => send reply directly
|
||||
1 => send reply to some tunnel
|
||||
bit 1: encryptionFlag
|
||||
through release 0.9.5, must be set to 0
|
||||
as of release 0.9.6, ignored
|
||||
as of release 0.9.7:
|
||||
0 => send unencrypted reply
|
||||
1 => send AES encrypted reply using enclosed key and tag
|
||||
bits 3-2: lookup type flags
|
||||
through release 0.9.5, must be set to 00
|
||||
as of release 0.9.6, ignored
|
||||
as of release 0.9.16:
|
||||
00 => normal lookup, return RouterInfo or LeaseSet or
|
||||
DatabaseSearchReplyMessage
|
||||
Not recommended when sending to routers
|
||||
with version 0.9.16 or higher.
|
||||
01 => LS lookup, return LeaseSet or
|
||||
DatabaseSearchReplyMessage
|
||||
10 => RI lookup, return RouterInfo or
|
||||
DatabaseSearchReplyMessage
|
||||
11 => exploration lookup, return DatabaseSearchReplyMessage
|
||||
containing non-floodfill routers only (replaces an
|
||||
excludedPeer of all zeroes)
|
||||
bits 7-4:
|
||||
through release 0.9.5, must be set to 0
|
||||
as of release 0.9.6, ignored, set to 0 for compatibility with
|
||||
future uses and with older routers
|
||||
|
||||
reply_tunnelId ::
|
||||
4 byte TunnelID
|
||||
only included if deliveryFlag == 1
|
||||
tunnelId of the tunnel to send the reply to
|
||||
|
||||
size ::
|
||||
2 byte Integer
|
||||
valid range: 0-512
|
||||
number of peers to exclude from the DatabaseSearchReplyMessage
|
||||
|
||||
excludedPeers ::
|
||||
$size SHA256 hashes of 32 bytes each (total $size*32 bytes)
|
||||
if the lookup fails, these peers are requested to be excluded
|
||||
from the list in the DatabaseSearchReplyMessage.
|
||||
if excludedPeers includes a hash of all zeroes, the request is
|
||||
exploratory, and the DatabaseSearchReplyMessage is requested
|
||||
to list non-floodfill routers only.
|
||||
|
||||
reply_key ::
|
||||
32 byte SessionKey
|
||||
only included if encryptionFlag == 1, only as of release 0.9.7
|
||||
|
||||
tags ::
|
||||
1 byte Integer
|
||||
valid range: 1-32 (typically 1)
|
||||
the number of reply tags that follow
|
||||
only included if encryptionFlag == 1, only as of release 0.9.7
|
||||
|
||||
reply_tags ::
|
||||
one or more 32 byte SessionTags (typically one)
|
||||
only included if encryptionFlag == 1, only as of release 0.9.7
|
||||
*/
|
||||
|
||||
type DatabaseLookup struct {
|
||||
Key common.Hash
|
||||
From common.Hash
|
||||
Flags byte
|
||||
ReplyTunnelID [4]byte
|
||||
Size int
|
||||
ExcludedPeers []common.Hash
|
||||
ReplyKey common.SessionKey
|
||||
tags int
|
||||
ReplyTags []common.SessionTag
|
||||
}
|
63
lib/i2np/database_search_reply.go
Normal file
63
lib/i2np/database_search_reply.go
Normal file
@ -0,0 +1,63 @@
|
||||
package i2np
|
||||
|
||||
import (
|
||||
"github.com/hkparker/go-i2p/lib/common"
|
||||
)
|
||||
|
||||
/*
|
||||
I2P I2NP DatabaseSearchReply
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| SHA256 hash as query key |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| num| peer_hashes |
|
||||
+----+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +----+----+----+----+----+----+----+
|
||||
| | from |
|
||||
+----+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +----+----+----+----+----+----+----+
|
||||
| |
|
||||
+----+
|
||||
|
||||
key ::
|
||||
32 bytes
|
||||
SHA256 of the object being searched
|
||||
|
||||
num ::
|
||||
1 byte Integer
|
||||
number of peer hashes that follow, 0-255
|
||||
|
||||
peer_hashes ::
|
||||
$num SHA256 hashes of 32 bytes each (total $num*32 bytes)
|
||||
SHA256 of the RouterIdentity that the other router thinks is close
|
||||
to the key
|
||||
|
||||
from ::
|
||||
32 bytes
|
||||
SHA256 of the RouterInfo of the router this reply was sent from
|
||||
*/
|
||||
|
||||
type DatabaseSearchReply struct {
|
||||
Key common.Hash
|
||||
Count int
|
||||
PeerHashes []common.Hash
|
||||
From common.Hash
|
||||
}
|
96
lib/i2np/database_store.go
Normal file
96
lib/i2np/database_store.go
Normal file
@ -0,0 +1,96 @@
|
||||
package i2np
|
||||
|
||||
import (
|
||||
"github.com/hkparker/go-i2p/lib/common"
|
||||
)
|
||||
|
||||
/*
|
||||
I2P I2NP DatabaseStore
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
with reply token:
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| SHA256 Hash as key |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|type| reply token | reply_tunnelId
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| SHA256 of the gateway RouterInfo |
|
||||
+----+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +----+----+----+----+----+----+----+
|
||||
| | data ...
|
||||
+----+-//
|
||||
|
||||
with reply token == 0:
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| SHA256 Hash as key |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|type| 0 | data ...
|
||||
+----+----+----+----+----+-//
|
||||
|
||||
key ::
|
||||
32 bytes
|
||||
SHA256 hash
|
||||
|
||||
type ::
|
||||
1 byte
|
||||
type identifier
|
||||
bit 0:
|
||||
0 RouterInfo
|
||||
1 LeaseSet
|
||||
bits 7-1:
|
||||
Through release 0.9.17, must be 0
|
||||
As of release 0.9.18, ignored, reserved for future options, set to 0 for compatibility
|
||||
|
||||
reply token ::
|
||||
4 bytes
|
||||
If greater than zero, a DeliveryStatusMessage
|
||||
is requested with the Message ID set to the value of the Reply Token.
|
||||
A floodfill router is also expected to flood the data to the closest floodfill peers
|
||||
if the token is greater than zero.
|
||||
|
||||
reply_tunnelId ::
|
||||
4 byte TunnelId
|
||||
Only included if reply token > 0
|
||||
This is the TunnelId of the inbound gateway of the tunnel the response should be sent to
|
||||
If $reply_tunnelId is zero, the reply is sent directy to the reply gateway router.
|
||||
|
||||
reply gateway ::
|
||||
32 bytes
|
||||
Hash of the RouterInfo entry to reach the gateway
|
||||
Only included if reply token > 0
|
||||
If $reply_tunnelId is nonzero, this is the router hash of the inbound gateway
|
||||
of the tunnel the response should be sent to.
|
||||
If $reply_tunnelId is zero, this is the router hash the response should be sent to.
|
||||
|
||||
data ::
|
||||
If type == 0, data is a 2-byte Integer specifying the number of bytes that follow,
|
||||
followed by a gzip-compressed RouterInfo.
|
||||
If type == 1, data is an uncompressed LeaseSet.
|
||||
*/
|
||||
|
||||
type DatabaseStore struct {
|
||||
Key common.Hash
|
||||
Type byte
|
||||
ReplyToken [4]byte
|
||||
ReplyTunnelID [4]byte
|
||||
ReplyGateway common.Hash
|
||||
Data []byte
|
||||
}
|
29
lib/i2np/delivery_status.go
Normal file
29
lib/i2np/delivery_status.go
Normal file
@ -0,0 +1,29 @@
|
||||
package i2np
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
I2P I2NP DeliveryStatus
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
+----+----+----+----+----+----+----+----+----+----+----+----+
|
||||
| msg_id | time_stamp |
|
||||
+----+----+----+----+----+----+----+----+----+----+----+----+
|
||||
|
||||
msg_id :: Integer
|
||||
4 bytes
|
||||
unique ID of the message we deliver the DeliveryStatus for (see
|
||||
I2NPMessageHeader for details)
|
||||
|
||||
time_stamp :: Date
|
||||
8 bytes
|
||||
time the message was successfully created or delivered
|
||||
*/
|
||||
|
||||
type DeliveryStatus struct {
|
||||
MessageID int
|
||||
Timestamp time.Time
|
||||
}
|
72
lib/i2np/garlic.go
Normal file
72
lib/i2np/garlic.go
Normal file
@ -0,0 +1,72 @@
|
||||
package i2np
|
||||
|
||||
import (
|
||||
"github.com/hkparker/go-i2p/lib/common"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
I2P I2NP Garlic
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
Encrypted:
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| length | data |
|
||||
+----+----+----+----+ +
|
||||
| |
|
||||
~ ~
|
||||
~ ~
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|
||||
length ::
|
||||
4 byte Integer
|
||||
number of bytes that follow 0 - 64 KB
|
||||
|
||||
data ::
|
||||
$length bytes
|
||||
ElGamal encrypted data
|
||||
|
||||
Unencrypted data:
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| num| clove 1 |
|
||||
+----+ +
|
||||
| |
|
||||
~ ~
|
||||
~ ~
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| clove 2 ... |
|
||||
~ ~
|
||||
~ ~
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| Certificate | Message_ID |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
Expiration |
|
||||
+----+----+----+----+----+----+----+
|
||||
|
||||
num ::
|
||||
1 byte Integer number of GarlicCloves to follow
|
||||
|
||||
clove :: a GarlicClove
|
||||
|
||||
Certificate :: always NULL in the current implementation (3 bytes total, all zeroes)
|
||||
|
||||
Message_ID :: 4 byte Integer
|
||||
|
||||
Expiration :: Date (8 bytes)
|
||||
*/
|
||||
|
||||
type GarlicElGamal []byte
|
||||
|
||||
type Garlic struct {
|
||||
Count int
|
||||
Cloves []GarlicClove
|
||||
Certificate common.Certificate
|
||||
MessageID int
|
||||
Expiration time.Time
|
||||
}
|
49
lib/i2np/garlic_clove.go
Normal file
49
lib/i2np/garlic_clove.go
Normal file
@ -0,0 +1,49 @@
|
||||
package i2np
|
||||
|
||||
import (
|
||||
"github.com/hkparker/go-i2p/lib/common"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
I2P I2NP GarlicClove
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
Unencrypted:
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| Delivery Instructions |
|
||||
~ ~
|
||||
~ ~
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| I2NP Message |
|
||||
~ ~
|
||||
~ ~
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| Clove ID | Expiration
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| Certificate |
|
||||
+----+----+----+----+----+----+----+
|
||||
|
||||
Delivery Instructions :: as defined below
|
||||
Length varies but is typically 1, 33, or 37 bytes
|
||||
|
||||
I2NP Message :: Any I2NP Message
|
||||
|
||||
Clove ID :: 4 byte Integer
|
||||
|
||||
Expiration :: Date (8 bytes)
|
||||
|
||||
Certificate :: Always NULL in the current implementation (3 bytes total, all zeroes)
|
||||
*/
|
||||
|
||||
type GarlicClove struct {
|
||||
DeliveryInstructions GarlicCloveDeliveryInstructions
|
||||
I2NPMessage I2NPMessage
|
||||
CloveID int
|
||||
Expiration time.Time
|
||||
Certificate common.Certificate
|
||||
}
|
80
lib/i2np/garlic_clove_delivery_instructions.go
Normal file
80
lib/i2np/garlic_clove_delivery_instructions.go
Normal file
@ -0,0 +1,80 @@
|
||||
package i2np
|
||||
|
||||
import (
|
||||
"github.com/hkparker/go-i2p/lib/common"
|
||||
"github.com/hkparker/go-i2p/lib/tunnel"
|
||||
)
|
||||
|
||||
/*
|
||||
I2P I2NP GarlicCloveDeliveryInstructions
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|flag| |
|
||||
+----+ +
|
||||
| |
|
||||
+ Session Key (optional) +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +----+----+----+----+--------------+
|
||||
| | |
|
||||
+----+ +
|
||||
| |
|
||||
+ To Hash (optional) +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ +----+----+----+----+--------------+
|
||||
| | Tunnel ID (opt) | Delay (opt)
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|
|
||||
+----+
|
||||
|
||||
flag ::
|
||||
1 byte
|
||||
Bit order: 76543210
|
||||
bit 7: encrypted? Unimplemented, always 0
|
||||
If 1, a 32-byte encryption session key is included
|
||||
bits 6-5: delivery type
|
||||
0x0 = LOCAL, 0x01 = DESTINATION, 0x02 = ROUTER, 0x03 = TUNNEL
|
||||
bit 4: delay included? Not fully implemented, always 0
|
||||
If 1, four delay bytes are included
|
||||
bits 3-0: reserved, set to 0 for compatibility with future uses
|
||||
|
||||
Session Key ::
|
||||
32 bytes
|
||||
Optional, present if encrypt flag bit is set.
|
||||
Unimplemented, never set, never present.
|
||||
|
||||
To Hash ::
|
||||
32 bytes
|
||||
Optional, present if delivery type is DESTINATION, ROUTER, or TUNNEL
|
||||
If DESTINATION, the SHA256 Hash of the destination
|
||||
If ROUTER, the SHA256 Hash of the router
|
||||
If TUNNEL, the SHA256 Hash of the gateway router
|
||||
|
||||
Tunnel ID :: TunnelId
|
||||
4 bytes
|
||||
Optional, present if delivery type is TUNNEL
|
||||
The destination tunnel ID
|
||||
|
||||
Delay :: Integer
|
||||
4 bytes
|
||||
Optional, present if delay included flag is set
|
||||
Not fully implemented. Specifies the delay in seconds.
|
||||
|
||||
Total length: Typical length is:
|
||||
1 byte for LOCAL delivery;
|
||||
33 bytes for ROUTER / DESTINATION delivery;
|
||||
37 bytes for TUNNEL delivery
|
||||
*/
|
||||
|
||||
type GarlicCloveDeliveryInstructions struct {
|
||||
Flag byte
|
||||
SessionKey common.SessionKey
|
||||
Hash common.Hash
|
||||
TunnelID tunnel.TunnelID
|
||||
Delay int
|
||||
}
|
281
lib/i2np/header.go
Normal file
281
lib/i2np/header.go
Normal file
@ -0,0 +1,281 @@
|
||||
package i2np
|
||||
|
||||
import (
|
||||
"errors"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/hkparker/go-i2p/lib/common"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
I2P I2NP Message
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
Standard (16 bytes):
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|type| msg_id | expiration
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| size |chks|
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|
||||
Short (SSU, 5 bytes):
|
||||
|
||||
+----+----+----+----+----+
|
||||
|type| short_expiration |
|
||||
+----+----+----+----+----+
|
||||
|
||||
type :: Integer
|
||||
length -> 1 byte
|
||||
purpose -> identifies the message type (see table below)
|
||||
|
||||
msg_id :: Integer
|
||||
length -> 4 bytes
|
||||
purpose -> uniquely identifies this message (for some time at least)
|
||||
This is usually a locally-generated random number, but
|
||||
for outgoing tunnel build messages it may be derived from
|
||||
the incoming message. See below.
|
||||
|
||||
expiration :: Date
|
||||
8 bytes
|
||||
date this message will expire
|
||||
|
||||
short_expiration :: Integer
|
||||
4 bytes
|
||||
date this message will expire (seconds since the epoch)
|
||||
|
||||
size :: Integer
|
||||
length -> 2 bytes
|
||||
purpose -> length of the payload
|
||||
|
||||
chks :: Integer
|
||||
length -> 1 byte
|
||||
purpose -> checksum of the payload
|
||||
SHA256 hash truncated to the first byte
|
||||
|
||||
data ::
|
||||
length -> $size bytes
|
||||
purpose -> actual message contents
|
||||
*/
|
||||
|
||||
const (
|
||||
I2NP_MESSAGE_TYPE_DATABASE_STORE = 1
|
||||
I2NP_MESSAGE_TYPE_DATABASE_LOOKUP = 2
|
||||
I2NP_MESSAGE_TYPE_DATABASE_SEARCH_REPLY = 3
|
||||
I2NP_MESSAGE_TYPE_DELIVERY_STATUS = 10
|
||||
I2NP_MESSAGE_TYPE_GARLIC = 11
|
||||
I2NP_MESSAGE_TYPE_TUNNEL_DATA = 18
|
||||
I2NP_MESSAGE_TYPE_TUNNEL_GATEWAY = 19
|
||||
I2NP_MESSAGE_TYPE_DATA = 20
|
||||
I2NP_MESSAGE_TYPE_TUNNEL_BUILD = 21
|
||||
I2NP_MESSAGE_TYPE_TUNNEL_BUILD_REPLY = 22
|
||||
I2NP_MESSAGE_TYPE_VARIABLE_TUNNEL_BUILD = 23
|
||||
I2NP_MESSAGE_TYPE_VARIABLE_TUNNEL_BUILD_REPLY = 24
|
||||
)
|
||||
|
||||
type I2NPNTCPHeader struct {
|
||||
Type int
|
||||
MessageID int
|
||||
Expiration time.Time
|
||||
Size int
|
||||
Checksum int
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type I2NPSSUHeader struct {
|
||||
Type int
|
||||
Expiration time.Time
|
||||
}
|
||||
|
||||
var ERR_I2NP_NOT_ENOUGH_DATA = errors.New("not enough i2np header data")
|
||||
|
||||
// Read an entire I2NP message and return the parsed header
|
||||
// with embedded encrypted data
|
||||
func ReadI2NPNTCPHeader(data []byte) (I2NPNTCPHeader, error) {
|
||||
header := I2NPNTCPHeader{}
|
||||
|
||||
message_type, err := ReadI2NPType(data)
|
||||
if err != nil {
|
||||
return header, err
|
||||
} else {
|
||||
header.Type = message_type
|
||||
}
|
||||
|
||||
message_id, err := ReadI2NPNTCPMessageID(data)
|
||||
if err != nil {
|
||||
return header, err
|
||||
} else {
|
||||
header.MessageID = message_id
|
||||
}
|
||||
|
||||
message_date, err := ReadI2NPNTCPMessageExpiration(data)
|
||||
if err != nil {
|
||||
return header, err
|
||||
} else {
|
||||
header.Expiration = message_date.Time()
|
||||
}
|
||||
|
||||
message_size, err := ReadI2NPNTCPMessageSize(data)
|
||||
if err != nil {
|
||||
return header, err
|
||||
} else {
|
||||
header.Size = message_size
|
||||
}
|
||||
|
||||
message_checksum, err := ReadI2NPNTCPMessageChecksum(data)
|
||||
if err != nil {
|
||||
return header, err
|
||||
} else {
|
||||
header.Checksum = message_checksum
|
||||
}
|
||||
|
||||
message_data, err := ReadI2NPNTCPData(data, header.Size)
|
||||
if err != nil {
|
||||
return header, err
|
||||
} else {
|
||||
header.Data = message_data
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPNTCPHeader",
|
||||
"error": err.Error(),
|
||||
}).Debug("parsed_i2np_ntcp_header")
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func ReadI2NPSSUHeader(data []byte) (I2NPSSUHeader, error) {
|
||||
header := I2NPSSUHeader{}
|
||||
|
||||
message_type, err := ReadI2NPType(data)
|
||||
if err != nil {
|
||||
return header, err
|
||||
} else {
|
||||
header.Type = message_type
|
||||
}
|
||||
|
||||
message_date, err := ReadI2NPSSUMessageExpiration(data)
|
||||
if err != nil {
|
||||
return header, err
|
||||
} else {
|
||||
header.Expiration = message_date.Time()
|
||||
}
|
||||
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func ReadI2NPType(data []byte) (int, error) {
|
||||
if len(data) < 1 {
|
||||
return 0, ERR_I2NP_NOT_ENOUGH_DATA
|
||||
}
|
||||
|
||||
message_type := common.Integer([]byte{data[0]})
|
||||
|
||||
if (message_type >= 4 || message_type <= 9) ||
|
||||
(message_type >= 12 || message_type <= 17) {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPType",
|
||||
"type": message_type,
|
||||
}).Warn("unknown_i2np_type")
|
||||
}
|
||||
|
||||
if message_type >= 224 || message_type <= 254 {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPType",
|
||||
"type": message_type,
|
||||
}).Warn("experimental_i2np_type")
|
||||
}
|
||||
|
||||
if message_type == 255 {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPType",
|
||||
"type": message_type,
|
||||
}).Warn("reserved_i2np_type")
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPType",
|
||||
"type": message_type,
|
||||
}).Debug("parsed_i2np_type")
|
||||
return message_type, nil
|
||||
}
|
||||
|
||||
func ReadI2NPNTCPMessageID(data []byte) (int, error) {
|
||||
if len(data) < 5 {
|
||||
return 0, ERR_I2NP_NOT_ENOUGH_DATA
|
||||
}
|
||||
|
||||
message_id := common.Integer(data[1:5])
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPNTCPMessageID",
|
||||
"type": message_id,
|
||||
}).Debug("parsed_i2np_message_id")
|
||||
return message_id, nil
|
||||
}
|
||||
|
||||
func ReadI2NPNTCPMessageExpiration(data []byte) (common.Date, error) {
|
||||
if len(data) < 13 {
|
||||
return common.Date{}, ERR_I2NP_NOT_ENOUGH_DATA
|
||||
}
|
||||
|
||||
date := common.Date{}
|
||||
copy(date[:], data[5:13])
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPNTCPMessageExpiration",
|
||||
"date": date,
|
||||
}).Debug("parsed_i2np_message_date")
|
||||
return date, nil
|
||||
}
|
||||
|
||||
func ReadI2NPSSUMessageExpiration(data []byte) (common.Date, error) {
|
||||
if len(data) < 5 {
|
||||
return common.Date{}, ERR_I2NP_NOT_ENOUGH_DATA
|
||||
}
|
||||
|
||||
date := common.Date{}
|
||||
copy(date[4:], data[1:5])
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPSSUMessageExpiration",
|
||||
"date": date,
|
||||
}).Debug("parsed_i2np_message_date")
|
||||
return date, nil
|
||||
}
|
||||
|
||||
func ReadI2NPNTCPMessageSize(data []byte) (int, error) {
|
||||
if len(data) < 15 {
|
||||
return 0, ERR_I2NP_NOT_ENOUGH_DATA
|
||||
}
|
||||
|
||||
size := common.Integer(data[13:15])
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPNTCPMessageSize",
|
||||
"size": size,
|
||||
}).Debug("parsed_i2np_message_size")
|
||||
return size, nil
|
||||
}
|
||||
|
||||
func ReadI2NPNTCPMessageChecksum(data []byte) (int, error) {
|
||||
if len(data) < 16 {
|
||||
return 0, ERR_I2NP_NOT_ENOUGH_DATA
|
||||
}
|
||||
|
||||
checksum := common.Integer(data[15:16])
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPNTCPMessageCHecksum",
|
||||
"checksum": checksum,
|
||||
}).Debug("parsed_i2np_message_checksum")
|
||||
return checksum, nil
|
||||
}
|
||||
|
||||
func ReadI2NPNTCPData(data []byte, size int) ([]byte, error) {
|
||||
if len(data) < 16+size {
|
||||
return []byte{}, ERR_I2NP_NOT_ENOUGH_DATA
|
||||
}
|
||||
|
||||
return data[16 : 16+size], nil
|
||||
}
|
@ -38,18 +38,34 @@ func TestReadI2NPNTCPMessageIDWithValidData(t *testing.T) {
|
||||
assert.Nil(err)
|
||||
}
|
||||
|
||||
func TestReadI2NPNTCPMessageDateWithMissingData(t *testing.T) {
|
||||
func TestReadI2NPNTCPMessageExpirationWithMissingData(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
date, err := ReadI2NPNTCPMessageDate([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||
date, err := ReadI2NPNTCPMessageExpiration([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||
assert.Equal(common.Date{}, date)
|
||||
assert.Equal(ERR_I2NP_NOT_ENOUGH_DATA, err)
|
||||
}
|
||||
|
||||
func TestReadI2NPNTCPMessageDateWithValidData(t *testing.T) {
|
||||
func TestReadI2NPNTCPMessageExpirationWithValidData(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
date, err := ReadI2NPNTCPMessageDate([]byte{0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0x26, 0x5c, 0x00})
|
||||
date, err := ReadI2NPNTCPMessageExpiration([]byte{0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0x26, 0x5c, 0x00})
|
||||
assert.Equal(int64(86400), date.Time().Unix())
|
||||
assert.Nil(err)
|
||||
}
|
||||
|
||||
func TestReadI2NPSSUMessageExpirationWithMissingData(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
date, err := ReadI2NPSSUMessageExpiration([]byte{0x00, 0x00, 0x00, 0x00})
|
||||
assert.Equal(common.Date{}, date)
|
||||
assert.Equal(ERR_I2NP_NOT_ENOUGH_DATA, err)
|
||||
}
|
||||
|
||||
func TestReadI2NPSSUMessageExpirationWithValidData(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
date, err := ReadI2NPSSUMessageExpiration([]byte{0x01, 0x05, 0x26, 0x5c, 0x00})
|
||||
assert.Equal(int64(86400), date.Time().Unix())
|
||||
assert.Nil(err)
|
||||
}
|
||||
@ -85,3 +101,35 @@ func TestReadI2NPNTCPMessageChecksumWithValidData(t *testing.T) {
|
||||
assert.Equal(1, checksum)
|
||||
assert.Nil(err)
|
||||
}
|
||||
|
||||
func TestReadI2NPNTCPDataWithNoData(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
data, err := ReadI2NPNTCPData([]byte{}, 3)
|
||||
assert.Equal([]byte{}, data)
|
||||
assert.Equal(ERR_I2NP_NOT_ENOUGH_DATA, err)
|
||||
}
|
||||
|
||||
func TestReadI2NPNTCPDataWithMissingData(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
data, err := ReadI2NPNTCPData([]byte{0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0x26, 0x5c, 0x00, 0x00, 0x03, 0x01, 0x01, 0x02}, 3)
|
||||
assert.Equal([]byte{}, data)
|
||||
assert.Equal(ERR_I2NP_NOT_ENOUGH_DATA, err)
|
||||
}
|
||||
|
||||
func TestReadI2NPNTCPDataWithExtraData(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
data, err := ReadI2NPNTCPData([]byte{0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0x26, 0x5c, 0x00, 0x00, 0x03, 0x01, 0x01, 0x02, 0x03, 0x04}, 3)
|
||||
assert.Equal([]byte{0x01, 0x02, 0x03}, data)
|
||||
assert.Nil(err)
|
||||
}
|
||||
|
||||
func TestReadI2NPNTCPDataWithValidData(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
data, err := ReadI2NPNTCPData([]byte{0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0x26, 0x5c, 0x00, 0x00, 0x03, 0x01, 0x01, 0x02, 0x03}, 3)
|
||||
assert.Equal([]byte{0x01, 0x02, 0x03}, data)
|
||||
assert.Nil(err)
|
||||
}
|
225
lib/i2np/i2np.go
225
lib/i2np/i2np.go
@ -1,228 +1,3 @@
|
||||
package i2np
|
||||
|
||||
import (
|
||||
"errors"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/hkparker/go-i2p/lib/common"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
I2P I2NP Message
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
Standard (16 bytes):
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|type| msg_id | expiration
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| size |chks|
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|
||||
Short (SSU, 5 bytes):
|
||||
|
||||
+----+----+----+----+----+
|
||||
|type| short_expiration |
|
||||
+----+----+----+----+----+
|
||||
|
||||
type :: Integer
|
||||
length -> 1 byte
|
||||
purpose -> identifies the message type (see table below)
|
||||
|
||||
msg_id :: Integer
|
||||
length -> 4 bytes
|
||||
purpose -> uniquely identifies this message (for some time at least)
|
||||
This is usually a locally-generated random number, but
|
||||
for outgoing tunnel build messages it may be derived from
|
||||
the incoming message. See below.
|
||||
|
||||
expiration :: Date
|
||||
8 bytes
|
||||
date this message will expire
|
||||
|
||||
short_expiration :: Integer
|
||||
4 bytes
|
||||
date this message will expire (seconds since the epoch)
|
||||
|
||||
size :: Integer
|
||||
length -> 2 bytes
|
||||
purpose -> length of the payload
|
||||
|
||||
chks :: Integer
|
||||
length -> 1 byte
|
||||
purpose -> checksum of the payload
|
||||
SHA256 hash truncated to the first byte
|
||||
|
||||
data ::
|
||||
length -> $size bytes
|
||||
purpose -> actual message contents
|
||||
*/
|
||||
|
||||
const (
|
||||
I2NP_MESSAGE_TYPE_DATABASE_STORE = 1
|
||||
I2NP_MESSAGE_TYPE_DATABASE_LOOKUP = 2
|
||||
I2NP_MESSAGE_TYPE_DATABASE_SEARCH_REPLY = 3
|
||||
I2NP_MESSAGE_TYPE_DELIVERY_STATUS = 10
|
||||
I2NP_MESSAGE_TYPE_GARLIC = 11
|
||||
I2NP_MESSAGE_TYPE_TUNNEL_DATA = 18
|
||||
I2NP_MESSAGE_TYPE_TUNNEL_GATEWAY = 19
|
||||
I2NP_MESSAGE_TYPE_DATA = 20
|
||||
I2NP_MESSAGE_TYPE_TUNNEL_BUILD = 21
|
||||
I2NP_MESSAGE_TYPE_TUNNEL_BUILD_REPLY = 22
|
||||
I2NP_MESSAGE_TYPE_VARIABLE_TUNNEL_BUILD = 23
|
||||
I2NP_MESSAGE_TYPE_VARIABLE_TUNNEL_BUILD_REPLY = 24
|
||||
)
|
||||
|
||||
type I2NPMessage []byte
|
||||
|
||||
type I2NPHeader struct {
|
||||
Type int
|
||||
MessageID int
|
||||
Expiration time.Time
|
||||
Size int
|
||||
Checksum int
|
||||
Data []byte
|
||||
}
|
||||
|
||||
var ERR_I2NP_NOT_ENOUGH_DATA = errors.New("not enough i2np header data")
|
||||
|
||||
// Read an entire I2NP message and return the parsed header
|
||||
// with embedded encrypted data
|
||||
func ReadI2NPNTCPHeader(data []byte) (I2NPHeader, error) {
|
||||
header := I2NPHeader{}
|
||||
|
||||
message_type, err := ReadI2NPType(data)
|
||||
if err != nil {
|
||||
return header, err
|
||||
} else {
|
||||
header.Type = message_type
|
||||
}
|
||||
|
||||
message_id, err := ReadI2NPNTCPMessageID(data)
|
||||
if err != nil {
|
||||
return header, err
|
||||
} else {
|
||||
header.MessageID = message_id
|
||||
}
|
||||
|
||||
message_date, err := ReadI2NPNTCPMessageDate(data)
|
||||
if err != nil {
|
||||
return header, err
|
||||
} else {
|
||||
header.Expiration = message_date.Time()
|
||||
}
|
||||
|
||||
message_size, err := ReadI2NPNTCPMessageSize(data)
|
||||
if err != nil {
|
||||
return header, err
|
||||
} else {
|
||||
header.Size = message_size
|
||||
}
|
||||
|
||||
message_checksum, err := ReadI2NPNTCPMessageChecksum(data)
|
||||
if err != nil {
|
||||
return header, err
|
||||
} else {
|
||||
header.Checksum = message_checksum
|
||||
}
|
||||
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func ReadI2NPSSUHeader(data []byte) (I2NPHeader, error) {
|
||||
return I2NPHeader{}, nil
|
||||
}
|
||||
|
||||
func ReadI2NPType(data []byte) (int, error) {
|
||||
if len(data) < 1 {
|
||||
return 0, ERR_I2NP_NOT_ENOUGH_DATA
|
||||
}
|
||||
|
||||
message_type := common.Integer([]byte{data[0]})
|
||||
|
||||
if (message_type >= 4 || message_type <= 9) ||
|
||||
(message_type >= 12 || message_type <= 17) {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPType",
|
||||
"type": message_type,
|
||||
}).Warn("unknown_i2np_type")
|
||||
}
|
||||
|
||||
if message_type >= 224 || message_type <= 254 {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPType",
|
||||
"type": message_type,
|
||||
}).Warn("experimental_i2np_type")
|
||||
}
|
||||
|
||||
if message_type == 255 {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPType",
|
||||
"type": message_type,
|
||||
}).Warn("reserved_i2np_type")
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPType",
|
||||
"type": message_type,
|
||||
}).Debug("parsed_i2np_type")
|
||||
return message_type, nil
|
||||
}
|
||||
|
||||
func ReadI2NPNTCPMessageID(data []byte) (int, error) {
|
||||
if len(data) < 5 {
|
||||
return 0, ERR_I2NP_NOT_ENOUGH_DATA
|
||||
}
|
||||
|
||||
message_id := common.Integer(data[1:5])
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPNTCPMessageID",
|
||||
"type": message_id,
|
||||
}).Debug("parsed_i2np_message_id")
|
||||
return message_id, nil
|
||||
}
|
||||
|
||||
func ReadI2NPNTCPMessageDate(data []byte) (common.Date, error) {
|
||||
if len(data) < 13 {
|
||||
return common.Date{}, ERR_I2NP_NOT_ENOUGH_DATA
|
||||
}
|
||||
|
||||
date := common.Date{}
|
||||
copy(date[:], data[5:13])
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPNTCPMessageDate",
|
||||
"date": date,
|
||||
}).Debug("parsed_i2np_message_date")
|
||||
return date, nil
|
||||
}
|
||||
|
||||
func ReadI2NPNTCPMessageSize(data []byte) (int, error) {
|
||||
if len(data) < 15 {
|
||||
return 0, ERR_I2NP_NOT_ENOUGH_DATA
|
||||
}
|
||||
|
||||
size := common.Integer(data[13:15])
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPNTCPMessageSize",
|
||||
"size": size,
|
||||
}).Debug("parsed_i2np_message_size")
|
||||
return size, nil
|
||||
}
|
||||
|
||||
func ReadI2NPNTCPMessageChecksum(data []byte) (int, error) {
|
||||
if len(data) < 16 {
|
||||
return 0, ERR_I2NP_NOT_ENOUGH_DATA
|
||||
}
|
||||
|
||||
checksum := common.Integer(data[15:16])
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"at": "i2np.ReadI2NPNTCPMessageCHecksum",
|
||||
"checksum": checksum,
|
||||
}).Debug("parsed_i2np_message_checksum")
|
||||
return checksum, nil
|
||||
}
|
||||
|
28
lib/i2np/tunnel_build.go
Normal file
28
lib/i2np/tunnel_build.go
Normal file
@ -0,0 +1,28 @@
|
||||
package i2np
|
||||
|
||||
/*
|
||||
I2P I2NP TunnelBuild
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| Record 0 ... |
|
||||
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| Record 1 ... |
|
||||
|
||||
~ ..... ~
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| Record 7 ... |
|
||||
|
||||
| |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|
||||
Just 8 BuildRequestRecords attached together
|
||||
record size: 528 bytes
|
||||
total size: 8*528 = 4224 bytes
|
||||
*/
|
||||
|
||||
type TunnelBuild [8]BuildRequestRecord
|
11
lib/i2np/tunnel_build_reply.go
Normal file
11
lib/i2np/tunnel_build_reply.go
Normal file
@ -0,0 +1,11 @@
|
||||
package i2np
|
||||
|
||||
/*
|
||||
I2P I2NP TunnelBuildReply
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
Same format as TunnelBuildMessage, with BuildResponseRecords
|
||||
*/
|
||||
|
||||
type TunnelBuildReply [8]BuildResponseRecord
|
29
lib/i2np/tunnel_data.go
Normal file
29
lib/i2np/tunnel_data.go
Normal file
@ -0,0 +1,29 @@
|
||||
package i2np
|
||||
|
||||
/*
|
||||
I2P I2NP TunnelData
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| tunnnelID | data |
|
||||
+----+----+----+----+ |
|
||||
| |
|
||||
~ ~
|
||||
~ ~
|
||||
| |
|
||||
+ +----+----+----+----+
|
||||
| |
|
||||
+----+----+----+----+
|
||||
|
||||
tunnelId ::
|
||||
4 byte TunnelId
|
||||
identifies the tunnel this message is directed at
|
||||
|
||||
data ::
|
||||
1024 bytes
|
||||
payload data.. fixed to 1024 bytes
|
||||
*/
|
||||
|
||||
type TunnelData [1028]byte
|
33
lib/i2np/tunnel_gateway.go
Normal file
33
lib/i2np/tunnel_gateway.go
Normal file
@ -0,0 +1,33 @@
|
||||
package i2np
|
||||
|
||||
import (
|
||||
"github.com/hkparker/go-i2p/lib/tunnel"
|
||||
)
|
||||
|
||||
/*
|
||||
I2P I2NP TunnelGateway
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
+----+----+----+----+----+----+----+-//
|
||||
| tunnelId | length | data...
|
||||
+----+----+----+----+----+----+----+-//
|
||||
|
||||
tunnelId ::
|
||||
4 byte TunnelId
|
||||
identifies the tunnel this message is directed at
|
||||
|
||||
length ::
|
||||
2 byte Integer
|
||||
length of the payload
|
||||
|
||||
data ::
|
||||
$length bytes
|
||||
actual payload of this message
|
||||
*/
|
||||
|
||||
type TunnelGatway struct {
|
||||
TunnelID tunnel.TunnelID
|
||||
Length int
|
||||
Data []byte
|
||||
}
|
26
lib/i2np/variable_tunnel_build.go
Normal file
26
lib/i2np/variable_tunnel_build.go
Normal file
@ -0,0 +1,26 @@
|
||||
package i2np
|
||||
|
||||
/*
|
||||
I2P I2NP VariableTunnelBuild
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| num| BuildRequestRecords...
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|
||||
Same format as TunnelBuildMessage, except for the addition of a $num field
|
||||
in front and $num number of BuildRequestRecords instead of 8
|
||||
|
||||
num ::
|
||||
1 byte Integer
|
||||
Valid values: 1-8
|
||||
|
||||
record size: 528 bytes
|
||||
total size: 1+$num*528
|
||||
*/
|
||||
|
||||
type VariableTunnelBuild struct {
|
||||
Count int
|
||||
BuildRequestRecords []BuildRequestRecord
|
||||
}
|
18
lib/i2np/variable_tunnel_build_reply.go
Normal file
18
lib/i2np/variable_tunnel_build_reply.go
Normal file
@ -0,0 +1,18 @@
|
||||
package i2np
|
||||
|
||||
/*
|
||||
I2P I2NP VariableTunnelBuildReply
|
||||
https://geti2p.net/spec/i2np
|
||||
Accurate for version 0.9.28
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| num| BuildResponseRecords...
|
||||
+----+----+----+----+----+----+----+----+
|
||||
|
||||
Same format as VariableTunnelBuildMessage, with BuildResponseRecords.
|
||||
*/
|
||||
|
||||
type VariableTunnelBuildReply struct {
|
||||
Count int
|
||||
BuildResponseRecords []BuildResponseRecord
|
||||
}
|
Reference in New Issue
Block a user