165 lines
4.6 KiB
Go
165 lines
4.6 KiB
Go
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()
|
|
usingPassword := conn.Router.UsingPassword()
|
|
fv, private, err := conn.handshakeSendRecv(localConnID, usingPassword, enc, dec)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
nameStr, _ := fv.Value("Name")
|
|
nickNameStr, _ := fv.Value("NickName")
|
|
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.Router.Ourself.ConnectionTo(name); found && existingConn.Established() {
|
|
return fmt.Errorf("Already have connection to %s at %s", existingConn.Remote(), existingConn.RemoteTCPAddr())
|
|
}
|
|
uid, err := ParsePeerUID(uidStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
remoteConnID, err := strconv.ParseUint(remoteConnIDStr, 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
conn.uid = localConnID ^ remoteConnID
|
|
|
|
remotePublicStr, rpErr := fv.Value("PublicKey")
|
|
if usingPassword {
|
|
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.SessionKey, conn.outbound)
|
|
conn.Decryptor = NewNaClDecryptor(conn.SessionKey, conn.outbound)
|
|
} else {
|
|
if rpErr == nil {
|
|
return fmt.Errorf("Remote network is encrypted. Password required.")
|
|
}
|
|
conn.tcpSender = NewSimpleTCPSender(enc)
|
|
conn.Decryptor = NewNonDecryptor()
|
|
}
|
|
|
|
return conn.setRemote(NewPeer(name, nickNameStr, uid, 0))
|
|
}
|
|
|
|
func (conn *LocalConnection) handshakeSendRecv(localConnID uint64, usingPassword bool, enc *gob.Encoder, dec *gob.Decoder) (*FieldValidator, *[32]byte, error) {
|
|
versionStr := fmt.Sprint(ProtocolVersion)
|
|
handshakeSend := map[string]string{
|
|
"Protocol": Protocol,
|
|
"ProtocolVersion": versionStr,
|
|
"PeerNameFlavour": PeerNameFlavour,
|
|
"Name": conn.local.Name.String(),
|
|
"NickName": conn.local.NickName,
|
|
"UID": fmt.Sprint(conn.local.UID),
|
|
"ConnID": fmt.Sprint(localConnID)}
|
|
handshakeRecv := map[string]string{}
|
|
|
|
var public, private *[32]byte
|
|
var err error
|
|
if usingPassword {
|
|
public, private, err = GenerateKeyPair()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
handshakeSend["PublicKey"] = hex.EncodeToString(public[:])
|
|
}
|
|
enc.Encode(handshakeSend)
|
|
|
|
err = dec.Decode(&handshakeRecv)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
fv := NewFieldValidator(handshakeRecv)
|
|
fv.CheckEqual("Protocol", Protocol)
|
|
fv.CheckEqual("ProtocolVersion", versionStr)
|
|
fv.CheckEqual("PeerNameFlavour", PeerNameFlavour)
|
|
return fv, private, nil
|
|
}
|
|
|
|
func (conn *LocalConnection) setRemote(toPeer *Peer) error {
|
|
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
|
|
}
|
|
}
|