mirror of
https://github.com/cwinfo/yggdrasil-go.git
synced 2024-11-25 21:51:38 +00:00
Merge pull request #107 from Arceliar/wire
Add version information to connection setup
This commit is contained in:
commit
b0acc19e3d
@ -10,6 +10,10 @@ package yggdrasil
|
|||||||
// Could be used to DoS (connect, give someone else's keys, spew garbage)
|
// Could be used to DoS (connect, give someone else's keys, spew garbage)
|
||||||
// I guess the "peer" part should watch for link packets, disconnect?
|
// I guess the "peer" part should watch for link packets, disconnect?
|
||||||
|
|
||||||
|
// TCP connections start with a metadata exchange.
|
||||||
|
// It involves exchanging version numbers and crypto keys
|
||||||
|
// See version.go for version metadata format
|
||||||
|
|
||||||
import "net"
|
import "net"
|
||||||
import "time"
|
import "time"
|
||||||
import "errors"
|
import "errors"
|
||||||
@ -142,28 +146,39 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) {
|
|||||||
defer sock.Close()
|
defer sock.Close()
|
||||||
// Get our keys
|
// Get our keys
|
||||||
myLinkPub, myLinkPriv := newBoxKeys() // ephemeral link keys
|
myLinkPub, myLinkPriv := newBoxKeys() // ephemeral link keys
|
||||||
keys := []byte{}
|
meta := version_getBaseMetadata()
|
||||||
keys = append(keys, tcp_key[:]...)
|
meta.box = iface.core.boxPub
|
||||||
keys = append(keys, iface.core.boxPub[:]...)
|
meta.sig = iface.core.sigPub
|
||||||
keys = append(keys, iface.core.sigPub[:]...)
|
meta.link = *myLinkPub
|
||||||
keys = append(keys, myLinkPub[:]...)
|
metaBytes := meta.encode()
|
||||||
_, err := sock.Write(keys)
|
_, err := sock.Write(metaBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
timeout := time.Now().Add(6 * time.Second)
|
timeout := time.Now().Add(6 * time.Second)
|
||||||
sock.SetReadDeadline(timeout)
|
sock.SetReadDeadline(timeout)
|
||||||
n, err := sock.Read(keys)
|
_, err = sock.Read(metaBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n < len(keys) { /*panic("Partial key packet?") ;*/
|
meta = version_metadata{} // Reset to zero value
|
||||||
|
if !meta.decode(metaBytes) || !meta.check() {
|
||||||
|
// Failed to decode and check the metadata
|
||||||
|
// If it's a version mismatch issue, then print an error message
|
||||||
|
base := version_getBaseMetadata()
|
||||||
|
if meta.meta == base.meta {
|
||||||
|
if meta.ver > base.ver {
|
||||||
|
iface.core.log.Println("Failed to connect to node:", sock.RemoteAddr().String(), "version:", meta.ver)
|
||||||
|
} else if meta.ver == base.ver && meta.minorVer > base.minorVer {
|
||||||
|
iface.core.log.Println("Failed to connect to node:", sock.RemoteAddr().String(), "version:", fmt.Sprintf("%d.%d", meta.ver, meta.minorVer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO? Block forever to prevent future connection attempts? suppress future messages about the same node?
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
info := tcpInfo{} // used as a map key, so don't include ephemeral link eys
|
info := tcpInfo{ // used as a map key, so don't include ephemeral link key
|
||||||
var theirLinkPub boxPubKey
|
box: meta.box,
|
||||||
if !tcp_chop_keys(&info.box, &info.sig, &theirLinkPub, &keys) { /*panic("Invalid key packet?") ;*/
|
sig: meta.sig,
|
||||||
return
|
|
||||||
}
|
}
|
||||||
// Quit the parent call if this is a connection to ourself
|
// Quit the parent call if this is a connection to ourself
|
||||||
equiv := func(k1, k2 []byte) bool {
|
equiv := func(k1, k2 []byte) bool {
|
||||||
@ -210,7 +225,7 @@ func (iface *tcpInterface) handler(sock net.Conn, incoming bool) {
|
|||||||
}()
|
}()
|
||||||
// Note that multiple connections to the same node are allowed
|
// Note that multiple connections to the same node are allowed
|
||||||
// E.g. over different interfaces
|
// E.g. over different interfaces
|
||||||
p := iface.core.peers.newPeer(&info.box, &info.sig, getSharedKey(myLinkPriv, &theirLinkPub))
|
p := iface.core.peers.newPeer(&info.box, &info.sig, getSharedKey(myLinkPriv, &meta.link))
|
||||||
p.linkOut = make(chan []byte, 1)
|
p.linkOut = make(chan []byte, 1)
|
||||||
in := func(bs []byte) {
|
in := func(bs []byte) {
|
||||||
p.handlePacket(bs)
|
p.handlePacket(bs)
|
||||||
@ -336,30 +351,8 @@ func (iface *tcpInterface) reader(sock net.Conn, in func([]byte)) {
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Magic bytes to check
|
// Magic bytes to check
|
||||||
var tcp_key = [...]byte{'k', 'e', 'y', 's'}
|
|
||||||
var tcp_msg = [...]byte{0xde, 0xad, 0xb1, 0x75} // "dead bits"
|
var tcp_msg = [...]byte{0xde, 0xad, 0xb1, 0x75} // "dead bits"
|
||||||
|
|
||||||
func tcp_chop_keys(box *boxPubKey, sig *sigPubKey, link *boxPubKey, bs *[]byte) bool {
|
|
||||||
// This one is pretty simple: we know how long the message should be
|
|
||||||
// So don't call this with a message that's too short
|
|
||||||
if len(*bs) < len(tcp_key)+2*len(*box)+len(*sig) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for idx := range tcp_key {
|
|
||||||
if (*bs)[idx] != tcp_key[idx] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(*bs) = (*bs)[len(tcp_key):]
|
|
||||||
copy(box[:], *bs)
|
|
||||||
(*bs) = (*bs)[len(box):]
|
|
||||||
copy(sig[:], *bs)
|
|
||||||
(*bs) = (*bs)[len(sig):]
|
|
||||||
copy(link[:], *bs)
|
|
||||||
(*bs) = (*bs)[len(sig):]
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func tcp_chop_msg(bs *[]byte) ([]byte, bool, error) {
|
func tcp_chop_msg(bs *[]byte) ([]byte, bool, error) {
|
||||||
// Returns msg, ok, err
|
// Returns msg, ok, err
|
||||||
if len(*bs) < len(tcp_msg) {
|
if len(*bs) < len(tcp_msg) {
|
||||||
|
70
src/yggdrasil/version.go
Normal file
70
src/yggdrasil/version.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package yggdrasil
|
||||||
|
|
||||||
|
// This file contains the version metadata struct
|
||||||
|
// Used in the inital connection setup and key exchange
|
||||||
|
// Some of this could arguably go in wire.go instead
|
||||||
|
|
||||||
|
type version_metadata struct {
|
||||||
|
meta [4]byte
|
||||||
|
ver uint64 // 1 byte in this version
|
||||||
|
// Everything after this point potentially depends on the version number, and is subject to change in future versions
|
||||||
|
minorVer uint64 // 1 byte in this version
|
||||||
|
box boxPubKey
|
||||||
|
sig sigPubKey
|
||||||
|
link boxPubKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func version_getBaseMetadata() version_metadata {
|
||||||
|
return version_metadata{
|
||||||
|
meta: [4]byte{'m', 'e', 't', 'a'},
|
||||||
|
ver: 0,
|
||||||
|
minorVer: 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func version_getMetaLength() (mlen int) {
|
||||||
|
mlen += 4 // meta
|
||||||
|
mlen += 1 // ver
|
||||||
|
mlen += 1 // minorVer
|
||||||
|
mlen += boxPubKeyLen // box
|
||||||
|
mlen += sigPubKeyLen // sig
|
||||||
|
mlen += boxPubKeyLen // link
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *version_metadata) encode() []byte {
|
||||||
|
bs := make([]byte, 0, version_getMetaLength())
|
||||||
|
bs = append(bs, m.meta[:]...)
|
||||||
|
bs = append(bs, wire_encode_uint64(m.ver)...)
|
||||||
|
bs = append(bs, wire_encode_uint64(m.minorVer)...)
|
||||||
|
bs = append(bs, m.box[:]...)
|
||||||
|
bs = append(bs, m.sig[:]...)
|
||||||
|
bs = append(bs, m.link[:]...)
|
||||||
|
if len(bs) != version_getMetaLength() {
|
||||||
|
panic("Inconsistent metadata length")
|
||||||
|
}
|
||||||
|
return bs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *version_metadata) decode(bs []byte) bool {
|
||||||
|
switch {
|
||||||
|
case !wire_chop_slice(m.meta[:], &bs):
|
||||||
|
return false
|
||||||
|
case !wire_chop_uint64(&m.ver, &bs):
|
||||||
|
return false
|
||||||
|
case !wire_chop_uint64(&m.minorVer, &bs):
|
||||||
|
return false
|
||||||
|
case !wire_chop_slice(m.box[:], &bs):
|
||||||
|
return false
|
||||||
|
case !wire_chop_slice(m.sig[:], &bs):
|
||||||
|
return false
|
||||||
|
case !wire_chop_slice(m.link[:], &bs):
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *version_metadata) check() bool {
|
||||||
|
base := version_getBaseMetadata()
|
||||||
|
return base.meta == m.meta && base.ver == m.ver && base.minorVer == m.minorVer
|
||||||
|
}
|
@ -66,23 +66,14 @@ func wire_decode_uint64(bs []byte) (uint64, int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func wire_intToUint(i int64) uint64 {
|
func wire_intToUint(i int64) uint64 {
|
||||||
var u uint64
|
// Non-negative integers mapped to even integers: 0 -> 0, 1 -> 2, etc.
|
||||||
if i < 0 {
|
// Negative integres mapped to odd integes: -1 -> 1, -2 -> 3, etc.
|
||||||
u = uint64(-i) << 1
|
// This means the least significant bit is a sign bit.
|
||||||
u |= 0x01 // sign bit
|
return ((uint64(-(i+1))<<1)|0x01)*(uint64(i)>>63) + (uint64(i)<<1)*(^uint64(i)>>63)
|
||||||
} else {
|
|
||||||
u = uint64(i) << 1
|
|
||||||
}
|
|
||||||
return u
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func wire_intFromUint(u uint64) int64 {
|
func wire_intFromUint(u uint64) int64 {
|
||||||
var i int64
|
return int64(u&0x01)*(-int64(u>>1)-1) + int64(^u&0x01)*int64(u>>1)
|
||||||
i = int64(u >> 1)
|
|
||||||
if u&0x01 != 0 {
|
|
||||||
i *= -1
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
Loading…
Reference in New Issue
Block a user