Commit bbfba910 authored by Juan Batiz-Benet's avatar Juan Batiz-Benet

Merge pull request #180 from cryptix/versionHandshake

Version handshake
parents f0571d32 0f47b930
all: semver.pb.go
semver.pb.go: semver.proto
protoc --gogo_out=. --proto_path=../../../../../:/usr/local/opt/protobuf/include:. $<
clean:
rm semver.pb.go
// Code generated by protoc-gen-gogo.
// source: semver.proto
// DO NOT EDIT!
/*
Package handshake is a generated protocol buffer package.
It is generated from these files:
semver.proto
It has these top-level messages:
Handshake1
*/
package handshake
import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = math.Inf
type Handshake1 struct {
// protocolVersion determines compatibility between peers
ProtocolVersion *string `protobuf:"bytes,1,opt,name=protocolVersion" json:"protocolVersion,omitempty"`
// agentVersion is like a UserAgent string in browsers, or client version in bittorrent
// includes the client name and client. e.g. "go-ipfs/0.1.0"
AgentVersion *string `protobuf:"bytes,2,opt,name=agentVersion" json:"agentVersion,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Handshake1) Reset() { *m = Handshake1{} }
func (m *Handshake1) String() string { return proto.CompactTextString(m) }
func (*Handshake1) ProtoMessage() {}
func (m *Handshake1) GetProtocolVersion() string {
if m != nil && m.ProtocolVersion != nil {
return *m.ProtocolVersion
}
return ""
}
func (m *Handshake1) GetAgentVersion() string {
if m != nil && m.AgentVersion != nil {
return *m.AgentVersion
}
return ""
}
func init() {
}
package handshake;
message Handshake1 {
// protocolVersion determines compatibility between peers
optional string protocolVersion = 1; // semver
// agentVersion is like a UserAgent string in browsers, or client version in bittorrent
// includes the client name and client. e.g. "go-ipfs/0.1.0"
optional string agentVersion = 2; // semver
// we'll have more fields here later.
}
package handshake
import (
"errors"
"fmt"
updates "github.com/jbenet/go-ipfs/updates"
semver "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
)
// currentVersion holds the current protocol version for a client running this code
var currentVersion *semver.Version
func init() {
var err error
currentVersion, err = semver.NewVersion("0.0.1")
if err != nil {
panic(fmt.Errorf("invalid protocol version: %v", err))
}
}
// CurrentHandshake returns the current protocol version as a protobuf message
func CurrentHandshake() *Handshake1 {
return NewHandshake1(currentVersion.String(), "go-ipfs/"+updates.Version)
}
// ErrVersionMismatch is returned when two clients don't share a protocol version
var ErrVersionMismatch = errors.New("protocol missmatch")
// Compatible checks wether two versions are compatible
// returns nil if they are fine
func Compatible(handshakeA, handshakeB *Handshake1) error {
a, err := semver.NewVersion(*handshakeA.ProtocolVersion)
if err != nil {
return err
}
b, err := semver.NewVersion(*handshakeB.ProtocolVersion)
if err != nil {
return err
}
if a.Major != b.Major {
return ErrVersionMismatch
}
return nil
}
// NewHandshake1 creates a new Handshake1 from the two strings
func NewHandshake1(protoVer, agentVer string) *Handshake1 {
return &Handshake1{
ProtocolVersion: &protoVer,
AgentVersion: &agentVer,
}
}
package handshake
import "testing"
func TestCompatible(t *testing.T) {
tcases := []struct {
a, b string
expected error
}{
{"0.0.0", "0.0.0", nil},
{"1.0.0", "1.1.0", nil},
{"1.0.0", "1.0.1", nil},
{"0.0.0", "1.0.0", ErrVersionMismatch},
{"1.0.0", "0.0.0", ErrVersionMismatch},
}
for i, tcase := range tcases {
if Compatible(NewHandshake1(tcase.a, ""), NewHandshake1(tcase.b, "")) != tcase.expected {
t.Fatalf("case[%d] failed", i)
}
}
}
......@@ -6,8 +6,10 @@ import (
spipe "github.com/jbenet/go-ipfs/crypto/spipe"
conn "github.com/jbenet/go-ipfs/net/conn"
handshake "github.com/jbenet/go-ipfs/net/handshake"
msg "github.com/jbenet/go-ipfs/net/message"
proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net"
)
......@@ -109,6 +111,10 @@ func (s *Swarm) connSetup(c *conn.Conn) error {
// add address of connection to Peer. Maybe it should happen in connSecure.
c.Peer.AddAddress(c.Addr)
if err := s.connVersionExchange(c); err != nil {
return fmt.Errorf("Conn version exchange error: %v", err)
}
// add to conns
s.connsLock.Lock()
if _, ok := s.conns[c.Peer.Key()]; ok {
......@@ -152,6 +158,53 @@ func (s *Swarm) connSecure(c *conn.Conn) error {
return nil
}
// connVersionExchange exchanges local and remote versions and compares them
// closes remote and returns an error in case of major difference
func (s *Swarm) connVersionExchange(remote *conn.Conn) error {
var remoteHandshake, localHandshake *handshake.Handshake1
localHandshake = handshake.CurrentHandshake()
myVerBytes, err := proto.Marshal(localHandshake)
if err != nil {
return err
}
remote.Secure.Out <- myVerBytes
log.Debug("Send my version(%s) [to = %s]", localHandshake, remote.Peer)
select {
case <-s.ctx.Done():
return s.ctx.Err()
case <-remote.Closed:
return errors.New("remote closed connection during version exchange")
case data, ok := <-remote.Secure.In:
if !ok {
return fmt.Errorf("Error retrieving from conn: %v", remote.Peer)
}
remoteHandshake = new(handshake.Handshake1)
err = proto.Unmarshal(data, remoteHandshake)
if err != nil {
s.Close()
return fmt.Errorf("connSetup: could not decode remote version: %q", err)
}
log.Debug("Received remote version(%s) [from = %s]", remoteHandshake, remote.Peer)
}
if err := handshake.Compatible(localHandshake, remoteHandshake); err != nil {
log.Info("%s (%s) incompatible version with %s (%s)", s.local, localHandshake, remote.Peer, remoteHandshake)
remote.Close()
return err
}
log.Debug("[peer: %s] Version compatible", remote.Peer)
return nil
}
// Handles the unwrapping + sending of messages to the right connection.
func (s *Swarm) fanOut() {
for {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment