2018-03-04 12:41:44 -05:00
|
|
|
package go_i2cp
|
2018-03-04 17:52:55 -05:00
|
|
|
import (
|
|
|
|
sll "github.com/emirpasic/gods/lists/singlylinkedlist"
|
|
|
|
"net/rpc"
|
|
|
|
"bytes"
|
|
|
|
"sync"
|
|
|
|
)
|
2018-03-04 12:41:44 -05:00
|
|
|
const I2P_CLIENT_VERSION string = "0.9.11"
|
2018-03-04 17:52:55 -05:00
|
|
|
const TAG = CLIENT
|
|
|
|
const I2CP_PROTOCOL_INIT uint8 = 0x2a
|
2018-03-04 12:41:44 -05:00
|
|
|
const I2CP_MESSAGE_SIZE int = 0xffff
|
|
|
|
const I2CP_MAX_SESSIONS int = 0xffff
|
|
|
|
const I2CP_MAX_SESSIONS_PER_CLIENT int = 32
|
|
|
|
|
|
|
|
const I2CP_MSG_ANY int = 0
|
2018-03-04 17:52:55 -05:00
|
|
|
const I2CP_MSG_BANDWIDTH_LIMITS uint8 = 23
|
|
|
|
const I2CP_MSG_CREATE_LEASE_SET uint8 = 4
|
|
|
|
const I2CP_MSG_CREATE_SESSION uint8 = 1
|
|
|
|
const I2CP_MSG_DEST_LOOKUP uint8 = 34
|
|
|
|
const I2CP_MSG_DEST_REPLY uint8 = 35
|
|
|
|
const I2CP_MSG_DESTROY_SESSION uint8 = 3
|
|
|
|
const I2CP_MSG_DISCONNECT uint8 = 30
|
|
|
|
const I2CP_MSG_GET_BANDWIDTH_LIMITS uint8 = 8
|
|
|
|
const I2CP_MSG_GET_DATE uint8 = 32
|
|
|
|
const I2CP_MSG_HOST_LOOKUP uint8 = 38
|
|
|
|
const I2CP_MSG_HOST_REPLY uint8 = 39
|
|
|
|
const I2CP_MSG_MESSAGE_STATUS uint8 = 22
|
|
|
|
const I2CP_MSG_PAYLOAD_MESSAGE uint8 = 31
|
|
|
|
const I2CP_MSG_REQUEST_LEASESET uint8 = 21
|
|
|
|
const I2CP_MSG_REQUEST_VARIABLE_LEASESET uint8 = 37
|
|
|
|
const I2CP_MSG_SEND_MESSAGE uint8 = 5
|
|
|
|
const I2CP_MSG_SESSION_STATUS uint8 = 20
|
|
|
|
const I2CP_MSG_SET_DATE uint8 = 33
|
2018-03-04 12:41:44 -05:00
|
|
|
|
|
|
|
/* Router capabilities */
|
|
|
|
const ROUTER_CAN_HOST_LOOKUP int = 1
|
|
|
|
|
|
|
|
const (
|
|
|
|
CLIENT_PROP_ROUTER_ADDRESS = iota
|
|
|
|
CLIENT_PROP_ROUTER_PORT = iota
|
|
|
|
CLIENT_PROP_ROUTER_USE_TLS = iota
|
|
|
|
CLIENT_PROP_USERNAME = iota
|
|
|
|
CLIENT_PROP_PASSWORD = iota
|
|
|
|
NR_OF_I2CP_CLIENT_PROPERTIES = iota
|
|
|
|
)
|
|
|
|
const (
|
|
|
|
PROTOCOL_STREAMING = 6
|
|
|
|
PROTOCOL_DATAGRAM = 17
|
|
|
|
PROTOCOL_RAW_DATAGRAM = 18
|
|
|
|
)
|
|
|
|
const (
|
|
|
|
HOST_LOOKUP_TYPE_HASH = iota
|
|
|
|
HOST_LOOKUP_TYPE_HOST = iota
|
|
|
|
)
|
|
|
|
|
|
|
|
type ClientCallBacks struct {
|
|
|
|
opaque *interface{}
|
2018-03-04 17:52:55 -05:00
|
|
|
onDisconnect func(*Client, string, *interface{})
|
2018-03-04 12:41:44 -05:00
|
|
|
onLog func(*Client, LoggerTags, string)
|
|
|
|
}
|
2018-03-04 17:52:55 -05:00
|
|
|
type LookupEntry struct {
|
|
|
|
address string
|
|
|
|
session Session
|
|
|
|
}
|
|
|
|
type RouterInfo struct {
|
|
|
|
date uint64
|
|
|
|
version string
|
|
|
|
capabilities uint32
|
|
|
|
}
|
|
|
|
|
2018-03-04 12:41:44 -05:00
|
|
|
type Client struct {
|
2018-03-04 17:52:55 -05:00
|
|
|
logger *LoggerCallbacks // TODO idk wat this is for
|
|
|
|
callbacks ClientCallBacks
|
|
|
|
properties [NR_OF_I2CP_CLIENT_PROPERTIES]string
|
|
|
|
tcp *Tcp
|
|
|
|
outputStream *Stream
|
|
|
|
messageStream *Stream
|
|
|
|
router RouterInfo
|
|
|
|
outputQueue *sll.List
|
|
|
|
sessions []*Session
|
|
|
|
n_sessions int
|
|
|
|
lookup map[string]LookupEntry
|
|
|
|
lookupReq map[int]string
|
|
|
|
lock sync.Mutex
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewClient creates a new i2p client with the specified callbacks
|
|
|
|
func NewClient(callbacks ClientCallBacks) (c *Client) {
|
|
|
|
c = new(Client)
|
|
|
|
c.callbacks = callbacks
|
|
|
|
LogInit(nil, ERROR)
|
|
|
|
c.outputStream = bytes.NewBuffer(make([]byte, 0, I2CP_MESSAGE_SIZE))
|
|
|
|
c.messageStream = bytes.NewBuffer(make([]byte, 0, I2CP_MESSAGE_SIZE))
|
|
|
|
c.setDefaultProperties()
|
|
|
|
c.lookup = make(map[string]LookupEntry, 1000)
|
|
|
|
c.lookupReq = make(map[int]string, 1000)
|
|
|
|
c.outputQueue = sll.New()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) setDefaultProperties() {
|
|
|
|
c.properties[CLIENT_PROP_ROUTER_ADDRESS] = "127.0.0.1"
|
|
|
|
c.properties[CLIENT_PROP_ROUTER_PORT] = "7654"
|
|
|
|
c.properties[CLIENT_PROP_ROUTER_PORT] = "7654"
|
|
|
|
// TODO PARSE I2CP config file
|
|
|
|
}
|
|
|
|
func (c *Client) Connect() {
|
|
|
|
Info(0, "Client connecting to i2cp at %s:%s", c.properties[CLIENT_PROP_ROUTER_ADDRESS], c.properties[CLIENT_PROP_ROUTER_PORT]);
|
|
|
|
err := c.tcp.Connect()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
c.outputStream.Reset()
|
|
|
|
c.outputStream.WriteByte(I2CP_PROTOCOL_INIT)
|
|
|
|
_, err = c.tcp.Send(c.outputStream)
|
|
|
|
Debug(PROTOCOL, "Sending protocol byte message")
|
|
|
|
c.messageGetDate(false)
|
|
|
|
c.recvMessage(I2CP_MSG_SET_DATE, c.messageStream, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO Write messageGetDate
|
|
|
|
func (c *Client) messageGetDate(queue bool) {
|
|
|
|
Debug(PROTOCOL, "Sending GetDateMessage")
|
|
|
|
c.messageStream.Reset()
|
|
|
|
c.messageStream.WriteString(I2P_CLIENT_VERSION)
|
|
|
|
/* write new 0.9.10 auth mapping if username property is set */
|
|
|
|
if c.properties[CLIENT_PROP_USERNAME] != "" {
|
|
|
|
auth := NewStream(make([]byte, 0, 512))
|
|
|
|
auth.WriteString("i2cp.password")
|
|
|
|
auth.WriteByte('=')
|
|
|
|
auth.WriteString(c.properties[CLIENT_PROP_PASSWORD])
|
|
|
|
auth.WriteByte(';')
|
|
|
|
auth.WriteString("i2cp.username")
|
|
|
|
auth.WriteByte('=')
|
|
|
|
auth.WriteString(c.properties[CLIENT_PROP_USERNAME])
|
|
|
|
auth.WriteByte(';')
|
|
|
|
c.messageStream.WriteUint16(uint16(auth.Len()))
|
|
|
|
c.messageStream.Write(auth.Bytes())
|
|
|
|
auth.Reset()
|
|
|
|
}
|
|
|
|
if err := c.sendMessage(I2CP_MSG_GET_DATE, c.messageStream, queue);
|
|
|
|
err != nil {
|
|
|
|
Error(0, "%s", "error while sending GetDateMessage.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) sendMessage(typ uint8, stream *Stream, queue bool) (err error) {
|
|
|
|
send := NewStream(make([]byte, stream.Len() + 4 + 1))
|
|
|
|
err = send.WriteUint32(uint32(stream.Len()))
|
|
|
|
err = send.WriteByte(typ)
|
|
|
|
_, err = send.Write(stream.Bytes())
|
|
|
|
if queue {
|
|
|
|
Debug(PROTOCOL, "Putting %d bytes message on the output queue.", send.Len())
|
|
|
|
c.lock.Lock()
|
|
|
|
c.outputQueue.Add(send)
|
|
|
|
c.lock.Unlock()
|
|
|
|
} else {
|
|
|
|
err = c.tcp.Send(send)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) recvMessage(typ uint8, stream *Stream, dispatch bool) (err error) {
|
|
|
|
length := uint32(0)
|
|
|
|
msgType := uint8(0)
|
|
|
|
var i int
|
|
|
|
firstFive := NewStream(make([]byte, 5))
|
|
|
|
i, err = c.tcp.Receive(firstFive)
|
|
|
|
if i ==0 {
|
|
|
|
c.callbacks.onDisconnect(c, "Didn't receive anything", nil)
|
|
|
|
}
|
|
|
|
length, err = firstFive.ReadUint32()
|
|
|
|
msgType, err = firstFive.ReadByte()
|
|
|
|
if (typ == I2CP_MSG_SET_DATE) && (length > 0xffff) {
|
|
|
|
Fatal(PROTOCOL, "Unexpected response, your router is probably configured to use SSL")
|
|
|
|
}
|
|
|
|
if length > 0xffff {
|
|
|
|
Fatal(PROTOCOL, "unexpected message length, length > 0xffff")
|
|
|
|
}
|
|
|
|
if (typ != 0) && (msgType != typ) {
|
|
|
|
Error(PROTOCOL, "expected message type %d, received %d", typ, msgType)
|
|
|
|
}
|
|
|
|
// receive rest
|
|
|
|
stream.ChLen(int(length))
|
|
|
|
i, err = c.tcp.Receive(stream)
|
|
|
|
|
|
|
|
if dispatch {
|
|
|
|
c.onMessage(msgType, stream)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
func (c *Client) onMessage(msgType uint8, stream *Stream) {
|
|
|
|
switch msgType {
|
|
|
|
case I2CP_MSG_SET_DATE: c.onMsgSetDate(stream)
|
|
|
|
case I2CP_MSG_DISCONNECT: c.onMsgDisconnect(stream)
|
|
|
|
case I2CP_MSG_PAYLOAD_MESSAGE: c.onMsgPayload(stream)
|
|
|
|
case I2CP_MSG_MESSAGE_STATUS: c.onMsgStatus(stream)
|
|
|
|
case I2CP_MSG_DEST_REPLY: c.onMsgDestReply(stream)
|
|
|
|
case I2CP_MSG_BANDWIDTH_LIMITS: c.onMsgBandwithLimit(stream)
|
|
|
|
case I2CP_MSG_SESSION_STATUS: c.onMsgSessionStatus(stream)
|
|
|
|
case I2CP_MSG_REQUEST_VARIABLE_LEASESET: c.onMsgReqVariableLease(stream)
|
|
|
|
case I2CP_MSG_HOST_REPLY: c.onMsgHostReply(stream)
|
|
|
|
default: Info(TAG, "%s", "recieved unhandled i2cp message.")
|
|
|
|
}
|
2018-03-04 12:41:44 -05:00
|
|
|
}
|
2018-03-04 17:52:55 -05:00
|
|
|
func (c *Client) onMsgSetDate(stream *Stream) {
|
|
|
|
Debug(TAG|PROTOCOL, "Received SetDate message.")
|
|
|
|
var err error
|
|
|
|
c.router.date, err = stream.ReadUint64()
|
|
|
|
c.router.version, err = stream.R
|
|
|
|
if err != nil {
|
|
|
|
Error(TAG|PROTOCOL, "Could not read SetDate correctly data")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func (c *Client) onMsgDisconnect(stream *Stream) {
|
|
|
|
|
|
|
|
}
|
|
|
|
func (c *Client) onMsgPayload(stream *Stream) {
|
|
|
|
|
|
|
|
}
|
|
|
|
func (c *Client) onMsgStatus(stream *Stream) {
|
|
|
|
|
|
|
|
}
|
|
|
|
func (c *Client) onMsgDestReply(stream *Stream) {
|
|
|
|
|
|
|
|
}
|
|
|
|
func (c *Client) onMsgBandwithLimit(stream *Stream) {
|
|
|
|
|
|
|
|
}
|
|
|
|
func (c *Client) onMsgSessionStatus(stream *Stream) {
|
|
|
|
|
|
|
|
}
|
|
|
|
func (c *Client) onMsgReqVariableLease(stream *Stream) {
|
|
|
|
|
|
|
|
}
|
|
|
|
func (c *Client) onMsgHostReply(stream *Stream) {
|
|
|
|
|
|
|
|
}
|