5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-11-23 02:01:36 +00:00

Some attempt at exchanging session metadata over the wire (broken)

This commit is contained in:
Neil Alexander 2018-10-21 22:58:27 +01:00
parent 4f435705e3
commit a1b72c16d8
No known key found for this signature in database
GPG Key ID: A02A2019A2BB0944
9 changed files with 228 additions and 83 deletions

View File

@ -470,7 +470,9 @@ func (a *admin) getData_getSelf() *admin_nodeInfo {
{"ip", a.core.GetAddress().String()}, {"ip", a.core.GetAddress().String()},
{"subnet", a.core.GetSubnet().String()}, {"subnet", a.core.GetSubnet().String()},
{"coords", fmt.Sprint(coords)}, {"coords", fmt.Sprint(coords)},
{"friendly_name", a.core.friendlyName}, {"name", a.core.metadata.name},
{"location", a.core.metadata.location},
{"contact", a.core.metadata.contact},
} }
return &self return &self
} }
@ -494,7 +496,6 @@ func (a *admin) getData_getPeers() []admin_nodeInfo {
{"bytes_sent", atomic.LoadUint64(&p.bytesSent)}, {"bytes_sent", atomic.LoadUint64(&p.bytesSent)},
{"bytes_recvd", atomic.LoadUint64(&p.bytesRecvd)}, {"bytes_recvd", atomic.LoadUint64(&p.bytesRecvd)},
{"endpoint", p.endpoint}, {"endpoint", p.endpoint},
{"friendly_name", p.friendlyName},
} }
peerInfos = append(peerInfos, info) peerInfos = append(peerInfos, info)
} }
@ -520,7 +521,6 @@ func (a *admin) getData_getSwitchPeers() []admin_nodeInfo {
{"bytes_sent", atomic.LoadUint64(&peer.bytesSent)}, {"bytes_sent", atomic.LoadUint64(&peer.bytesSent)},
{"bytes_recvd", atomic.LoadUint64(&peer.bytesRecvd)}, {"bytes_recvd", atomic.LoadUint64(&peer.bytesRecvd)},
{"endpoint", peer.endpoint}, {"endpoint", peer.endpoint},
{"friendly_name", peer.friendlyName},
} }
peerInfos = append(peerInfos, info) peerInfos = append(peerInfos, info)
} }

View File

@ -2,7 +2,7 @@ package config
// NodeConfig defines all configuration values needed to run a signle yggdrasil node // NodeConfig defines all configuration values needed to run a signle yggdrasil node
type NodeConfig struct { type NodeConfig struct {
FriendlyName string `comment:"Friendly name for this node. It is visible to direct peers."` Metadata Metadata `comment:"Optional node metadata. Entirely optional but visible to all\npeers and nodes with open sessions."`
Listen string `comment:"Listen address for peer connections. Default is to listen for all\nTCP connections over IPv4 and IPv6 with a random port."` Listen string `comment:"Listen address for peer connections. Default is to listen for all\nTCP connections over IPv4 and IPv6 with a random port."`
AdminListen string `comment:"Listen address for admin connections Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X."` AdminListen string `comment:"Listen address for admin connections Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X."`
Peers []string `comment:"List of connection strings for static peers in URI format, i.e.\ntcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j."` Peers []string `comment:"List of connection strings for static peers in URI format, i.e.\ntcp://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j."`
@ -35,3 +35,9 @@ type SessionFirewall struct {
WhitelistEncryptionPublicKeys []string `comment:"List of public keys from which network traffic is always accepted,\nregardless of AllowFromDirect or AllowFromRemote."` WhitelistEncryptionPublicKeys []string `comment:"List of public keys from which network traffic is always accepted,\nregardless of AllowFromDirect or AllowFromRemote."`
BlacklistEncryptionPublicKeys []string `comment:"List of public keys from which network traffic is always rejected,\nregardless of the whitelist, AllowFromDirect or AllowFromRemote."` BlacklistEncryptionPublicKeys []string `comment:"List of public keys from which network traffic is always rejected,\nregardless of the whitelist, AllowFromDirect or AllowFromRemote."`
} }
type Metadata struct {
Name string
Location string
Contact string
}

View File

@ -20,7 +20,7 @@ type Core struct {
boxPriv boxPrivKey boxPriv boxPrivKey
sigPub sigPubKey sigPub sigPubKey
sigPriv sigPrivKey sigPriv sigPrivKey
friendlyName string metadata metadata
switchTable switchTable switchTable switchTable
peers peers peers peers
sigs sigManager sigs sigManager
@ -40,7 +40,7 @@ func (c *Core) init(bpub *boxPubKey,
bpriv *boxPrivKey, bpriv *boxPrivKey,
spub *sigPubKey, spub *sigPubKey,
spriv *sigPrivKey, spriv *sigPrivKey,
friendlyname string) { metadata metadata) {
// TODO separate init and start functions // TODO separate init and start functions
// Init sets up structs // Init sets up structs
// Start launches goroutines that depend on structs being set up // Start launches goroutines that depend on structs being set up
@ -51,7 +51,7 @@ func (c *Core) init(bpub *boxPubKey,
} }
c.boxPub, c.boxPriv = *bpub, *bpriv c.boxPub, c.boxPriv = *bpub, *bpriv
c.sigPub, c.sigPriv = *spub, *spriv c.sigPub, c.sigPriv = *spub, *spriv
c.friendlyName = friendlyname c.metadata = metadata
c.admin.core = c c.admin.core = c
c.sigs.init() c.sigs.init()
c.searches.init(c) c.searches.init(c)
@ -65,11 +65,8 @@ func (c *Core) init(bpub *boxPubKey,
} }
// Gets the friendly name of this node, as specified in the NodeConfig. // Gets the friendly name of this node, as specified in the NodeConfig.
func (c *Core) GetFriendlyName() string { func (c *Core) GetMeta() metadata {
if c.friendlyName == "" { return c.metadata
return "(none)"
}
return c.friendlyName
} }
// Starts up Yggdrasil using the provided NodeConfig, and outputs debug logging // Starts up Yggdrasil using the provided NodeConfig, and outputs debug logging
@ -105,7 +102,13 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error {
copy(sigPub[:], sigPubHex) copy(sigPub[:], sigPubHex)
copy(sigPriv[:], sigPrivHex) copy(sigPriv[:], sigPrivHex)
c.init(&boxPub, &boxPriv, &sigPub, &sigPriv, nc.FriendlyName) meta := metadata{
name: nc.Metadata.Name,
location: nc.Metadata.Location,
contact: nc.Metadata.Contact,
}
c.init(&boxPub, &boxPriv, &sigPub, &sigPriv, meta)
c.admin.init(c, nc.AdminListen) c.admin.init(c, nc.AdminListen)
if err := c.tcp.init(c, nc.Listen, nc.ReadTimeout); err != nil { if err := c.tcp.init(c, nc.Listen, nc.ReadTimeout); err != nil {

View File

@ -0,0 +1,7 @@
package yggdrasil
type metadata struct {
name string
location string
contact string
}

View File

@ -86,7 +86,7 @@ type peer struct {
shared boxSharedKey shared boxSharedKey
linkShared boxSharedKey linkShared boxSharedKey
endpoint string endpoint string
friendlyName string metadata metadata
firstSeen time.Time // To track uptime for getPeers firstSeen time.Time // To track uptime for getPeers
linkOut (chan []byte) // used for protocol traffic (to bypass queues) linkOut (chan []byte) // used for protocol traffic (to bypass queues)
doSend (chan struct{}) // tell the linkLoop to send a switchMsg doSend (chan struct{}) // tell the linkLoop to send a switchMsg
@ -96,14 +96,14 @@ type peer struct {
} }
// Creates a new peer with the specified box, sig, and linkShared keys, using the lowest unocupied port number. // Creates a new peer with the specified box, sig, and linkShared keys, using the lowest unocupied port number.
func (ps *peers) newPeer(box *boxPubKey, sig *sigPubKey, linkShared *boxSharedKey, endpoint string, friendlyname string) *peer { func (ps *peers) newPeer(box *boxPubKey, sig *sigPubKey, linkShared *boxSharedKey, endpoint string, metadata metadata) *peer {
now := time.Now() now := time.Now()
p := peer{box: *box, p := peer{box: *box,
sig: *sig, sig: *sig,
shared: *getSharedKey(&ps.core.boxPriv, box), shared: *getSharedKey(&ps.core.boxPriv, box),
linkShared: *linkShared, linkShared: *linkShared,
endpoint: endpoint, endpoint: endpoint,
friendlyName: friendlyname, metadata: metadata,
firstSeen: now, firstSeen: now,
doSend: make(chan struct{}, 1), doSend: make(chan struct{}, 1),
core: ps.core} core: ps.core}

View File

@ -47,7 +47,7 @@ func (r *router) init(core *Core) {
r.core = core r.core = core
r.addr = *address_addrForNodeID(&r.core.dht.nodeID) r.addr = *address_addrForNodeID(&r.core.dht.nodeID)
in := make(chan []byte, 32) // TODO something better than this... in := make(chan []byte, 32) // TODO something better than this...
p := r.core.peers.newPeer(&r.core.boxPub, &r.core.sigPub, &boxSharedKey{}, "(self)", r.core.GetFriendlyName()) p := r.core.peers.newPeer(&r.core.boxPub, &r.core.sigPub, &boxSharedKey{}, "(self)", r.core.metadata)
p.out = func(packet []byte) { p.out = func(packet []byte) {
// This is to make very sure it never blocks // This is to make very sure it never blocks
select { select {
@ -324,6 +324,10 @@ func (r *router) handleProto(packet []byte) {
r.handlePing(bs, &p.FromKey) r.handlePing(bs, &p.FromKey)
case wire_SessionPong: case wire_SessionPong:
r.handlePong(bs, &p.FromKey) r.handlePong(bs, &p.FromKey)
case wire_SessionMetaRequest:
fallthrough
case wire_SessionMetaResponse:
r.handleMeta(bs, &p.FromKey)
case wire_DHTLookupRequest: case wire_DHTLookupRequest:
r.handleDHTReq(bs, &p.FromKey) r.handleDHTReq(bs, &p.FromKey)
case wire_DHTLookupResponse: case wire_DHTLookupResponse:
@ -368,6 +372,17 @@ func (r *router) handleDHTRes(bs []byte, fromKey *boxPubKey) {
r.core.dht.handleRes(&res) r.core.dht.handleRes(&res)
} }
// Decodes meta request
func (r *router) handleMeta(bs []byte, fromKey *boxPubKey) {
req := sessionMeta{}
if !req.decode(bs) {
return
}
req.SendPermPub = *fromKey
r.core.log.Printf("handleMeta: %+v\n", req)
r.core.sessions.handleMeta(&req)
}
// Passed a function to call. // Passed a function to call.
// This will send the function to r.admin and block until it finishes. // This will send the function to r.admin and block until it finishes.
// It's used by the admin socket to ask the router mainLoop goroutine about information in the session or dht structs, which cannot be read safely from outside that goroutine. // It's used by the admin socket to ask the router mainLoop goroutine about information in the session or dht structs, which cannot be read safely from outside that goroutine.

View File

@ -25,6 +25,9 @@ type sessionInfo struct {
myHandle handle myHandle handle
theirNonce boxNonce theirNonce boxNonce
myNonce boxNonce myNonce boxNonce
metaReqTime time.Time
metaResTime time.Time
theirMetadata metadata
theirMTU uint16 theirMTU uint16
myMTU uint16 myMTU uint16
wasMTUFixed bool // Was the MTU fixed by a receive error? wasMTUFixed bool // Was the MTU fixed by a receive error?
@ -54,6 +57,13 @@ type sessionPing struct {
MTU uint16 MTU uint16
} }
// Represents a session metadata packet.
type sessionMeta struct {
SendPermPub boxPubKey // Sender's permanent key
IsResponse bool
Metadata metadata
}
// Updates session info in response to a ping, after checking that the ping is OK. // Updates session info in response to a ping, after checking that the ping is OK.
// Returns true if the session was updated, or false otherwise. // Returns true if the session was updated, or false otherwise.
func (s *sessionInfo) update(p *sessionPing) bool { func (s *sessionInfo) update(p *sessionPing) bool {
@ -431,6 +441,66 @@ func (ss *sessions) handlePing(ping *sessionPing) {
bs, sinfo.packet = sinfo.packet, nil bs, sinfo.packet = sinfo.packet, nil
ss.core.router.sendPacket(bs) ss.core.router.sendPacket(bs)
} }
if time.Since(sinfo.metaResTime).Minutes() > 15 {
if time.Since(sinfo.metaReqTime).Minutes() > 1 {
ss.sendMeta(sinfo, false)
}
}
}
func (ss *sessions) sendMeta(sinfo *sessionInfo, isResponse bool) {
meta := sessionMeta{
IsResponse: isResponse,
Metadata: metadata{
name: "some.name.com", //[]byte(ss.core.friendlyName)[0:len(ss.core.friendlyName):32],
location: "Some Place",
contact: "someone@somewhere.com",
},
}
bs := meta.encode()
shared := ss.getSharedKey(&ss.core.boxPriv, &sinfo.theirPermPub)
payload, nonce := boxSeal(shared, bs, nil)
p := wire_protoTrafficPacket{
Coords: sinfo.coords,
ToKey: sinfo.theirPermPub,
FromKey: ss.core.boxPub,
Nonce: *nonce,
Payload: payload,
}
packet := p.encode()
ss.core.router.out(packet)
if isResponse {
ss.core.log.Println("Sent meta response to", sinfo.theirAddr)
} else {
ss.core.log.Println("Sent meta request to", sinfo.theirAddr)
sinfo.metaReqTime = time.Now()
}
}
// Handles a meta request/response.
func (ss *sessions) handleMeta(meta *sessionMeta) {
// Get the corresponding session (or create a new session)
sinfo, isIn := ss.getByTheirPerm(&meta.SendPermPub)
// Check the session firewall
if !isIn && ss.sessionFirewallEnabled {
if !ss.isSessionAllowed(&meta.SendPermPub, false) {
return
}
}
if !isIn || sinfo.timedout() {
return
}
if meta.IsResponse {
ss.core.log.Println("Received meta response", string(meta.Metadata.name), "from", sinfo.theirAddr)
sinfo.theirMetadata = meta.Metadata
sinfo.metaResTime = time.Now()
ss.core.log.Println("- name:", meta.Metadata.name)
ss.core.log.Println("- contact:", meta.Metadata.contact)
ss.core.log.Println("- location:", meta.Metadata.location)
} else {
ss.core.log.Println("Received meta request", string(meta.Metadata.name), "from", sinfo.theirAddr)
ss.sendMeta(sinfo, true)
}
} }
// Used to subtract one nonce from another, staying in the range +- 64. // Used to subtract one nonce from another, staying in the range +- 64.

View File

@ -287,7 +287,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, &meta.link), sock.RemoteAddr().String(), "(none)") p := iface.core.peers.newPeer(&info.box, &info.sig, getSharedKey(myLinkPriv, &meta.link), sock.RemoteAddr().String(), metadata{})
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)

View File

@ -16,6 +16,8 @@ const (
wire_SessionPong // inside protocol traffic header wire_SessionPong // inside protocol traffic header
wire_DHTLookupRequest // inside protocol traffic header wire_DHTLookupRequest // inside protocol traffic header
wire_DHTLookupResponse // inside protocol traffic header wire_DHTLookupResponse // inside protocol traffic header
wire_SessionMetaRequest // inside protocol traffic header
wire_SessionMetaResponse // inside protocol traffic header
) )
// Calls wire_put_uint64 on a nil slice. // Calls wire_put_uint64 on a nil slice.
@ -353,6 +355,48 @@ func (p *sessionPing) decode(bs []byte) bool {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Encodes a sessionPing into its wire format.
func (p *sessionMeta) encode() []byte {
var pTypeVal uint64
if p.IsResponse {
pTypeVal = wire_SessionMetaResponse
} else {
pTypeVal = wire_SessionMetaRequest
}
bs := wire_encode_uint64(pTypeVal)
if p.IsResponse {
bs = append(bs, p.Metadata.name...)
bs = append(bs, p.Metadata.location...)
bs = append(bs, p.Metadata.contact...)
}
return bs
}
// Decodes an encoded sessionPing into the struct, returning true if successful.
func (p *sessionMeta) decode(bs []byte) bool {
var pType uint64
switch {
case !wire_chop_uint64(&pType, &bs):
return false
case pType != wire_SessionMetaRequest && pType != wire_SessionMetaResponse:
return false
}
p.IsResponse = pType == wire_SessionMetaResponse
if p.IsResponse {
switch {
case !wire_chop_slice([]byte(p.Metadata.name), &bs):
return false
case !wire_chop_slice([]byte(p.Metadata.location), &bs):
return false
case !wire_chop_slice([]byte(p.Metadata.contact), &bs):
return false
}
}
return true
}
////////////////////////////////////////////////////////////////////////////////
// Encodes a dhtReq into its wire format. // Encodes a dhtReq into its wire format.
func (r *dhtReq) encode() []byte { func (r *dhtReq) encode() []byte {
coords := wire_encode_coords(r.Coords) coords := wire_encode_coords(r.Coords)