diff --git a/client.go b/client.go index fb5e463..8a6a8ba 100644 --- a/client.go +++ b/client.go @@ -78,9 +78,9 @@ type RouterInfo struct { type Client struct { logger *LoggerCallbacks // TODO idk wat this is for - callbacks ClientCallBacks + callbacks *ClientCallBacks properties [NR_OF_I2CP_CLIENT_PROPERTIES]string - tcp *Tcp + tcp Tcp outputStream *Stream messageStream *Stream router RouterInfo @@ -98,17 +98,18 @@ type Client struct { var defaultConfigFile = "/.i2cp.conf" // NewClient creates a new i2p client with the specified callbacks -func NewClient(callbacks ClientCallBacks) (c *Client) { +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.outputStream = NewStream(make([]byte, 0, I2CP_MESSAGE_SIZE)) + c.messageStream = NewStream(make([]byte, 0, I2CP_MESSAGE_SIZE)) c.setDefaultProperties() c.lookup = make(map[string]uint32, 1000) c.lookupReq = make(map[uint32]LookupEntry, 1000) c.sessions = make(map[uint16]*Session) c.outputQueue = make([]*Stream, 0) + c.tcp.Init() return } @@ -267,20 +268,21 @@ func (c *Client) onMsgPayload(stream *Stream) { var protocol uint8 var sessionId, srcPort, destPort uint16 var messageId, payloadSize uint32 - var out Stream var err error var ret int Debug(TAG|PROTOCOL, "Received PayloadMessage message") sessionId, err = stream.ReadUint16() messageId, err = stream.ReadUint32() + _ = messageId // currently unused session, ok := c.sessions[sessionId] if !ok { Fatal(TAG|FATAL, "Session id %d does not match any of our currently initiated sessions by %p", sessionId, c) } payloadSize, err = stream.ReadUint32() + _ = payloadSize // currently unused // validate gzip header var msgStream = bytes.NewBuffer(stream.Bytes()) - ret, err = stream.Read(testHeader[:]) + _, err = stream.Read(testHeader[:]) if testHeader != gzipHeader { Warning(TAG, "Payload validation failed, skipping payload") return @@ -298,9 +300,10 @@ func (c *Client) onMsgPayload(stream *Stream) { destPort, err = stream.ReadUint16() _, err = stream.ReadByte() protocol, err = stream.ReadByte() - session.dispatchMessage(protocol, srcPort, destPort, payload) + session.dispatchMessage(protocol, srcPort, destPort, &Stream{payload}) } - + _ = err // currently unused + _ = ret // currently unused } func (c *Client) onMsgStatus(stream *Stream) { var status uint8 @@ -313,6 +316,7 @@ func (c *Client) onMsgStatus(stream *Stream) { status, err = stream.ReadByte() size, err = stream.ReadUint32() nonce, err = stream.ReadUint32() + _ = err // currently unused Debug(TAG|PROTOCOL, "Message status; session id %d, message id %d, status %d, size %d, nonce %d", sessionId, messageId, status, size, nonce) } func (c *Client) onMsgDestReply(stream *Stream) { @@ -354,7 +358,8 @@ func (c *Client) onMsgSessionStatus(stream *Stream) { Debug(TAG|PROTOCOL, "Received SessionStatus message.") sessionID, err = stream.ReadUint16() sessionStatus, err = stream.ReadByte() - if sessionStatus == I2CP_SESSION_STATUS_CREATED { + _ = err // currently unused + if SessionStatus(sessionStatus) == I2CP_SESSION_STATUS_CREATED { if c.currentSession == nil { Error(TAG, "Received session status created without waiting for it %p", c) return @@ -367,11 +372,10 @@ func (c *Client) onMsgSessionStatus(stream *Stream) { if sess == nil { Fatal(TAG|FATAL, "Session with id %d doesn't exists in client instance %p.", sessionID, c) } else { - sess.dispatchStatus(sessionStatus) + sess.dispatchStatus(SessionStatus(sessionStatus)) } } func (c *Client) onMsgReqVariableLease(stream *Stream) { - var t int var sessionId uint16 var tunnels uint8 var sess *Session @@ -386,9 +390,10 @@ func (c *Client) onMsgReqVariableLease(stream *Stream) { } leases = make([]*Lease, tunnels) for i := uint8(0); i < tunnels; i++ { - leases[i] = NewLeaseFromStream(stream) + leases[i], err = NewLeaseFromStream(stream) } c.msgCreateLeaseSet(sess, tunnels, leases, true) + _ = err // currently unused } func (c *Client) onMsgHostReply(stream *Stream) { var result uint8 @@ -407,6 +412,7 @@ func (c *Client) onMsgHostReply(stream *Stream) { if err != nil { Fatal(TAG|FATAL, "Failed to construct destination from stream.") } + dest = &dst } sess = c.sessions[sessionId] if sess == nil { @@ -415,6 +421,7 @@ func (c *Client) onMsgHostReply(stream *Stream) { lup = c.lookupReq[requestId] delete(c.lookupReq, requestId) sess.dispatchDestination(requestId, lup.address, dest) + _ = err // currently unused } func (c *Client) configFileParseCallback(name, value string) { @@ -571,7 +578,6 @@ func (c *Client) Connect() { } func (c *Client) CreateSession(sess *Session) { - var config *SessionConfig if c.n_sessions == I2CP_MAX_SESSIONS_PER_CLIENT { Warning(TAG, "Maximum number of session per client connection reached.") return @@ -621,8 +627,7 @@ func (c *Client) DestinationLookup(session *Session, address string) (requestId Debug(TAG, "Lookup of b32 address detected, decode and use hash for faster lookup.") host := address[:strings.Index(address, ".")] in.Write([]byte(host)) - dout, _ := GetCryptoInstance().DecodeStream(CODEC_BASE32, in) - out = &dout + out, _ = GetCryptoInstance().DecodeStream(CODEC_BASE32, in) if out.Len() == 0 { Warning(TAG, "Failed to decode hash of address '%s'", address) } diff --git a/client_test.go b/client_test.go new file mode 100644 index 0000000..5d0e934 --- /dev/null +++ b/client_test.go @@ -0,0 +1,11 @@ +package go_i2cp + +import ( + "testing" +) + +func TestClient(t *testing.T) { + client := NewClient(nil) + client.Connect() + client.Disconnect() +} diff --git a/crypto.go b/crypto.go index 72658c1..597fd3d 100644 --- a/crypto.go +++ b/crypto.go @@ -7,6 +7,7 @@ import ( "crypto/sha256" "encoding/base32" "encoding/base64" + "errors" "hash" "io" "math/big" @@ -171,6 +172,19 @@ func (c *Crypto) SignatureKeyPairFromStream(stream *Stream) (sgk SignatureKeyPai return } +func (c *Crypto) PublicKeyFromStream(keyType uint32, stream *Stream) (key *big.Int, err error) { + if keyType == DSA_SHA1 { + key = &big.Int{} + keyBytes := make([]byte, 128) + _, err = stream.Read(keyBytes) + key.SetBytes(keyBytes) + return key, err + } else { + Fatal(CRYPTO, "Unknown signature algorithm") + return nil, errors.New("Unknown signature algorithm") + } +} + // Generate a signature keypair func (c *Crypto) SignatureKeygen(algorithmTyp uint32) (sgk SignatureKeyPair, err error) { var pkey dsa.PrivateKey @@ -193,22 +207,27 @@ func (c *Crypto) HashStream(algorithmTyp uint8, src *Stream) *Stream { return NewStream(c.sh256.Sum(src.Bytes())) } else { Fatal(tAG|FATAL, "Request of unsupported hash algorithm.") + return nil } } -func (c *Crypto) EncodeStream(algorithmTyp uint8, src *Stream) (dst Stream) { +func (c *Crypto) EncodeStream(algorithmTyp uint8, src *Stream) (dst *Stream) { switch algorithmTyp { case CODEC_BASE32: + dst = NewStream(make([]byte, c.b32.EncodedLen(src.Len()))) c.b32.Encode(dst.Bytes(), src.Bytes()) case CODEC_BASE64: + dst = NewStream(make([]byte, c.b64.EncodedLen(src.Len()))) c.b64.Encode(dst.Bytes(), src.Bytes()) } return } -func (c *Crypto) DecodeStream(algorithmTyp uint8, src *Stream) (dst Stream, err error) { +func (c *Crypto) DecodeStream(algorithmTyp uint8, src *Stream) (dst *Stream, err error) { switch algorithmTyp { case CODEC_BASE32: + dst = NewStream(make([]byte, c.b32.DecodedLen(src.Len()))) _, err = c.b32.Decode(dst.Bytes(), src.Bytes()) case CODEC_BASE64: + dst = NewStream(make([]byte, c.b64.DecodedLen(src.Len()))) _, err = c.b64.Decode(dst.Bytes(), src.Bytes()) } return diff --git a/destination.go b/destination.go index ec8e2d9..cc5717b 100644 --- a/destination.go +++ b/destination.go @@ -2,6 +2,7 @@ package go_i2cp import ( "errors" + "fmt" "math/big" "os" "strings" @@ -16,7 +17,7 @@ type Destination struct { cert *Certificate sgk SignatureKeyPair signPubKey *big.Int - pubKey []byte + pubKey [PUB_KEY_SIZE]byte digest [DIGEST_SIZE]byte b32 string b64 string @@ -26,21 +27,29 @@ func NewDestination() (dest Destination, err error) { nullCert := NewCertificate(CERTIFICATE_NULL) dest.cert = &nullCert dest.sgk, err = GetCryptoInstance().SignatureKeygen(DSA_SHA1) + dest.signPubKey = dest.sgk.pub.Y dest.generateB32() dest.generateB64() return } -// TODO ensure that this function not setting dest.sgk is the right thing to do func NewDestinationFromMessage(stream *Stream) (dest Destination, err error) { - signPubKey := make([]byte, 128) - pubKey := make([]byte, PUB_KEY_SIZE) - _, err = stream.Read(pubKey) - _, err = stream.Read(signPubKey) - dest.pubKey = pubKey - dest.signPubKey.SetBytes(signPubKey) + _, err = stream.Read(dest.pubKey[:]) + if err != nil { + return + } + dest.signPubKey, err = GetCryptoInstance().PublicKeyFromStream(DSA_SHA1, stream) + if err != nil { + return + } + dest.sgk = SignatureKeyPair{} + dest.sgk.priv.Y = dest.signPubKey + dest.sgk.pub.Y = dest.signPubKey var cert Certificate cert, err = NewCertificateFromMessage(stream) + if err != nil { + return + } dest.cert = &cert dest.generateB32() dest.generateB64() @@ -57,9 +66,7 @@ func NewDestinationFromStream(stream *Stream) (dest Destination, err error) { if pubKeyLen != PUB_KEY_SIZE { Fatal(tag, "Failed to load pub key len, %d != %d", pubKeyLen, PUB_KEY_SIZE) } - pubKey := make([]byte, PUB_KEY_SIZE) - _, err = stream.Read(pubKey) - dest.pubKey = pubKey + _, err = stream.Read(dest.pubKey[:]) dest.generateB32() dest.generateB64() return @@ -79,9 +86,9 @@ func NewDestinationFromBase64(base64 string) (dest Destination, err error) { replaced = strings.Replace(base64, "~", "/", -1) replaced = strings.Replace(replaced, "-", "+", -1) stream := NewStream([]byte(replaced)) - var decoded Stream + var decoded *Stream decoded, err = GetCryptoInstance().DecodeStream(CODEC_BASE64, stream) - return NewDestinationFromMessage(&decoded) + return NewDestinationFromMessage(decoded) } func NewDestinationFromFile(file *os.File) (dest Destination, err error) { @@ -92,7 +99,7 @@ func NewDestinationFromFile(file *os.File) (dest Destination, err error) { func (dest *Destination) Copy() (newDest Destination) { newDest.cert = dest.cert newDest.signPubKey = dest.signPubKey - copy(newDest.pubKey, dest.pubKey) + newDest.pubKey = dest.pubKey newDest.sgk = dest.sgk newDest.b32 = dest.b32 newDest.b64 = dest.b64 @@ -100,48 +107,59 @@ func (dest *Destination) Copy() (newDest Destination) { return } func (dest *Destination) WriteToFile(filename string) (err error) { - stream := NewStream(make([]byte, DEST_SIZE)) + stream := NewStream(make([]byte, 0, DEST_SIZE)) dest.WriteToStream(stream) var file *os.File file, err = os.Open(filename) stream.WriteTo(file) file.Close() + return } func (dest *Destination) WriteToMessage(stream *Stream) (err error) { - _, err = stream.Write(dest.pubKey) + lena := len(dest.pubKey) + _ = lena + _, err = stream.Write(dest.pubKey[:]) _, err = stream.Write(dest.signPubKey.Bytes()) //GetCryptoInstance().WriteSignatureToStream(&dest.sgk, stream) err = dest.cert.WriteToMessage(stream) + lenb := stream.Len() + _ = lenb return } func (dest *Destination) WriteToStream(stream *Stream) (err error) { err = dest.cert.WriteToStream(stream) err = GetCryptoInstance().WriteSignatureToStream(&dest.sgk, stream) err = stream.WriteUint16(PUB_KEY_SIZE) - _, err = stream.Write(dest.pubKey) + _, err = stream.Write(dest.pubKey[:]) return } //Doesn't seem to be used anywhere?? func (dest *Destination) Verify() (verified bool, err error) { - stream := NewStream(make([]byte, DEST_SIZE)) + stream := NewStream(make([]byte, 0, DEST_SIZE)) dest.WriteToMessage(stream) stream.Write(dest.digest[:]) return GetCryptoInstance().VerifyStream(&dest.sgk, stream) } func (dest *Destination) generateB32() { - stream := NewStream(make([]byte, DEST_SIZE)) + stream := NewStream(make([]byte, 0, DEST_SIZE)) dest.WriteToMessage(stream) cpt := GetCryptoInstance() hash := cpt.HashStream(HASH_SHA256, stream) b32 := cpt.EncodeStream(CODEC_BASE32, hash) - dest.b32 = string(b32.Bytes()) + ".b32.i2p" + length := b32.Len() + _ = length + dest.b32 = string(b32.Bytes()) + dest.b32 += ".b32.i2p" Debug(tag, "New destination %s", dest.b32) } func (dest *Destination) generateB64() { - stream := NewStream(make([]byte, DEST_SIZE)) + stream := NewStream(make([]byte, 0, DEST_SIZE)) dest.WriteToMessage(stream) cpt := GetCryptoInstance() + if stream.Len() > 0 { + fmt.Printf("Stream len %d \n", stream.Len()) + } b64B := cpt.EncodeStream(CODEC_BASE64, stream) replaced := strings.Replace(string(b64B.Bytes()), "/", "~", -1) replaced = strings.Replace(replaced, "/", "~", -1) diff --git a/destination_test.go b/destination_test.go new file mode 100644 index 0000000..7645694 --- /dev/null +++ b/destination_test.go @@ -0,0 +1,55 @@ +package go_i2cp + +import "testing" + +func TestRandomDestination(t *testing.T) { + var destOne, destTwo Destination + var err error + destOne, err = NewDestination() + var stream = NewStream(make([]byte, 4096)) + destOne.WriteToStream(stream) + if err != nil { + t.Fatalf("Could not create first test destination with error %s", err.Error()) + } + destTwo, err = NewDestination() + if err != nil { + t.Fatalf("Could not create second test destination with error %s", err.Error()) + } + if destOne.b32 == destTwo.b32 { + t.Fatal("Random destOne == random destTwo") + } +} + +func TestNewDestinationFromMessage(t *testing.T) { + stream := NewStream(make([]byte, 0, 4096)) + randDest, err := NewDestination() + if err != nil { + t.Fatal("Could not create random destination.") + } + initialB32 := randDest.b32 + randDest.WriteToMessage(stream) + secDest, err := NewDestinationFromMessage(stream) + if err != nil { + t.Fatalf("Failed to create destination from message: '%s'", err.Error()) + } + finalB32 := secDest.b32 + if initialB32 != finalB32 { + t.Fatalf("Recreated destination base32 addresses do not match %s != %s", initialB32, finalB32) + } +} + +func TestNewDestinationFromBase64(t *testing.T) { + randDest, err := NewDestination() + if err != nil { + t.Fatal("Could not create random destination.") + } + initialB64 := randDest.b64 + secDest, err := NewDestinationFromBase64(initialB64) + if err != nil { + t.Fatalf("Failed to create destination from message: '%s'", err.Error()) + } + finalB64 := secDest.b64 + if initialB64 != finalB64 { + t.Fatalf("Recreated destination base64 addresses do not match %s != %s", initialB64, finalB64) + } +} diff --git a/logger.go b/logger.go index b5d4bfd..2a69ab2 100644 --- a/logger.go +++ b/logger.go @@ -3,31 +3,31 @@ package go_i2cp import "fmt" const ( - PROTOCOL = (1 << 0) - LOGIC = (1 << 1) + PROTOCOL = 1 << 0 + LOGIC = 1 << 1 - DEBUG = (1 << 4) - INFO = (1 << 5) - WARNING = (1 << 6) - ERROR = (1 << 7) - FATAL = (1 << 8) + 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) + 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 @@ -46,7 +46,7 @@ type Logger struct { logLevel int } -var logInstance *Logger +var logInstance = &Logger{} // TODO filter func LogInit(callbacks *LoggerCallbacks, level int) { diff --git a/session_config.go b/session_config.go index 8e0ecce..c685355 100644 --- a/session_config.go +++ b/session_config.go @@ -85,7 +85,7 @@ func NewSessionConfigFromDestinationFile(filename string) (config SessionConfig) } } if config.destination == nil { - dest, err := NewDestination() + dest, _ := NewDestination() config.destination = &dest } if len(filename) > 0 { @@ -113,14 +113,14 @@ func (config *SessionConfig) writeMappingToMessage(stream *Stream) (err error) { count := 0 for i := 0; i < int(NR_OF_SESSION_CONFIG_PROPERTIES); i++ { var option string - if sc.properties[i] == "" { + if config.properties[i] == "" { continue } - option = sc.configOptLookup(SessionConfigProperty(i)) + option = config.configOptLookup(SessionConfigProperty(i)) if option == "" { continue } - is.Write([]byte(option + "=" + sc.properties[i] + ";")) + is.Write([]byte(option + "=" + config.properties[i] + ";")) count++ } Debug(SESSION_CONFIG, "Writing %d options to mapping table", count) diff --git a/stream.go b/stream.go index e35ce93..a6855ed 100644 --- a/stream.go +++ b/stream.go @@ -6,10 +6,12 @@ import ( "os" ) -type Stream = bytes.Buffer +type Stream struct { + *bytes.Buffer +} -func NewStream(buf []byte) *Stream { - return bytes.NewBuffer(buf) +func NewStream(buf []byte) (s *Stream) { + return &Stream{bytes.NewBuffer(buf)} } func (s *Stream) ReadUint16() (r uint16, err error) { bts := make([]byte, 2) diff --git a/tcp.go b/tcp.go index e1d2894..c4a1229 100644 --- a/tcp.go +++ b/tcp.go @@ -30,15 +30,15 @@ func (tcp *Tcp) Init() (err error) { func (tcp *Tcp) Connect() (err error) { if USE_TLS { - roots, err := x509.SystemCertPool() + roots, _ := 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) } } + _ = err // currently unused return } diff --git a/version.go b/version.go index 052451b..8011888 100644 --- a/version.go +++ b/version.go @@ -1,7 +1,6 @@ package go_i2cp import ( - "fmt" "strconv" "strings" ) @@ -12,28 +11,23 @@ type Version struct { } func parseVersion(str string) Version { - var err error - var v Version = Version{} + var v = Version{} segments := strings.Split(str, ".") n := len(segments) if n > 0 { - var i int - i, err = strconv.Atoi(segments[0]) + i, _ := strconv.Atoi(segments[0]) v.major = uint16(i) } if n > 1 { - var i int - i, err = strconv.Atoi(segments[1]) + i, _ := strconv.Atoi(segments[1]) v.minor = uint16(i) } if n > 2 { - var i int - i, err = strconv.Atoi(segments[2]) + i, _ := strconv.Atoi(segments[2]) v.micro = uint16(i) } if n > 3 { - var i int - i, err = strconv.Atoi(segments[3]) + i, _ := strconv.Atoi(segments[3]) v.qualifier = uint16(i) } return v