From 82596d0be2c81ba2ac701db25c7e5e5a46f93cb7 Mon Sep 17 00:00:00 2001 From: Willie Koomson Date: Mon, 12 Mar 2018 19:46:44 -0400 Subject: [PATCH] Starting to add tests --- .idea/go.imports.xml | 9 +++ client.go | 163 +++++++++++++++++-------------------------- client_test.go | 22 ++++++ destination.go | 15 ++-- destination_test.go | 2 +- logger.go | 2 +- session.go | 5 +- session_config.go | 22 ++---- session_test.go | 1 + stream.go | 41 +++++++++-- tcp.go | 2 +- 11 files changed, 153 insertions(+), 131 deletions(-) create mode 100644 .idea/go.imports.xml create mode 100644 session_test.go diff --git a/.idea/go.imports.xml b/.idea/go.imports.xml new file mode 100644 index 0000000..b653761 --- /dev/null +++ b/.idea/go.imports.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/client.go b/client.go index 8a6a8ba..7d7c299 100644 --- a/client.go +++ b/client.go @@ -11,7 +11,7 @@ import ( "sync" ) -const I2CP_CLIENT_VERSION = "0.9.11" +const I2CP_CLIENT_VERSION = "0.9.33" const TAG = CLIENT const I2CP_PROTOCOL_INIT uint8 = 0x2a const I2CP_MESSAGE_SIZE = 0xffff @@ -61,6 +61,26 @@ const ( HOST_LOOKUP_TYPE_HOST = iota ) +var defaultProperties = map[string]string{ + "i2cp.password": "", + "i2cp.username": "", + "i2cp.closeIdleTime": "", + "i2cp.closeOnIdle": "", + "i2cp.encryptLeaseSet": "", + "i2cp.fastReceive": "", + "i2cp.gzip": "", + "i2cp.leaseSetKey": "", + "i2cp.leaseSetPrivateKey": "", + "i2cp.leaseSetSigningPrivateKey": "", + "i2cp.messageReliability": "", + "i2cp.reduceIdleTime": "", + "i2cp.reduceOnIdle": "", + "i2cp.reduceQuantity": "", + "i2cp.SSL": "", + "i2cp.tcp.host": "127.0.0.1", + "i2cp.tcp.port": "7654", +} + type ClientCallBacks struct { opaque *interface{} onDisconnect func(*Client, string, *interface{}) @@ -79,7 +99,7 @@ type RouterInfo struct { type Client struct { logger *LoggerCallbacks // TODO idk wat this is for callbacks *ClientCallBacks - properties [NR_OF_I2CP_CLIENT_PROPERTIES]string + properties map[string]string tcp Tcp outputStream *Stream messageStream *Stream @@ -114,59 +134,25 @@ 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" + c.properties = defaultProperties 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) -} - -func (c *Client) messageGetDate(queue bool) { - Debug(PROTOCOL, "Sending GetDateMessage") - c.messageStream.Reset() - c.messageStream.WriteString(I2CP_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.") - } + ParseConfig(config, c.SetProperty) } func (c *Client) sendMessage(typ uint8, stream *Stream, queue bool) (err error) { - send := NewStream(make([]byte, stream.Len()+4+1)) + send := NewStream(make([]byte, 0, stream.Len()+4+1)) err = send.WriteUint32(uint32(stream.Len())) err = send.WriteByte(typ) + lenb := stream.Len() + _ = lenb _, err = send.Write(stream.Bytes()) + lenc := send.Len() + _ = lenc if queue { Debug(PROTOCOL, "Putting %d bytes message on the output queue.", send.Len()) c.lock.Lock() @@ -190,16 +176,15 @@ func (c *Client) recvMessage(typ uint8, stream *Stream, dispatch bool) (err erro 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") + Fatal(PROTOCOL, "Unexpected response, check that your router SSL settings match the ~/.i2cp.conf configuration") } - if length > 0xffff { + if length > 0xffff && typ != I2CP_MSG_DISCONNECT { 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 { @@ -241,7 +226,7 @@ func (c *Client) onMsgSetDate(stream *Stream) { version := make([]byte, verLength) _, err = stream.Read(version) c.router.version = parseVersion(string(version)) - Debug(TAG|PROTOCOL, "Router version %s, date %ld", string(version), c.router.date) + Debug(TAG|PROTOCOL, "Router version %s, date %d", string(version), c.router.date) if err != nil { Error(TAG|PROTOCOL, "Could not read SetDate correctly data") } @@ -250,17 +235,21 @@ func (c *Client) onMsgSetDate(stream *Stream) { } } func (c *Client) onMsgDisconnect(stream *Stream) { - var size uint8 var err error Debug(TAG|PROTOCOL, "Received Disconnect message") - size, err = stream.ReadByte() - strbuf := make([]byte, size) + //size, err = stream.ReadByte() + strbuf := make([]byte, stream.Len()) + lens := stream.Len() + _ = lens _, err = stream.Read(strbuf) + Debug(TAG|PROTOCOL, "Received Disconnect message with reason %s", string(strbuf)) if err != nil { Error(TAG|PROTOCOL, "Could not read msgDisconnect correctly data") } - c.callbacks.onDisconnect(c, string(strbuf), nil) + if c.callbacks != nil && c.callbacks.onDisconnect != nil { + c.callbacks.onDisconnect(c, string(strbuf), nil) + } } func (c *Client) onMsgPayload(stream *Stream) { var gzipHeader = [3]byte{0x1f, 0x8b, 0x08} @@ -321,7 +310,7 @@ func (c *Client) onMsgStatus(stream *Stream) { } func (c *Client) onMsgDestReply(stream *Stream) { var b32 string - var destination Destination + var destination *Destination var lup LookupEntry var err error var requestId uint32 @@ -344,7 +333,7 @@ func (c *Client) onMsgDestReply(stream *Stream) { if lup == (LookupEntry{}) { Warning(TAG, "No sesssion for destination lookup of address '%s'", b32) } else { - lup.session.dispatchDestination(requestId, b32, &destination) + lup.session.dispatchDestination(requestId, b32, destination) } } func (c *Client) onMsgBandwithLimit(stream *Stream) { @@ -408,11 +397,10 @@ func (c *Client) onMsgHostReply(stream *Stream) { requestId, err = stream.ReadUint32() result, err = stream.ReadByte() if result == 0 { - dst, err := NewDestinationFromMessage(stream) + dest, err = NewDestinationFromMessage(stream) if err != nil { Fatal(TAG|FATAL, "Failed to construct destination from stream.") } - dest = &dst } sess = c.sessions[sessionId] if sess == nil { @@ -424,22 +412,6 @@ func (c *Client) onMsgHostReply(stream *Stream) { _ = err // currently unused } -func (c *Client) configFileParseCallback(name, value string) { - switch name { - case "i2cp.tcp.host": - c.properties[CLIENT_PROP_ROUTER_ADDRESS] = value - case "i2cp.tcp.port": - c.properties[CLIENT_PROP_ROUTER_PORT] = value - case "i2cp.tcp.SSL": - c.properties[CLIENT_PROP_ROUTER_USE_TLS] = value - case "i2cp.tcp.username": - c.properties[CLIENT_PROP_USERNAME] = value - case "i2cp.tcp.password": - c.properties[CLIENT_PROP_PASSWORD] = value - default: - break - } -} func (c *Client) msgCreateLeaseSet(session *Session, tunnels uint8, leases []*Lease, queue bool) { var err error var nullbytes [256]byte @@ -476,22 +448,17 @@ func (c *Client) msgCreateLeaseSet(session *Session, tunnels uint8, leases []*Le } func (c *Client) msgGetDate(queue bool) { var err error - var auth *Stream Debug(TAG|PROTOCOL, "Sending GetDateMessage") c.messageStream.Reset() - c.messageStream.Write([]byte(I2CP_CLIENT_VERSION)) - if len(c.properties[CLIENT_PROP_USERNAME]) > 0 { - auth = NewStream(make([]byte, 0, 512)) - auth.Write([]byte("i2cp.password=")) - auth.Write([]byte(c.properties[CLIENT_PROP_PASSWORD])) - auth.Write([]byte(";")) - auth.Write([]byte("i2cp.username=")) - auth.Write([]byte(c.properties[CLIENT_PROP_USERNAME])) - auth.Write([]byte(";")) - c.messageStream.WriteUint16(uint16(auth.Len())) - c.messageStream.Write(auth.Bytes()) - } - if err = c.sendMessage(I2CP_MSG_SET_DATE, c.messageStream, queue); err != nil { + c.messageStream.WriteLenPrefixedString(I2CP_CLIENT_VERSION) + if len(c.properties["i2cp.username"]) > 0 { + authInfo := map[string]string{ + "i2cp.username": c.properties["i2cp.username"], + "i2cp.password": c.properties["i2cp.password"], + } + c.messageStream.WriteMapping(authInfo) + } + if err = c.sendMessage(I2CP_MSG_GET_DATE, c.messageStream, queue); err != nil { Error(TAG, "Error while sending GetDateMessage") } } @@ -564,7 +531,7 @@ func (c *Client) msgSendMessage(sess *Session, dest *Destination, protocol uint8 } } 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]) + Info(0, "Client connecting to i2cp at %s:%s", c.properties["i2cp.tcp.host"], c.properties["i2cp.tcp.host"]) err := c.tcp.Connect() if err != nil { panic(err) @@ -654,22 +621,20 @@ func (c *Client) Disconnect() { c.tcp.Disconnect() } -func (c *Client) SetProperty(property ClientProperty, value string) { - c.properties[property] = value - switch property { - case CLIENT_PROP_ROUTER_ADDRESS: - c.tcp.SetProperty(TCP_PROP_ADDRESS, c.properties[CLIENT_PROP_ROUTER_ADDRESS]) - case CLIENT_PROP_ROUTER_PORT: - c.tcp.SetProperty(TCP_PROP_PORT, c.properties[CLIENT_PROP_ROUTER_PORT]) - case CLIENT_PROP_ROUTER_USE_TLS: - c.tcp.SetProperty(TCP_PROP_USE_TLS, c.properties[CLIENT_PROP_ROUTER_USE_TLS]) +func (c *Client) SetProperty(name, value string) { + if _, ok := c.properties[name]; ok { + c.properties[name] = value + switch name { + case "i2cp.tcp.host": + c.tcp.SetProperty(TCP_PROP_ADDRESS, c.properties[name]) + case "i2cp.tcp.port": + c.tcp.SetProperty(TCP_PROP_PORT, c.properties[name]) + case "i2cp.SSL": + c.tcp.SetProperty(TCP_PROP_USE_TLS, c.properties[name]) + } } } -func (c *Client) GetProperty(property ClientProperty) string { - return c.properties[property] -} - func (c *Client) IsConnected() bool { return c.tcp.IsConnected() } diff --git a/client_test.go b/client_test.go index 5d0e934..d667fae 100644 --- a/client_test.go +++ b/client_test.go @@ -9,3 +9,25 @@ func TestClient(t *testing.T) { client.Connect() client.Disconnect() } + +func TestClient_CreateSession(t *testing.T) { + client := NewClient(nil) + client.Connect() + session := NewSession(client, SessionCallbacks{ + onDestination: func(session *Session, requestId uint32, address string, dest *Destination) { + + }, + onStatus: func(session *Session, status SessionStatus) { + + }, + onMessage: func(session *Session, protocol uint8, srcPort, destPort uint16, payload *Stream) { + + }, + }) + session.config.SetProperty(SESSION_CONFIG_PROP_I2CP_FAST_RECEIVE, "true") + session.config.SetProperty(SESSION_CONFIG_PROP_OUTBOUND_NICKNAME, "test-i2cp") + session.config.SetProperty(SESSION_CONFIG_PROP_OUTBOUND_QUANTITY, "4") + session.config.destination, _ = NewDestinationFromBase64("r2zbc34IQSzOIF4N0enKf0xXkJKgsj9yTGGspRnstKZf~4UoAljZOW5aFZywGo-NlaXwt~tIyj4NC0Til0vl1D5N9ip7OMYUCajNNgiXEH~FN33yl-AcJbeTlB-FychSmVfYciTQj6yd19~6wICwkdpy6AYo90bAejSVGpvtFeP5P2pnSwPmcB8m79wyq~C2XjQCe5UcBxnfYolWKgr3uDFrgbhqBVCCkO7zTiARwOWZLVOvZsvKZR4WvYAmQI6CQaxnmT5n1FKO6NBb-HOxVw4onERq86Sc6EQ5d48719Yk-73wq1Mxmr7Y2UwmL~FCnY33rT1FJY2KzUENICL1uEuiVmr9N924CT9RbtldOUUcXmM1gaHlPS40-Hz4AvPxFXHynbyySktN3hBLPwfwhyIQw95ezSNuiBB0xPcujazCw02103n2CO-59rMDmWpttLjpLMggP9IwsAPa9FVLnBqfuCn3NrC4fia50RDwfR41AD1GOOWiUT0avYzbbOdsAAAA") + client.CreateSession(session) + client.Disconnect() +} diff --git a/destination.go b/destination.go index cc5717b..59fe21e 100644 --- a/destination.go +++ b/destination.go @@ -23,7 +23,8 @@ type Destination struct { b64 string } -func NewDestination() (dest Destination, err error) { +func NewDestination() (dest *Destination, err error) { + dest = &Destination{} nullCert := NewCertificate(CERTIFICATE_NULL) dest.cert = &nullCert dest.sgk, err = GetCryptoInstance().SignatureKeygen(DSA_SHA1) @@ -33,7 +34,8 @@ func NewDestination() (dest Destination, err error) { return } -func NewDestinationFromMessage(stream *Stream) (dest Destination, err error) { +func NewDestinationFromMessage(stream *Stream) (dest *Destination, err error) { + dest = &Destination{} _, err = stream.Read(dest.pubKey[:]) if err != nil { return @@ -53,12 +55,13 @@ func NewDestinationFromMessage(stream *Stream) (dest Destination, err error) { dest.cert = &cert dest.generateB32() dest.generateB64() - return + return dest, err } -func NewDestinationFromStream(stream *Stream) (dest Destination, err error) { +func NewDestinationFromStream(stream *Stream) (dest *Destination, err error) { var cert Certificate var pubKeyLen uint16 + dest = &Destination{} cert, err = NewCertificateFromStream(stream) dest.cert = &cert dest.sgk, err = GetCryptoInstance().SignatureKeyPairFromStream(stream) @@ -72,7 +75,7 @@ func NewDestinationFromStream(stream *Stream) (dest Destination, err error) { return } -func NewDestinationFromBase64(base64 string) (dest Destination, err error) { +func NewDestinationFromBase64(base64 string) (dest *Destination, err error) { /* Same as decode, except from a filesystem / URL friendly set of characters, * replacing / with ~, and + with - */ @@ -91,7 +94,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) (*Destination, error) { var stream Stream stream.loadFile(file) return NewDestinationFromStream(&stream) diff --git a/destination_test.go b/destination_test.go index 7645694..42e8176 100644 --- a/destination_test.go +++ b/destination_test.go @@ -3,7 +3,7 @@ package go_i2cp import "testing" func TestRandomDestination(t *testing.T) { - var destOne, destTwo Destination + var destOne, destTwo *Destination var err error destOne, err = NewDestination() var stream = NewStream(make([]byte, 4096)) diff --git a/logger.go b/logger.go index 2a69ab2..a94f33d 100644 --- a/logger.go +++ b/logger.go @@ -71,7 +71,7 @@ func Fatal(tags LoggerTags, message string, args ...interface{}) { func (l *Logger) log(tags LoggerTags, format string, args ...interface{}) { if l.callbacks == nil { - fmt.Printf(format, args) + fmt.Printf(format+"\n", args) } else { l.callbacks.onLog(l, tags, fmt.Sprintf(format, args)) } diff --git a/session.go b/session.go index b6c51de..6a61e4d 100644 --- a/session.go +++ b/session.go @@ -48,10 +48,11 @@ type Session struct { callbacks *SessionCallbacks } -func NewSession(client *Client, callbacks SessionCallbacks, destFilename string) (sess *Session) { +func NewSession(client *Client, callbacks SessionCallbacks) (sess *Session) { sess = &Session{} sess.client = client - sess.config = &SessionConfig{} + dest, _ := NewDestination() + sess.config = &SessionConfig{destination: dest} sess.callbacks = &callbacks return } diff --git a/session_config.go b/session_config.go index c685355..c8dfdff 100644 --- a/session_config.go +++ b/session_config.go @@ -78,15 +78,13 @@ type SessionConfig struct { func NewSessionConfigFromDestinationFile(filename string) (config SessionConfig) { var home string if file, err := os.Open(filename); err == nil { - dest, err := NewDestinationFromFile(file) - config.destination = &dest + config.destination, err = NewDestinationFromFile(file) 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, _ := NewDestination() - config.destination = &dest + config.destination, _ = NewDestination() } if len(filename) > 0 { config.destination.WriteToFile(filename) @@ -105,12 +103,11 @@ func NewSessionConfigFromDestinationFile(filename string) (config SessionConfig) func (config *SessionConfig) writeToMessage(stream *Stream) { config.destination.WriteToMessage(stream) config.writeMappingToMessage(stream) - stream.WriteUint64(uint64(time.Now().Unix())) + stream.WriteUint64(uint64(time.Now().Unix() * 1000)) GetCryptoInstance().WriteSignatureToStream(&config.destination.sgk, stream) } func (config *SessionConfig) writeMappingToMessage(stream *Stream) (err error) { - is := NewStream(make([]byte, 0xffff)) - count := 0 + m := make(map[string]string) for i := 0; i < int(NR_OF_SESSION_CONFIG_PROPERTIES); i++ { var option string if config.properties[i] == "" { @@ -120,15 +117,10 @@ func (config *SessionConfig) writeMappingToMessage(stream *Stream) (err error) { if option == "" { continue } - is.Write([]byte(option + "=" + config.properties[i] + ";")) - count++ + m[option] = config.properties[i] } - 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 + Debug(SESSION_CONFIG, "Writing %d options to mapping table", len(m)) + return stream.WriteMapping(m) } func (config *SessionConfig) configOptLookup(property SessionConfigProperty) string { return sessionOptions[property] diff --git a/session_test.go b/session_test.go new file mode 100644 index 0000000..0a3d374 --- /dev/null +++ b/session_test.go @@ -0,0 +1 @@ +package go_i2cp diff --git a/stream.go b/stream.go index a6855ed..d262986 100644 --- a/stream.go +++ b/stream.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/binary" "os" + "sort" ) type Stream struct { @@ -16,40 +17,68 @@ func NewStream(buf []byte) (s *Stream) { func (s *Stream) ReadUint16() (r uint16, err error) { bts := make([]byte, 2) _, err = s.Read(bts) - r = binary.LittleEndian.Uint16(bts) + r = binary.BigEndian.Uint16(bts) return } func (s *Stream) ReadUint32() (r uint32, err error) { bts := make([]byte, 4) _, err = s.Read(bts) - r = binary.LittleEndian.Uint32(bts) + r = binary.BigEndian.Uint32(bts) return } func (s *Stream) ReadUint64() (r uint64, err error) { bts := make([]byte, 8) _, err = s.Read(bts) - r = binary.LittleEndian.Uint64(bts) + r = binary.BigEndian.Uint64(bts) return } func (s *Stream) WriteUint16(i uint16) (err error) { bts := make([]byte, 2) - binary.LittleEndian.PutUint16(bts, i) + binary.BigEndian.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) + binary.BigEndian.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) + binary.BigEndian.PutUint64(bts, i) _, err = s.Write(bts) return } + +func (stream *Stream) WriteLenPrefixedString(s string) (err error) { + err = stream.WriteByte(uint8(len(s))) + _, err = stream.WriteString(s) + return +} + +func (stream *Stream) WriteMapping(m map[string]string) (err error) { + buf := NewStream(make([]byte, 0)) + keys := make([]string, len(m)) + for k := range m { + keys = append(keys, k) + } + sort.Strings(keys) + for _, key := range keys { + if key == "" { + continue + } + buf.WriteLenPrefixedString(key) + buf.WriteByte(byte('=')) + buf.WriteLenPrefixedString(m[key]) + buf.WriteByte(byte(';')) + } + err = stream.WriteUint16(uint16(buf.Len())) + _, err = stream.Write(buf.Bytes()) + return +} + func (s *Stream) loadFile(f *os.File) (err error) { _, err = f.Read(s.Bytes()) return diff --git a/tcp.go b/tcp.go index c4a1229..60c9e94 100644 --- a/tcp.go +++ b/tcp.go @@ -21,7 +21,7 @@ const ( var CAFile = "/etc/ssl/certs/ca-certificates.crt" var defaultRouterAddress = "127.0.0.1:7654" -const USE_TLS = true +const USE_TLS = false func (tcp *Tcp) Init() (err error) { tcp.address, err = net.ResolveTCPAddr("tcp", defaultRouterAddress)