refactor: move and simplify handshake logic
This commit is contained in:
1 parent
f01d9ce092
commit
e2818f883c
2 files changed
+151
-134
No files matched your search
@@ -3,11 +3,9 @@ package router
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/gob"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -409,138 +407,6 @@ func (conn *LocalConnection) handleShutdown() {
|
||||
|
||||
// Helpers
|
||||
|
||||
func (conn *LocalConnection) handshake(enc *gob.Encoder, dec *gob.Decoder, acceptNewPeer bool) error {
|
||||
// We do not need to worry about locking in here as at this point
|
||||
// the connection is not reachable by any go-routine other than
|
||||
// ourself. Only when we add this connection to the conn.local
|
||||
// peer will it be visible from multiple go-routines.
|
||||
|
||||
conn.extendReadDeadline()
|
||||
|
||||
localConnID := randUint64()
|
||||
versionStr := fmt.Sprint(ProtocolVersion)
|
||||
handshakeSend := map[string]string{
|
||||
"Protocol": Protocol,
|
||||
"ProtocolVersion": versionStr,
|
||||
"PeerNameFlavour": PeerNameFlavour,
|
||||
"Name": conn.local.Name.String(),
|
||||
"UID": fmt.Sprint(conn.local.UID),
|
||||
"ConnID": fmt.Sprint(localConnID)}
|
||||
handshakeRecv := map[string]string{}
|
||||
|
||||
usingPassword := conn.Router.UsingPassword()
|
||||
var public, private *[32]byte
|
||||
var err error
|
||||
if usingPassword {
|
||||
public, private, err = GenerateKeyPair()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
handshakeSend["PublicKey"] = hex.EncodeToString(public[:])
|
||||
}
|
||||
enc.Encode(handshakeSend)
|
||||
|
||||
err = dec.Decode(&handshakeRecv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = checkHandshakeStringField("Protocol", Protocol, handshakeRecv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = checkHandshakeStringField("ProtocolVersion", versionStr, handshakeRecv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = checkHandshakeStringField("PeerNameFlavour", PeerNameFlavour, handshakeRecv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nameStr, err := checkHandshakeStringField("Name", "", handshakeRecv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
name, err := PeerNameFromString(nameStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !acceptNewPeer {
|
||||
if _, found := conn.Router.Peers.Fetch(name); !found {
|
||||
return fmt.Errorf("Found unknown remote name: %s at %s", name, conn.remoteTCPAddr)
|
||||
}
|
||||
}
|
||||
if existingConn, found := conn.local.ConnectionTo(name); found && existingConn.Established() {
|
||||
return fmt.Errorf("Already have connection to %s at %s", name, existingConn.RemoteTCPAddr())
|
||||
}
|
||||
|
||||
uidStr, err := checkHandshakeStringField("UID", "", handshakeRecv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uid, err := strconv.ParseUint(uidStr, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
remoteConnIdStr, err := checkHandshakeStringField("ConnID", "", handshakeRecv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
remoteConnID, err := strconv.ParseUint(remoteConnIdStr, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn.uid = localConnID ^ remoteConnID
|
||||
|
||||
if usingPassword {
|
||||
remotePublicStr, rpErr := checkHandshakeStringField("PublicKey", "", handshakeRecv)
|
||||
if rpErr != nil {
|
||||
return rpErr
|
||||
}
|
||||
remotePublicSlice, rpErr := hex.DecodeString(remotePublicStr)
|
||||
if rpErr != nil {
|
||||
return rpErr
|
||||
}
|
||||
remotePublic := [32]byte{}
|
||||
for idx, elem := range remotePublicSlice {
|
||||
remotePublic[idx] = elem
|
||||
}
|
||||
conn.SessionKey = FormSessionKey(&remotePublic, private, conn.Router.Password)
|
||||
conn.tcpSender = NewEncryptedTCPSender(enc, conn)
|
||||
conn.Decryptor = NewNaClDecryptor(conn)
|
||||
} else {
|
||||
if _, found := handshakeRecv["PublicKey"]; found {
|
||||
return fmt.Errorf("Remote network is encrypted. Password required.")
|
||||
}
|
||||
conn.tcpSender = NewSimpleTCPSender(enc)
|
||||
conn.Decryptor = NewNonDecryptor(conn)
|
||||
}
|
||||
|
||||
toPeer := NewPeer(name, uid, 0)
|
||||
toPeer = conn.Router.Peers.FetchWithDefault(toPeer)
|
||||
switch toPeer {
|
||||
case nil:
|
||||
return fmt.Errorf("Connection appears to be with different version of a peer we already know of")
|
||||
case conn.local:
|
||||
conn.remote = toPeer // have to do assigment here to ensure Shutdown releases ref count
|
||||
return fmt.Errorf("Cannot connect to ourself")
|
||||
default:
|
||||
conn.remote = toPeer
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func checkHandshakeStringField(fieldName string, expectedValue string, handshake map[string]string) (string, error) {
|
||||
val, found := handshake[fieldName]
|
||||
if !found {
|
||||
return "", fmt.Errorf("Field %s is missing", fieldName)
|
||||
}
|
||||
if expectedValue != "" && val != expectedValue {
|
||||
return "", fmt.Errorf("Field %s has wrong value; expected '%s', received '%s'", fieldName, expectedValue, val)
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) receiveTCP(decoder *gob.Decoder) {
|
||||
defer conn.Decryptor.Shutdown()
|
||||
usingPassword := conn.SessionKey != nil
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type FieldValidator struct {
|
||||
fields map[string]string
|
||||
err error
|
||||
}
|
||||
|
||||
func NewFieldValidator(fields map[string]string) FieldValidator {
|
||||
return FieldValidator{fields, nil}
|
||||
}
|
||||
|
||||
func (fv FieldValidator) Value(fieldName string) (string, error) {
|
||||
if fv.err != nil {
|
||||
return "", fv.err
|
||||
}
|
||||
val, found := fv.fields[fieldName]
|
||||
if !found {
|
||||
fv.err = fmt.Errorf("Field %s is missing", fieldName)
|
||||
return "", fv.err
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (fv FieldValidator) CheckEqual(fieldName, expectedValue string) error {
|
||||
val, err := fv.Value(fieldName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if val != expectedValue {
|
||||
fv.err = fmt.Errorf("Field %s has wrong value; expected '%s', received '%s'", fieldName, expectedValue, val)
|
||||
return fv.err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fv FieldValidator) Err() error {
|
||||
return fv.err
|
||||
}
|
||||
|
||||
func (conn *LocalConnection) handshake(enc *gob.Encoder, dec *gob.Decoder, acceptNewPeer bool) error {
|
||||
// We do not need to worry about locking in here as at this point
|
||||
// the connection is not reachable by any go-routine other than
|
||||
// ourself. Only when we add this connection to the conn.local
|
||||
// peer will it be visible from multiple go-routines.
|
||||
|
||||
conn.extendReadDeadline()
|
||||
|
||||
localConnID := randUint64()
|
||||
versionStr := fmt.Sprint(ProtocolVersion)
|
||||
handshakeSend := map[string]string{
|
||||
"Protocol": Protocol,
|
||||
"ProtocolVersion": versionStr,
|
||||
"PeerNameFlavour": PeerNameFlavour,
|
||||
"Name": conn.local.Name.String(),
|
||||
"UID": fmt.Sprint(conn.local.UID),
|
||||
"ConnID": fmt.Sprint(localConnID)}
|
||||
handshakeRecv := map[string]string{}
|
||||
|
||||
usingPassword := conn.Router.UsingPassword()
|
||||
var public, private *[32]byte
|
||||
var err error
|
||||
if usingPassword {
|
||||
public, private, err = GenerateKeyPair()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
handshakeSend["PublicKey"] = hex.EncodeToString(public[:])
|
||||
}
|
||||
enc.Encode(handshakeSend)
|
||||
|
||||
err = dec.Decode(&handshakeRecv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fv := NewFieldValidator(handshakeRecv)
|
||||
fv.CheckEqual("Protocol", Protocol)
|
||||
fv.CheckEqual("ProtocolVersion", versionStr)
|
||||
fv.CheckEqual("PeerNameFlavour", PeerNameFlavour)
|
||||
nameStr, _ := fv.Value("Name")
|
||||
uidStr, _ := fv.Value("UID")
|
||||
remoteConnIdStr, _ := fv.Value("ConnID")
|
||||
if err := fv.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name, err := PeerNameFromString(nameStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !acceptNewPeer {
|
||||
if _, found := conn.Router.Peers.Fetch(name); !found {
|
||||
return fmt.Errorf("Found unknown remote name: %s at %s", name, conn.remoteTCPAddr)
|
||||
}
|
||||
}
|
||||
if existingConn, found := conn.local.ConnectionTo(name); found && existingConn.Established() {
|
||||
return fmt.Errorf("Already have connection to %s at %s", name, existingConn.RemoteTCPAddr())
|
||||
}
|
||||
uid, err := strconv.ParseUint(uidStr, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
remoteConnID, err := strconv.ParseUint(remoteConnIdStr, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn.uid = localConnID ^ remoteConnID
|
||||
|
||||
if usingPassword {
|
||||
remotePublicStr, rpErr := fv.Value("PublicKey")
|
||||
if rpErr != nil {
|
||||
return rpErr
|
||||
}
|
||||
remotePublicSlice, rpErr := hex.DecodeString(remotePublicStr)
|
||||
if rpErr != nil {
|
||||
return rpErr
|
||||
}
|
||||
remotePublic := [32]byte{}
|
||||
for idx, elem := range remotePublicSlice {
|
||||
remotePublic[idx] = elem
|
||||
}
|
||||
conn.SessionKey = FormSessionKey(&remotePublic, private, conn.Router.Password)
|
||||
conn.tcpSender = NewEncryptedTCPSender(enc, conn)
|
||||
conn.Decryptor = NewNaClDecryptor(conn)
|
||||
} else {
|
||||
if _, found := handshakeRecv["PublicKey"]; found {
|
||||
return fmt.Errorf("Remote network is encrypted. Password required.")
|
||||
}
|
||||
conn.tcpSender = NewSimpleTCPSender(enc)
|
||||
conn.Decryptor = NewNonDecryptor(conn)
|
||||
}
|
||||
|
||||
toPeer := NewPeer(name, uid, 0)
|
||||
toPeer = conn.Router.Peers.FetchWithDefault(toPeer)
|
||||
switch toPeer {
|
||||
case nil:
|
||||
return fmt.Errorf("Connection appears to be with different version of a peer we already know of")
|
||||
case conn.local:
|
||||
conn.remote = toPeer // have to do assigment here to ensure Shutdown releases ref count
|
||||
return fmt.Errorf("Cannot connect to ourself")
|
||||
default:
|
||||
conn.remote = toPeer
|
||||
return nil
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user