Finished sessions, only datagrams left

This commit is contained in:
Willie Koomson
2018-03-11 11:52:57 -04:00
parent c1aab76710
commit d9ca45d983
9 changed files with 406 additions and 51 deletions

View File

@ -6,6 +6,7 @@ import (
"compress/zlib"
"encoding/binary"
"io"
"os"
"strings"
"sync"
)
@ -94,6 +95,8 @@ type Client struct {
lookupRequestId uint32
}
var defaultConfigFile = "/.i2cp.conf"
// NewClient creates a new i2p client with the specified callbacks
func NewClient(callbacks ClientCallBacks) (c *Client) {
c = new(Client)
@ -112,10 +115,28 @@ func NewClient(callbacks ClientCallBacks) (c *Client) {
func (c *Client) setDefaultProperties() {
c.properties[CLIENT_PROP_ROUTER_ADDRESS] = "127.0.0.1"
c.properties[CLIENT_PROP_ROUTER_PORT] = "7654"
// TODO PARSE I2CP config file
home := os.Getenv("HOME")
if len(home) == 0 {
return
}
config := home + defaultConfigFile
Debug(CLIENT, "Loading config file %s", config)
ParseConfig(config, func(name, value string) {
if prop := c.propFromString(name); prop >= 0 {
c.SetProperty(prop, value)
}
})
}
func (c *Client) propFromString(name string) ClientProperty {
for i := 0; ClientProperty(i) < NR_OF_I2CP_CLIENT_PROPERTIES; i++ {
if sessionOptions[i] == name {
return ClientProperty(i)
}
}
return ClientProperty(-1)
}
// TODO Write messageGetDate
func (c *Client) messageGetDate(queue bool) {
Debug(PROTOCOL, "Sending GetDateMessage")
c.messageStream.Reset()
@ -576,12 +597,13 @@ func (c *Client) ProcessIO() error {
}
}
c.lock.Unlock()
var err error
for c.tcp.CanRead() {
if err := c.recvMessage(I2CP_MSG_ANY, c.messageStream, true); err != nil {
if err = c.recvMessage(I2CP_MSG_ANY, c.messageStream, true); err != nil {
return err
}
}
return nil
return err
}
func (c *Client) DestinationLookup(session *Session, address string) (requestId uint32) {

View File

@ -1,14 +1,12 @@
package go_i2cp
import (
"crypto"
"crypto/dsa"
"crypto/rand"
"crypto/sha1"
"crypto/sha256"
"encoding/base32"
"encoding/base64"
"fmt"
"hash"
"io"
"math/big"

View File

@ -1,9 +1,7 @@
package go_i2cp
import (
"crypto"
"errors"
"github.com/cryptix/go/crypt"
"math/big"
"os"
"strings"
@ -86,7 +84,7 @@ func NewDestinationFromBase64(base64 string) (dest Destination, err error) {
return NewDestinationFromMessage(&decoded)
}
func NewDestinationFromFile(file os.File) (dest Destination, err error) {
func NewDestinationFromFile(file *os.File) (dest Destination, err error) {
var stream Stream
stream.loadFile(file)
return NewDestinationFromStream(&stream)

12
i2pc.go
View File

@ -1,13 +1 @@
package go_i2cp
func Init() {
tcpInit()
}
func Deinit() {
tcpDeinit()
}
const (
CLIENT uint32 = (iota + 1) << 8
)

22
lease.go Normal file
View File

@ -0,0 +1,22 @@
package go_i2cp
type Lease struct {
tunnelGateway [32]byte // sha256 of the RouterIdentity of the tunnel gateway
tunnelId uint32
endDate uint64
}
func NewLeaseFromStream(stream *Stream) (l *Lease, err error) {
l = &Lease{}
stream.Read(l.tunnelGateway[:])
l.tunnelId, err = stream.ReadUint32()
l.endDate, err = stream.ReadUint64()
return
}
func (l *Lease) WriteToMessage(stream *Stream) (err error) {
_, err = stream.Write(l.tunnelGateway[:])
err = stream.WriteUint32(l.tunnelId)
err = stream.WriteUint64(l.endDate)
return
}

View File

@ -1,10 +1,93 @@
package go_i2cp
import "bytes"
type SessionMessageStatus int
const (
I2CP_MSG_STATUS_AVAILABLE SessionMessageStatus = iota
I2CP_MSG_STATUS_ACCEPTED
I2CP_MSG_STATUS_BEST_EFFORT_SUCCESS
I2CP_MSG_STATUS_BEST_EFFORT_FAILURE
I2CP_MSG_STATUS_GUARANTEED_SUCCESS
I2CP_MSG_STATUS_GUARANTEED_FAILURE
I2CP_MSG_STATUS_LOCAL_SUCCESS
I2CP_MSG_STATUS_LOCAL_FAILURE
I2CP_MSG_STATUS_ROUTER_FAILURE
I2CP_MSG_STATUS_NETWORK_FAILURE
I2CP_MSG_STATUS_BAD_SESSION
I2CP_MSG_STATUS_BAD_MESSAGE
I2CP_MSG_STATUS_OVERFLOW_FAILURE
I2CP_MSG_STATUS_MESSAGE_EXPIRED
I2CP_MSG_STATUS_MESSAGE_BAD_LOCAL_LEASESET
I2CP_MSG_STATUS_MESSAGE_NO_LOCAL_TUNNELS
I2CP_MSG_STATUS_MESSAGE_UNSUPPORTED_ENCRYPTION
I2CP_MSG_STATUS_MESSAGE_BAD_DESTINATION
I2CP_MSG_STATUS_MESSAGE_BAD_LEASESET
I2CP_MSG_STATUS_MESSAGE_EXPIRED_LEASESET
I2CP_MSG_STATUS_MESSAGE_NO_LEASESET
)
type SessionStatus int
const (
I2CP_SESSION_STATUS_DESTROYED SessionStatus = iota
I2CP_SESSION_STATUS_CREATED
I2CP_SESSION_STATUS_UPDATED
I2CP_SESSION_STATUS_INVALID
)
type SessionCallbacks struct {
onMessage func(session *Session, protocol uint8, srcPort, destPort uint16, payload *Stream)
onStatus func(session *Session, status SessionStatus)
onDestination func(session *Session, requestId uint32, address string, dest *Destination)
}
type Session struct {
id uint16
config *SessionConfig
client *Client
callbacks *SessionCallbacks
}
func (session Session) dispatchMessage(protocol uint8, srcPort, destPort uint16, payload *Stream) {
func NewSession(client *Client, callbacks SessionCallbacks, destFilename string) (sess *Session) {
sess = &Session{}
sess.client = client
sess.config = &SessionConfig{}
sess.callbacks = &callbacks
return
}
func (session *Session) SendMessage(destination *Destination, protocol uint8, srcPort, destPort uint16, payload *Stream, nonce uint32) {
session.client.msgSendMessage(session, destination, protocol, srcPort, destPort, payload, nonce, true)
}
func (session *Session) Destination() *Destination {
return session.config.destination
}
func (session *Session) dispatchMessage(protocol uint8, srcPort, destPort uint16, payload *Stream) {
if session.callbacks == nil || session.callbacks.onMessage == nil {
return
}
session.callbacks.onMessage(session, protocol, srcPort, destPort, payload)
}
func (session *Session) dispatchDestination(requestId uint32, address string, destination *Destination) {
if session.callbacks == nil || session.callbacks.onDestination == nil {
return
}
session.callbacks.onDestination(session, requestId, address, destination)
}
func (session *Session) dispatchStatus(status SessionStatus) {
switch status {
case I2CP_SESSION_STATUS_CREATED:
Debug(SESSION, "Session %p is created", session)
case I2CP_SESSION_STATUS_DESTROYED:
Debug(SESSION, "Session %p is destroyed", session)
case I2CP_SESSION_STATUS_UPDATED:
Debug(SESSION, "Session %p is updated", session)
case I2CP_SESSION_STATUS_INVALID:
Debug(SESSION, "Session %p is invalid", session)
}
if session.callbacks == nil || session.callbacks.onStatus == nil {
return
}
session.callbacks.onStatus(session, status)
}

166
session_config.go Normal file
View File

@ -0,0 +1,166 @@
package go_i2cp
import (
"bufio"
"os"
"regexp"
"time"
)
type SessionConfigProperty int
const (
SESSION_CONFIG_PROP_CRYPTO_LOW_TAG_THRESHOLD SessionConfigProperty = iota
SESSION_CONFIG_PROP_CRYPTO_TAGS_TO_SEND
SESSION_CONFIG_PROP_I2CP_DONT_PUBLISH_LEASE_SET
SESSION_CONFIG_PROP_I2CP_FAST_RECEIVE
SESSION_CONFIG_PROP_I2CP_GZIP
SESSION_CONFIG_PROP_I2CP_MESSAGE_RELIABILITY
SESSION_CONFIG_PROP_I2CP_PASSWORD
SESSION_CONFIG_PROP_I2CP_USERNAME
SESSION_CONFIG_PROP_INBOUND_ALLOW_ZERO_HOP
SESSION_CONFIG_PROP_INBOUND_BACKUP_QUANTITY
SESSION_CONFIG_PROP_INBOUND_IP_RESTRICTION
SESSION_CONFIG_PROP_INBOUND_LENGTH
SESSION_CONFIG_PROP_INBOUND_LENGTH_VARIANCE
SESSION_CONFIG_PROP_INBOUND_NICKNAME
SESSION_CONFIG_PROP_INBOUND_QUANTITY
SESSION_CONFIG_PROP_OUTBOUND_ALLOW_ZERO_HOP
SESSION_CONFIG_PROP_OUTBOUND_BACKUP_QUANTITY
SESSION_CONFIG_PROP_OUTBOUND_IP_RESTRICTION
SESSION_CONFIG_PROP_OUTBOUND_LENGTH
SESSION_CONFIG_PROP_OUTBOUND_LENGTH_VARIANCE
SESSION_CONFIG_PROP_OUTBOUND_NICKNAME
SESSION_CONFIG_PROP_OUTBOUND_PRIORITY
SESSION_CONFIG_PROP_OUTBOUND_QUANTITY
NR_OF_SESSION_CONFIG_PROPERTIES
)
var sessionOptions = [NR_OF_SESSION_CONFIG_PROPERTIES]string{
"crypto.lowTagThreshold",
"crypto.tagsToSend",
"i2cp.dontPublishLeaseSet",
"i2cp.fastReceive",
"i2cp.gzip",
"i2cp.messageReliability",
"i2cp.password",
"i2cp.username",
"inbound.allowZeroHop",
"inbound.backupQuantity",
"inbound.IPRestriction",
"inbound.length",
"inbound.lengthVariance",
"inbound.nickname",
"inbound.quantity",
"outbound.allowZeroHop",
"outbound.backupQuantity",
"outbound.IPRestriction",
"outbound.length",
"outbound.lengthVariance",
"outbound.nickname",
"outbound.priority",
"outbound.quantity",
}
var configRegex = regexp.MustCompile("\\s*([\\w.]+)=\\s*(.+)\\s*;\\s*")
type SessionConfig struct {
properties [NR_OF_SESSION_CONFIG_PROPERTIES]string
date uint64
destination *Destination
}
func NewSessionConfigFromDestinationFile(filename string) (config SessionConfig) {
var home string
if file, err := os.Open(filename); err == nil {
dest, err := NewDestinationFromFile(file)
config.destination = &dest
if err != nil {
Warning(SESSION_CONFIG, "Failed to load destination from file '%s', a new destination will be generated.", filename)
}
}
if config.destination == nil {
dest, err := NewDestination()
config.destination = &dest
}
if len(filename) > 0 {
config.destination.WriteToFile(filename)
}
home = os.Getenv("HOME")
if len(home) > 0 {
configFile := home + "/.i2cp.conf"
ParseConfig(configFile, func(name, value string) {
if prop := config.propFromString(name); prop >= 0 {
config.SetProperty(prop, value)
}
})
}
return config
}
func (config *SessionConfig) writeToMessage(stream *Stream) {
config.destination.WriteToMessage(stream)
config.writeMappingToMessage(stream)
stream.WriteUint64(uint64(time.Now().Unix()))
GetCryptoInstance().WriteSignatureToStream(&config.destination.sgk, stream)
}
func (config *SessionConfig) writeMappingToMessage(stream *Stream) (err error) {
is := NewStream(make([]byte, 0xffff))
count := 0
for i := 0; i < int(NR_OF_SESSION_CONFIG_PROPERTIES); i++ {
var option string
if sc.properties[i] == "" {
continue
}
option = sc.configOptLookup(SessionConfigProperty(i))
if option == "" {
continue
}
is.Write([]byte(option + "=" + sc.properties[i] + ";"))
count++
}
Debug(SESSION_CONFIG, "Writing %d options to mapping table", count)
err = stream.WriteUint16(uint16(is.Len()))
if is.Len() > 0 {
_, err = stream.Write(is.Bytes())
}
return
}
func (config *SessionConfig) configOptLookup(property SessionConfigProperty) string {
return sessionOptions[property]
}
func (config *SessionConfig) propFromString(name string) SessionConfigProperty {
for i := 0; SessionConfigProperty(i) < NR_OF_SESSION_CONFIG_PROPERTIES; i++ {
if sessionOptions[i] == name {
return SessionConfigProperty(i)
}
}
return SessionConfigProperty(-1)
}
func (config *SessionConfig) SetProperty(prop SessionConfigProperty, value string) {
config.properties[prop] = value
}
func ParseConfig(s string, cb func(string, string)) {
file, err := os.Open(s)
if err != nil {
Error(SESSION_CONFIG, err.Error())
return
}
Debug(SESSION_CONFIG, "Parsing config file '%s'", s)
scan := bufio.NewScanner(file)
for scan.Scan() {
line := scan.Text()
groups := configRegex.FindStringSubmatch(line)
if len(groups) != 3 {
continue
}
cb(groups[1], groups[2])
}
if err := scan.Err(); err != nil {
Error(SESSION_CONFIG, "reading input from %s config %s", s, err.Error())
}
}

View File

@ -1,32 +1,31 @@
package go_i2cp
import (
"fmt"
"os"
"encoding/binary"
"bytes"
"io"
"encoding/binary"
"os"
)
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)
_, 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)
_, 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)
_, err = s.Read(bts)
r = binary.LittleEndian.Uint64(bts)
return
}
@ -49,7 +48,7 @@ func (s *Stream) WriteUint64(i uint64) (err error) {
_, err = s.Write(bts)
return
}
func (s *Stream) loadFile(f os.File) (err error) {
func (s *Stream) loadFile(f *os.File) (err error) {
_, err = f.Read(s.Bytes())
return
}
@ -113,4 +112,4 @@ func (s *Stream) ReadUint8p(len uint32) []uint8 {
defer s.Skip(len)
return s.data[s.p:len]
}
*/
*/

115
tcp.go
View File

@ -1,35 +1,114 @@
package go_i2cp
import "net"
import (
"crypto/tls"
"crypto/x509"
"io"
"net"
"time"
)
func tcpInit() {
address = net.TCPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: 7654,
}
type TcpProperty int
const (
TCP_PROP_ADDRESS TcpProperty = iota
TCP_PROP_PORT
TCP_PROP_USE_TLS
TCP_PROP_TLS_CLIENT_CERTIFICATE
NR_OF_TCP_PROPERTIES
)
var CAFile = "/etc/ssl/certs/ca-certificates.crt"
var defaultRouterAddress = "127.0.0.1:7654"
const USE_TLS = true
func (tcp *Tcp) Init() (err error) {
tcp.address, err = net.ResolveTCPAddr("tcp", defaultRouterAddress)
return
}
func tcpDeinit() {
}
var address net.TCPAddr
var conn *net.TCPConn
func (tcp *Tcp) Connect() (err error) {
conn, err = net.DialTCP("tcp", nil, &address )
if USE_TLS {
roots, err := x509.SystemCertPool()
tcp.tlsConn, err = tls.Dial("tcp", tcp.address.String(), &tls.Config{RootCAs: roots})
err = tcp.tlsConn.Handshake()
} else {
tcp.conn, err = net.DialTCP("tcp", nil, tcp.address)
if err == nil {
err = tcp.conn.SetKeepAlive(true)
}
}
return
}
func (tcp *Tcp) Send(buf *Stream) (i int, err error) {
i, err = conn.Write(buf.Bytes())
if USE_TLS {
i, err = tcp.tlsConn.Write(buf.Bytes())
} else {
i, err = tcp.conn.Write(buf.Bytes())
}
return
}
func (tcp *Tcp) Receive(buf *Stream) (i int, err error) {
i, err = conn.Read(buf.Bytes())
if USE_TLS {
i, err = tcp.tlsConn.Read(buf.Bytes())
} else {
i, err = tcp.conn.Read(buf.Bytes())
}
return
}
func (tcp *Tcp) CanRead() bool {
var one []byte
if USE_TLS {
tcp.tlsConn.SetReadDeadline(time.Now())
if _, err := tcp.tlsConn.Read(one); err == io.EOF {
Debug(TCP, "%s detected closed LAN connection", tcp.address.String())
defer tcp.Disconnect()
return false
} else {
var zero time.Time
tcp.tlsConn.SetReadDeadline(zero)
return tcp.tlsConn.ConnectionState().HandshakeComplete
}
} else {
tcp.conn.SetReadDeadline(time.Now())
if _, err := tcp.conn.Read(one); err == io.EOF {
Debug(TCP, "%s detected closed LAN connection", tcp.address.String())
defer tcp.Disconnect()
return false
} else {
var zero time.Time
tcp.conn.SetReadDeadline(zero)
return true
}
}
}
func (tcp *Tcp) Disconnect() {
if USE_TLS {
tcp.tlsConn.Close()
} else {
tcp.conn.Close()
}
}
func (tcp *Tcp) IsConnected() bool {
return tcp.CanRead()
}
func (tcp *Tcp) SetProperty(property TcpProperty, value string) {
tcp.properties[property] = value
}
func (tcp *Tcp) GetProperty(property TcpProperty) string {
return tcp.properties[property]
}
type Tcp struct {
}
address *net.TCPAddr
conn *net.TCPConn
tlsConn *tls.Conn
properties [NR_OF_TCP_PROPERTIES]string
}