2019-04-20 15:32:27 +00:00
|
|
|
package yggdrasil
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/hex"
|
|
|
|
"errors"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
2019-06-29 21:10:02 +00:00
|
|
|
"time"
|
2019-04-20 15:32:27 +00:00
|
|
|
|
|
|
|
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Dialer represents an Yggdrasil connection dialer.
|
|
|
|
type Dialer struct {
|
|
|
|
core *Core
|
|
|
|
}
|
|
|
|
|
2019-06-28 23:42:31 +00:00
|
|
|
// TODO DialContext that allows timeouts/cancellation, Dial should just call this with no timeout set in the context
|
|
|
|
|
2019-04-20 15:32:27 +00:00
|
|
|
// 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
|
|
|
|
// target node ID.
|
2019-04-26 23:07:57 +00:00
|
|
|
func (d *Dialer) Dial(network, address string) (*Conn, error) {
|
2019-04-20 19:22:58 +00:00
|
|
|
var nodeID crypto.NodeID
|
|
|
|
var nodeMask crypto.NodeID
|
2019-04-20 15:32:27 +00:00
|
|
|
// Process
|
|
|
|
switch network {
|
|
|
|
case "nodeid":
|
|
|
|
// A node ID was provided - we don't need to do anything special with it
|
|
|
|
if tokens := strings.Split(address, "/"); len(tokens) == 2 {
|
|
|
|
len, err := strconv.Atoi(tokens[1])
|
|
|
|
if err != nil {
|
2019-04-26 23:07:57 +00:00
|
|
|
return nil, err
|
2019-04-20 15:32:27 +00:00
|
|
|
}
|
|
|
|
dest, err := hex.DecodeString(tokens[0])
|
|
|
|
if err != nil {
|
2019-04-26 23:07:57 +00:00
|
|
|
return nil, err
|
2019-04-20 15:32:27 +00:00
|
|
|
}
|
|
|
|
copy(nodeID[:], dest)
|
2019-04-22 10:49:47 +00:00
|
|
|
for idx := 0; idx < len; idx++ {
|
2019-04-20 15:32:27 +00:00
|
|
|
nodeMask[idx/8] |= 0x80 >> byte(idx%8)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
dest, err := hex.DecodeString(tokens[0])
|
|
|
|
if err != nil {
|
2019-04-26 23:07:57 +00:00
|
|
|
return nil, err
|
2019-04-20 15:32:27 +00:00
|
|
|
}
|
|
|
|
copy(nodeID[:], dest)
|
|
|
|
for i := range nodeMask {
|
|
|
|
nodeMask[i] = 0xFF
|
|
|
|
}
|
|
|
|
}
|
2019-04-20 19:22:58 +00:00
|
|
|
return d.DialByNodeIDandMask(&nodeID, &nodeMask)
|
2019-04-20 15:32:27 +00:00
|
|
|
default:
|
|
|
|
// An unexpected address type was given, so give up
|
2019-04-26 23:07:57 +00:00
|
|
|
return nil, errors.New("unexpected address type")
|
2019-04-20 15:32:27 +00:00
|
|
|
}
|
2019-04-20 19:22:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// DialByNodeIDandMask opens a session to the given node based on raw
|
|
|
|
// NodeID parameters.
|
2019-04-26 23:07:57 +00:00
|
|
|
func (d *Dialer) DialByNodeIDandMask(nodeID, nodeMask *crypto.NodeID) (*Conn, error) {
|
|
|
|
conn := newConn(d.core, nodeID, nodeMask, nil)
|
2019-06-28 23:42:31 +00:00
|
|
|
if err := conn.search(); err != nil {
|
2019-06-29 21:10:02 +00:00
|
|
|
conn.Close()
|
2019-06-28 23:42:31 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2019-06-29 21:10:02 +00:00
|
|
|
t := time.NewTimer(6 * time.Second) // TODO use a context instead
|
|
|
|
defer t.Stop()
|
|
|
|
select {
|
|
|
|
case <-conn.session.init:
|
2019-08-06 00:11:28 +00:00
|
|
|
conn.session.startWorkers()
|
2019-06-29 21:10:02 +00:00
|
|
|
return conn, nil
|
|
|
|
case <-t.C:
|
|
|
|
conn.Close()
|
|
|
|
return nil, errors.New("session handshake timeout")
|
|
|
|
}
|
2019-04-20 15:32:27 +00:00
|
|
|
}
|