5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-09-20 01:22:33 +00:00

give yggdrasil.Dialer the same interface as a net.Dialer, so the only differences are what fields exist in the struct

This commit is contained in:
Arceliar 2019-10-21 19:44:06 -05:00
parent efc0b9ef9f
commit eccd9a348f
4 changed files with 54 additions and 28 deletions

View File

@ -2,7 +2,11 @@
// Of particular importance are the functions used to derive addresses or subnets from a NodeID, or to get the NodeID and bitmask of the bits visible from an address, which is needed for DHT searches. // Of particular importance are the functions used to derive addresses or subnets from a NodeID, or to get the NodeID and bitmask of the bits visible from an address, which is needed for DHT searches.
package address package address
import "github.com/yggdrasil-network/yggdrasil-go/src/crypto" import (
"fmt"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
)
// Address represents an IPv6 address in the yggdrasil address range. // Address represents an IPv6 address in the yggdrasil address range.
type Address [16]byte type Address [16]byte
@ -128,6 +132,13 @@ func (a *Address) GetNodeIDandMask() (*crypto.NodeID, *crypto.NodeID) {
return &nid, &mask return &nid, &mask
} }
// GetNodeIDLengthString returns a string representation of the known bits of the NodeID, along with the number of known bits, for use with yggdrasil.Dialer's Dial and DialContext functions.
func (a *Address) GetNodeIDLengthString() string {
nid, mask := a.GetNodeIDandMask()
l := mask.PrefixLength()
return fmt.Sprintf("%s/%d", nid.String(), l)
}
// GetNodeIDandMask returns two *NodeID. // GetNodeIDandMask returns two *NodeID.
// The first is a NodeID with all the bits known from the Subnet set to their correct values. // The first is a NodeID with all the bits known from the Subnet set to their correct values.
// The second is a bitmask with 1 bit set for each bit that was known from the Subnet. // The second is a bitmask with 1 bit set for each bit that was known from the Subnet.
@ -156,3 +167,10 @@ func (s *Subnet) GetNodeIDandMask() (*crypto.NodeID, *crypto.NodeID) {
} }
return &nid, &mask return &nid, &mask
} }
// GetNodeIDLengthString returns a string representation of the known bits of the NodeID, along with the number of known bits, for use with yggdrasil.Dialer's Dial and DialContext functions.
func (s *Subnet) GetNodeIDLengthString() string {
nid, mask := s.GetNodeIDandMask()
l := mask.PrefixLength()
return fmt.Sprintf("%s/%d", nid.String(), l)
}

View File

@ -9,6 +9,7 @@ import (
"github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/util" "github.com/yggdrasil-network/yggdrasil-go/src/util"
"github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil"
"github.com/Arceliar/phony" "github.com/Arceliar/phony"
) )
@ -225,7 +226,7 @@ func (tun *TunAdapter) _handlePacket(recvd []byte, err error) {
return return
} }
// Do we have an active connection for this node address? // Do we have an active connection for this node address?
var dstNodeID, dstNodeIDMask *crypto.NodeID var dstString string
session, isIn := tun.addrToConn[dstAddr] session, isIn := tun.addrToConn[dstAddr]
if !isIn || session == nil { if !isIn || session == nil {
session, isIn = tun.subnetToConn[dstSnet] session, isIn = tun.subnetToConn[dstSnet]
@ -233,9 +234,9 @@ func (tun *TunAdapter) _handlePacket(recvd []byte, err error) {
// Neither an address nor a subnet mapping matched, therefore populate // Neither an address nor a subnet mapping matched, therefore populate
// the node ID and mask to commence a search // the node ID and mask to commence a search
if dstAddr.IsValid() { if dstAddr.IsValid() {
dstNodeID, dstNodeIDMask = dstAddr.GetNodeIDandMask() dstString = dstAddr.GetNodeIDLengthString()
} else { } else {
dstNodeID, dstNodeIDMask = dstSnet.GetNodeIDandMask() dstString = dstSnet.GetNodeIDLengthString()
} }
} }
} }
@ -243,27 +244,27 @@ func (tun *TunAdapter) _handlePacket(recvd []byte, err error) {
if !isIn || session == nil { if !isIn || session == nil {
// Check we haven't been given empty node ID, really this shouldn't ever // Check we haven't been given empty node ID, really this shouldn't ever
// happen but just to be sure... // happen but just to be sure...
if dstNodeID == nil || dstNodeIDMask == nil { if dstString == "" {
panic("Given empty dstNodeID and dstNodeIDMask - this shouldn't happen") panic("Given empty dstString - this shouldn't happen")
} }
_, known := tun.dials[*dstNodeID] _, known := tun.dials[dstString]
tun.dials[*dstNodeID] = append(tun.dials[*dstNodeID], bs) tun.dials[dstString] = append(tun.dials[dstString], bs)
for len(tun.dials[*dstNodeID]) > 32 { for len(tun.dials[dstString]) > 32 {
util.PutBytes(tun.dials[*dstNodeID][0]) util.PutBytes(tun.dials[dstString][0])
tun.dials[*dstNodeID] = tun.dials[*dstNodeID][1:] tun.dials[dstString] = tun.dials[dstString][1:]
} }
if !known { if !known {
go func() { go func() {
conn, err := tun.dialer.DialByNodeIDandMask(dstNodeID, dstNodeIDMask) conn, err := tun.dialer.Dial("nodeid", dstString)
tun.Act(nil, func() { tun.Act(nil, func() {
packets := tun.dials[*dstNodeID] packets := tun.dials[dstString]
delete(tun.dials, *dstNodeID) delete(tun.dials, dstString)
if err != nil { if err != nil {
return return
} }
// We've been given a connection so prepare the session wrapper // We've been given a connection so prepare the session wrapper
var tc *tunConn var tc *tunConn
if tc, err = tun._wrap(conn); err != nil { if tc, err = tun._wrap(conn.(*yggdrasil.Conn)); err != nil {
// Something went wrong when storing the connection, typically that // Something went wrong when storing the connection, typically that
// something already exists for this address or subnet // something already exists for this address or subnet
tun.log.Debugln("TUN/TAP iface wrap:", err) tun.log.Debugln("TUN/TAP iface wrap:", err)

View File

@ -52,7 +52,7 @@ type TunAdapter struct {
//mutex sync.RWMutex // Protects the below //mutex sync.RWMutex // Protects the below
addrToConn map[address.Address]*tunConn addrToConn map[address.Address]*tunConn
subnetToConn map[address.Subnet]*tunConn subnetToConn map[address.Subnet]*tunConn
dials map[crypto.NodeID][][]byte // Buffer of packets to send after dialing finishes dials map[string][][]byte // Buffer of packets to send after dialing finishes
isOpen bool isOpen bool
} }
@ -117,7 +117,7 @@ func (tun *TunAdapter) Init(config *config.NodeState, log *log.Logger, listener
tun.dialer = dialer tun.dialer = dialer
tun.addrToConn = make(map[address.Address]*tunConn) tun.addrToConn = make(map[address.Address]*tunConn)
tun.subnetToConn = make(map[address.Subnet]*tunConn) tun.subnetToConn = make(map[address.Subnet]*tunConn)
tun.dials = make(map[crypto.NodeID][][]byte) tun.dials = make(map[string][][]byte)
tun.writer.tun = tun tun.writer.tun = tun
tun.reader.tun = tun tun.reader.tun = tun
} }

View File

@ -1,8 +1,10 @@
package yggdrasil package yggdrasil
import ( import (
"context"
"encoding/hex" "encoding/hex"
"errors" "errors"
"net"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -15,12 +17,18 @@ type Dialer struct {
core *Core core *Core
} }
// TODO DialContext that allows timeouts/cancellation, Dial should just call this with no timeout set in the context
// Dial opens a session to the given node. The first paramter should be "nodeid" // Dial opens a session to the given node. The first paramter should be "nodeid"
// and the second parameter should contain a hexadecimal representation of the // and the second parameter should contain a hexadecimal representation of the
// target node ID. // target node ID. Internally, it uses DialContext with a 6-second timeout.
func (d *Dialer) Dial(network, address string) (*Conn, error) { func (d *Dialer) Dial(network, address string) (net.Conn, error) {
const timeout = 6 * time.Second
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return d.DialContext(ctx, network, address)
}
// DialContext is used internally by Dial, and should only be used with a context that includes a timeout.
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
var nodeID crypto.NodeID var nodeID crypto.NodeID
var nodeMask crypto.NodeID var nodeMask crypto.NodeID
// Process // Process
@ -28,7 +36,7 @@ func (d *Dialer) Dial(network, address string) (*Conn, error) {
case "nodeid": case "nodeid":
// A node ID was provided - we don't need to do anything special with it // A node ID was provided - we don't need to do anything special with it
if tokens := strings.Split(address, "/"); len(tokens) == 2 { if tokens := strings.Split(address, "/"); len(tokens) == 2 {
len, err := strconv.Atoi(tokens[1]) l, err := strconv.Atoi(tokens[1])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -37,7 +45,7 @@ func (d *Dialer) Dial(network, address string) (*Conn, error) {
return nil, err return nil, err
} }
copy(nodeID[:], dest) copy(nodeID[:], dest)
for idx := 0; idx < len; idx++ { for idx := 0; idx < l; idx++ {
nodeMask[idx/8] |= 0x80 >> byte(idx%8) nodeMask[idx/8] |= 0x80 >> byte(idx%8)
} }
} else { } else {
@ -50,7 +58,7 @@ func (d *Dialer) Dial(network, address string) (*Conn, error) {
nodeMask[i] = 0xFF nodeMask[i] = 0xFF
} }
} }
return d.DialByNodeIDandMask(&nodeID, &nodeMask) return d.DialByNodeIDandMask(ctx, &nodeID, &nodeMask)
default: default:
// An unexpected address type was given, so give up // An unexpected address type was given, so give up
return nil, errors.New("unexpected address type") return nil, errors.New("unexpected address type")
@ -59,19 +67,18 @@ func (d *Dialer) Dial(network, address string) (*Conn, error) {
// DialByNodeIDandMask opens a session to the given node based on raw // DialByNodeIDandMask opens a session to the given node based on raw
// NodeID parameters. // NodeID parameters.
func (d *Dialer) DialByNodeIDandMask(nodeID, nodeMask *crypto.NodeID) (*Conn, error) { func (d *Dialer) DialByNodeIDandMask(ctx context.Context, nodeID, nodeMask *crypto.NodeID) (net.Conn, error) {
conn := newConn(d.core, nodeID, nodeMask, nil) conn := newConn(d.core, nodeID, nodeMask, nil)
if err := conn.search(); err != nil { if err := conn.search(); err != nil {
// TODO: make searches take a context, so they can be cancelled early
conn.Close() conn.Close()
return nil, err return nil, err
} }
conn.session.setConn(nil, conn) conn.session.setConn(nil, conn)
t := time.NewTimer(6 * time.Second) // TODO use a context instead
defer t.Stop()
select { select {
case <-conn.session.init: case <-conn.session.init:
return conn, nil return conn, nil
case <-t.C: case <-ctx.Done():
conn.Close() conn.Close()
return nil, errors.New("session handshake timeout") return nil, errors.New("session handshake timeout")
} }