mirror of
https://github.com/ipfs/kubo.git
synced 2025-07-01 02:30:39 +08:00
Merge pull request #180 from cryptix/versionHandshake
Version handshake
This commit is contained in:
8
net/handshake/Makefile
Normal file
8
net/handshake/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
all: semver.pb.go
|
||||
|
||||
semver.pb.go: semver.proto
|
||||
protoc --gogo_out=. --proto_path=../../../../../:/usr/local/opt/protobuf/include:. $<
|
||||
|
||||
clean:
|
||||
rm semver.pb.go
|
51
net/handshake/semver.pb.go
Normal file
51
net/handshake/semver.pb.go
Normal file
@ -0,0 +1,51 @@
|
||||
// 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() {
|
||||
}
|
12
net/handshake/semver.proto
Normal file
12
net/handshake/semver.proto
Normal file
@ -0,0 +1,12 @@
|
||||
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.
|
||||
}
|
56
net/handshake/version.go
Normal file
56
net/handshake/version.go
Normal file
@ -0,0 +1,56 @@
|
||||
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,
|
||||
}
|
||||
}
|
23
net/handshake/version_test.go
Normal file
23
net/handshake/version_test.go
Normal file
@ -0,0 +1,23 @@
|
||||
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 {
|
||||
|
Reference in New Issue
Block a user