mirror of
https://github.com/go-i2p/go-i2cp.git
synced 2025-06-08 09:16:20 -04:00
Working on getting sessions functional
This commit is contained in:
227
client.go
227
client.go
@ -1,31 +1,36 @@
|
||||
package go_i2cp
|
||||
|
||||
import (
|
||||
sll "github.com/emirpasic/gods/lists/singlylinkedlist"
|
||||
"net/rpc"
|
||||
"bytes"
|
||||
"sync"
|
||||
)
|
||||
const I2P_CLIENT_VERSION string = "0.9.11"
|
||||
const TAG string = "CLIENT"
|
||||
const I2CP_PROTOCOL_INIT int = 0x2a
|
||||
const TAG = CLIENT
|
||||
const I2CP_PROTOCOL_INIT uint8 = 0x2a
|
||||
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
|
||||
const I2CP_MSG_BANDWIDTH_LIMITS int = 23
|
||||
const I2CP_MSG_CREATE_LEASE_SET int = 4
|
||||
const I2CP_MSG_CREATE_SESSION int = 1
|
||||
const I2CP_MSG_DEST_LOOKUP int = 34
|
||||
const I2CP_MSG_DEST_REPLY int = 35
|
||||
const I2CP_MSG_DESTROY_SESSION int = 3
|
||||
const I2CP_MSG_DISCONNECT int = 30
|
||||
const I2CP_MSG_GET_BANDWIDTH_LIMITS int = 8
|
||||
const I2CP_MSG_GET_DATE int = 32
|
||||
const I2CP_MSG_HOST_LOOKUP int = 38
|
||||
const I2CP_MSG_HOST_REPLY int = 39
|
||||
const I2CP_MSG_MESSAGE_STATUS int = 22
|
||||
const I2CP_MSG_PAYLOAD_MESSAGE int = 31
|
||||
const I2CP_MSG_REQUEST_LEASESET int = 21
|
||||
const I2CP_MSG_REQUEST_VARIABLE_LEASESET int = 37
|
||||
const I2CP_MSG_SEND_MESSAGE int = 5
|
||||
const I2CP_MSG_SESSION_STATUS int = 20
|
||||
const I2CP_MSG_SET_DATE int = 33
|
||||
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
|
||||
|
||||
/* Router capabilities */
|
||||
const ROUTER_CAN_HOST_LOOKUP int = 1
|
||||
@ -50,8 +55,184 @@ const (
|
||||
|
||||
type ClientCallBacks struct {
|
||||
opaque *interface{}
|
||||
onDisconnect *func(*Client, string, *interface{})
|
||||
onDisconnect func(*Client, string, *interface{})
|
||||
onLog func(*Client, LoggerTags, string)
|
||||
}
|
||||
type Client struct {
|
||||
type LookupEntry struct {
|
||||
address string
|
||||
session Session
|
||||
}
|
||||
type RouterInfo struct {
|
||||
date uint64
|
||||
version string
|
||||
capabilities uint32
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
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.")
|
||||
}
|
||||
}
|
||||
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) {
|
||||
|
||||
}
|
14
i2pc.go
Normal file
14
i2pc.go
Normal file
@ -0,0 +1,14 @@
|
||||
package go_i2cp
|
||||
|
||||
func Init() {
|
||||
tcpInit()
|
||||
}
|
||||
|
||||
func Deinit() {
|
||||
tcpDeinit()
|
||||
}
|
||||
|
||||
const (
|
||||
CLIENT uint32 = (iota +1) << 8
|
||||
|
||||
)
|
91
logger.go
Normal file
91
logger.go
Normal file
@ -0,0 +1,91 @@
|
||||
package go_i2cp
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
PROTOCOL = (1 << 0)
|
||||
LOGIC = (1 << 1)
|
||||
|
||||
DEBUG = (1 << 4)
|
||||
INFO = (1 << 5)
|
||||
WARNING = (1 << 6)
|
||||
ERROR = (1 << 7)
|
||||
FATAL = (1 << 8)
|
||||
|
||||
STRINGMAP = (1 << 9)
|
||||
INTMAP = (1 << 10)
|
||||
QUEUE = (1 << 11)
|
||||
STREAM = (1 << 12)
|
||||
CRYPTO = (1 << 13)
|
||||
TCP = (1 << 14)
|
||||
CLIENT = (1 << 15)
|
||||
CERTIFICATE = (1 << 16)
|
||||
LEASE = (1 << 17)
|
||||
DESTINATION = (1 << 18)
|
||||
SESSION = (1 << 19)
|
||||
SESSION_CONFIG = (1 << 20)
|
||||
TEST = (1 << 21)
|
||||
DATAGRAM = (1 << 22)
|
||||
CONFIG_FILE = (1 << 23)
|
||||
VERSION = (1 << 24)
|
||||
|
||||
TAG_MASK = 0x0000000f
|
||||
LEVEL_MASK = 0x000001f0
|
||||
COMPONENT_MASK = 0xfffffe00
|
||||
|
||||
ALL = 0xffffffff
|
||||
)
|
||||
|
||||
type LoggerTags = uint32
|
||||
type LoggerCallbacks struct {
|
||||
opaque *interface{}
|
||||
onLog func(*Logger, LoggerTags, string)
|
||||
}
|
||||
type Logger struct {
|
||||
callbacks *LoggerCallbacks
|
||||
logLevel int
|
||||
}
|
||||
|
||||
var logInstance *Logger
|
||||
|
||||
// TODO filter
|
||||
func LogInit(callbacks *LoggerCallbacks, level int) {
|
||||
logInstance = &Logger{callbacks: callbacks}
|
||||
logInstance.setLogLevel(level)
|
||||
}
|
||||
func Debug(tags LoggerTags, message string, args ...interface{}) {
|
||||
logInstance.log(tags|DEBUG, message, args...)
|
||||
}
|
||||
func Info(tags LoggerTags, message string, args ...interface{}) {
|
||||
logInstance.log(tags|INFO, message, args...)
|
||||
}
|
||||
func Warning(tags LoggerTags, message string, args ...interface{}) {
|
||||
logInstance.log(tags|WARNING, message, args...)
|
||||
}
|
||||
func Error(tags LoggerTags, message string, args ...interface{}) {
|
||||
logInstance.log(tags|ERROR, message, args...)
|
||||
}
|
||||
func Fatal(tags LoggerTags, message string, args ...interface{}) {
|
||||
logInstance.log(tags|FATAL, message, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) log(tags LoggerTags, format string, args ...interface{}) {
|
||||
if l.callbacks == nil {
|
||||
fmt.Printf(format, args)
|
||||
} else {
|
||||
l.callbacks.onLog(l, tags, fmt.Sprintf(format, args))
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) setLogLevel(level int) {
|
||||
switch level {
|
||||
case DEBUG:
|
||||
case INFO:
|
||||
case WARNING:
|
||||
case ERROR:
|
||||
case FATAL:
|
||||
l.logLevel = level
|
||||
default:
|
||||
l.logLevel = ERROR
|
||||
}
|
||||
}
|
116
stream.go
Normal file
116
stream.go
Normal file
@ -0,0 +1,116 @@
|
||||
package go_i2cp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"encoding/binary"
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Stream = bytes.Buffer
|
||||
func NewStream(buf []byte) *Stream {
|
||||
return bytes.NewBuffer(buf)
|
||||
}
|
||||
func (s *Stream) ReadUint16() (r uint16, err error) {
|
||||
bts := make([]byte, 2)
|
||||
_, err = s.Read(bts)
|
||||
r = binary.LittleEndian.Uint16(bts)
|
||||
return
|
||||
}
|
||||
func (s *Stream) ReadUint32() (r uint32, err error) {
|
||||
bts := make([]byte, 4)
|
||||
_, err = s.Read(bts)
|
||||
r = binary.LittleEndian.Uint32(bts)
|
||||
return
|
||||
}
|
||||
func (s *Stream) ReadUint64() (r uint64, err error) {
|
||||
bts := make([]byte, 8)
|
||||
_, err = s.Read(bts)
|
||||
r = binary.LittleEndian.Uint64(bts)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Stream) WriteUint16(i uint16) (err error) {
|
||||
bts := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(bts, i)
|
||||
_, err = s.Write(bts)
|
||||
return
|
||||
}
|
||||
func (s *Stream) WriteUint32(i uint32) (err error) {
|
||||
bts := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bts, i)
|
||||
_, err = s.Write(bts)
|
||||
return
|
||||
}
|
||||
func (s *Stream) WriteUint64(i uint64) (err error) {
|
||||
bts := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bts, i)
|
||||
_, err = s.Write(bts)
|
||||
return
|
||||
}
|
||||
func (s *Stream) loadFile(f os.File) (err error) {
|
||||
_, err = f.Read(s.Bytes())
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Stream) ChLen(len int) {
|
||||
byt := s.Bytes()
|
||||
byt = byt[:len]
|
||||
}
|
||||
|
||||
/*type Stream struct {
|
||||
data []byte
|
||||
size uint32
|
||||
p uint32
|
||||
end uint32
|
||||
}
|
||||
|
||||
func (s *Stream) Init(len uint32) {
|
||||
data := make([]byte, len)
|
||||
s.data = data
|
||||
s.size = len
|
||||
s.Reset()
|
||||
}
|
||||
|
||||
func (s *Stream) Reset() {
|
||||
s.end = s.size - 1
|
||||
s.p = 0
|
||||
}
|
||||
|
||||
func (s *Stream) Seek(a uint32) {
|
||||
s.p = a
|
||||
}
|
||||
func (s *Stream) Advance() {
|
||||
s.p += 1
|
||||
}
|
||||
func (s *Stream) Tell() uint32 { return s.p }
|
||||
func (s *Stream) MarkEnd() { s.end = s.p}
|
||||
func (s *Stream) Eof() bool { return s.end < s.p}
|
||||
func (s *Stream) Debug() {fmt.Printf("STREAM: data %p size %d p %d end %d", s.data, s.size, s.p, s.end)}
|
||||
func (s *Stream) Check(len uint32) {
|
||||
if (s.p + len) > s.size {
|
||||
s.Debug()
|
||||
// TODO better error message
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
func (s *Stream) Dump(file os.File) {
|
||||
file.Write(s.data)
|
||||
defer file.Close()
|
||||
}
|
||||
func (s *Stream) Skip(n uint32) {
|
||||
s.Check(n)
|
||||
s.p += n
|
||||
}
|
||||
func (s *Stream) ReadUint8() uint8 {
|
||||
s.Check(1)
|
||||
defer s.Advance()
|
||||
return s.data[s.p]
|
||||
}
|
||||
func (s *Stream) ReadUint8p(len uint32) []uint8 {
|
||||
s.Check(len)
|
||||
defer s.Skip(len)
|
||||
return s.data[s.p:len]
|
||||
}
|
||||
*/
|
35
tcp.go
Normal file
35
tcp.go
Normal file
@ -0,0 +1,35 @@
|
||||
package go_i2cp
|
||||
|
||||
import "net"
|
||||
|
||||
func tcpInit() {
|
||||
address = net.TCPAddr{
|
||||
IP: net.ParseIP("127.0.0.1"),
|
||||
Port: 7654,
|
||||
}
|
||||
}
|
||||
|
||||
func tcpDeinit() {
|
||||
|
||||
}
|
||||
|
||||
var address net.TCPAddr
|
||||
var conn *net.TCPConn
|
||||
|
||||
func (tcp *Tcp) Connect() (err error) {
|
||||
conn, err = net.DialTCP("tcp", nil, &address )
|
||||
return
|
||||
}
|
||||
|
||||
func (tcp *Tcp) Send(buf *Stream) (i int, err error) {
|
||||
i, err = conn.Write(buf.Bytes())
|
||||
return
|
||||
}
|
||||
|
||||
func (tcp *Tcp) Receive(buf *Stream) (i int, err error) {
|
||||
i, err = conn.Read(buf.Bytes())
|
||||
}
|
||||
|
||||
type Tcp struct {
|
||||
|
||||
}
|
Reference in New Issue
Block a user