2019-08-05 21:41:48 +03:00
|
|
|
package tunnel
|
|
|
|
|
|
|
|
import (
|
2019-08-07 20:44:33 +03:00
|
|
|
"errors"
|
2019-08-30 22:05:00 +03:00
|
|
|
"strings"
|
2019-08-05 21:41:48 +03:00
|
|
|
"sync"
|
2019-08-15 18:52:16 +03:00
|
|
|
"time"
|
2019-08-05 21:41:48 +03:00
|
|
|
|
2019-08-07 20:44:33 +03:00
|
|
|
"github.com/google/uuid"
|
2019-08-05 21:41:48 +03:00
|
|
|
"github.com/micro/go-micro/transport"
|
2019-08-07 20:44:33 +03:00
|
|
|
"github.com/micro/go-micro/util/log"
|
2019-08-05 21:41:48 +03:00
|
|
|
)
|
|
|
|
|
2019-08-15 18:52:16 +03:00
|
|
|
var (
|
2019-08-15 20:18:58 +03:00
|
|
|
// KeepAliveTime defines time interval we send keepalive messages to outbound links
|
|
|
|
KeepAliveTime = 30 * time.Second
|
|
|
|
// ReconnectTime defines time interval we periodically attempt to reconnect dead links
|
|
|
|
ReconnectTime = 5 * time.Second
|
2019-08-15 18:52:16 +03:00
|
|
|
)
|
|
|
|
|
2019-08-07 20:44:33 +03:00
|
|
|
// tun represents a network tunnel
|
2019-08-05 21:41:48 +03:00
|
|
|
type tun struct {
|
2019-08-07 20:44:33 +03:00
|
|
|
options Options
|
|
|
|
|
2019-08-05 21:41:48 +03:00
|
|
|
sync.RWMutex
|
2019-08-07 20:44:33 +03:00
|
|
|
|
2019-08-30 22:05:00 +03:00
|
|
|
// the unique id for this tunnel
|
|
|
|
id string
|
|
|
|
|
|
|
|
// tunnel token for authentication
|
2019-08-13 22:11:23 +03:00
|
|
|
token string
|
|
|
|
|
2019-08-07 20:44:33 +03:00
|
|
|
// to indicate if we're connected or not
|
2019-08-05 21:41:48 +03:00
|
|
|
connected bool
|
2019-08-07 20:44:33 +03:00
|
|
|
|
|
|
|
// the send channel for all messages
|
|
|
|
send chan *message
|
|
|
|
|
|
|
|
// close channel
|
|
|
|
closed chan bool
|
|
|
|
|
2019-08-30 22:05:00 +03:00
|
|
|
// a map of sessions based on Micro-Tunnel-Channel
|
|
|
|
sessions map[string]*session
|
2019-08-07 20:44:33 +03:00
|
|
|
|
|
|
|
// outbound links
|
|
|
|
links map[string]*link
|
|
|
|
|
|
|
|
// listener
|
|
|
|
listener transport.Listener
|
2019-08-05 21:41:48 +03:00
|
|
|
}
|
|
|
|
|
2019-08-07 20:44:33 +03:00
|
|
|
// create new tunnel on top of a link
|
|
|
|
func newTunnel(opts ...Option) *tun {
|
|
|
|
options := DefaultOptions()
|
2019-08-05 21:41:48 +03:00
|
|
|
for _, o := range opts {
|
|
|
|
o(&options)
|
|
|
|
}
|
|
|
|
|
2019-08-07 20:44:33 +03:00
|
|
|
return &tun{
|
2019-08-30 22:05:00 +03:00
|
|
|
options: options,
|
|
|
|
id: options.Id,
|
|
|
|
token: options.Token,
|
|
|
|
send: make(chan *message, 128),
|
|
|
|
closed: make(chan bool),
|
|
|
|
sessions: make(map[string]*session),
|
|
|
|
links: make(map[string]*link),
|
2019-08-05 21:41:48 +03:00
|
|
|
}
|
2019-08-07 20:44:33 +03:00
|
|
|
}
|
2019-08-05 21:41:48 +03:00
|
|
|
|
2019-08-14 15:26:23 +03:00
|
|
|
// Init initializes tunnel options
|
|
|
|
func (t *tun) Init(opts ...Option) error {
|
2019-08-22 18:31:37 +03:00
|
|
|
t.Lock()
|
|
|
|
defer t.Unlock()
|
2019-08-14 15:26:23 +03:00
|
|
|
for _, o := range opts {
|
|
|
|
o(&t.options)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-08-30 22:05:00 +03:00
|
|
|
// getSession returns a session from the internal session map.
|
|
|
|
// It does this based on the Micro-Tunnel-Channel and Micro-Tunnel-Session
|
|
|
|
func (t *tun) getSession(channel, session string) (*session, bool) {
|
|
|
|
// get the session
|
2019-08-07 20:44:33 +03:00
|
|
|
t.RLock()
|
2019-08-30 22:05:00 +03:00
|
|
|
s, ok := t.sessions[channel+session]
|
2019-08-07 20:44:33 +03:00
|
|
|
t.RUnlock()
|
|
|
|
return s, ok
|
2019-08-05 21:41:48 +03:00
|
|
|
}
|
|
|
|
|
2019-08-30 22:05:00 +03:00
|
|
|
// newSession creates a new session and saves it
|
|
|
|
func (t *tun) newSession(channel, sessionId string) (*session, bool) {
|
|
|
|
// new session
|
|
|
|
s := &session{
|
|
|
|
id: t.id,
|
|
|
|
channel: channel,
|
|
|
|
session: sessionId,
|
2019-08-07 20:44:33 +03:00
|
|
|
closed: make(chan bool),
|
|
|
|
recv: make(chan *message, 128),
|
|
|
|
send: t.send,
|
|
|
|
wait: make(chan bool),
|
2019-08-30 22:05:00 +03:00
|
|
|
errChan: make(chan error, 1),
|
2019-08-07 20:44:33 +03:00
|
|
|
}
|
|
|
|
|
2019-08-30 22:05:00 +03:00
|
|
|
// save session
|
2019-08-07 20:44:33 +03:00
|
|
|
t.Lock()
|
2019-08-30 22:05:00 +03:00
|
|
|
_, ok := t.sessions[channel+sessionId]
|
2019-08-07 20:44:33 +03:00
|
|
|
if ok {
|
2019-08-30 22:05:00 +03:00
|
|
|
// session already exists
|
2019-08-07 20:44:33 +03:00
|
|
|
t.Unlock()
|
|
|
|
return nil, false
|
|
|
|
}
|
2019-08-14 03:23:03 +03:00
|
|
|
|
2019-08-30 22:05:00 +03:00
|
|
|
t.sessions[channel+sessionId] = s
|
2019-08-07 20:44:33 +03:00
|
|
|
t.Unlock()
|
|
|
|
|
2019-08-30 22:05:00 +03:00
|
|
|
// return session
|
2019-08-07 20:44:33 +03:00
|
|
|
return s, true
|
2019-08-05 21:41:48 +03:00
|
|
|
}
|
|
|
|
|
2019-08-07 20:44:33 +03:00
|
|
|
// TODO: use tunnel id as part of the session
|
2019-08-30 22:05:00 +03:00
|
|
|
func (t *tun) newSessionId() string {
|
2019-08-07 20:44:33 +03:00
|
|
|
return uuid.New().String()
|
2019-08-06 13:45:25 +03:00
|
|
|
}
|
|
|
|
|
2019-08-15 20:18:58 +03:00
|
|
|
// monitor monitors outbound links and attempts to reconnect to the failed ones
|
2019-08-15 18:52:16 +03:00
|
|
|
func (t *tun) monitor() {
|
2019-08-15 20:18:58 +03:00
|
|
|
reconnect := time.NewTicker(ReconnectTime)
|
|
|
|
defer reconnect.Stop()
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-t.closed:
|
|
|
|
return
|
|
|
|
case <-reconnect.C:
|
|
|
|
for _, node := range t.options.Nodes {
|
2019-08-16 17:40:35 +03:00
|
|
|
t.Lock()
|
2019-08-15 20:18:58 +03:00
|
|
|
if _, ok := t.links[node]; !ok {
|
2019-08-15 21:24:24 +03:00
|
|
|
link, err := t.setupLink(node)
|
2019-08-15 20:18:58 +03:00
|
|
|
if err != nil {
|
2019-08-16 17:18:34 +03:00
|
|
|
log.Debugf("Tunnel failed to setup node link to %s: %v", node, err)
|
|
|
|
t.Unlock()
|
2019-08-15 20:18:58 +03:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
t.links[node] = link
|
|
|
|
}
|
2019-08-16 17:40:35 +03:00
|
|
|
t.Unlock()
|
2019-08-15 20:18:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-15 18:52:16 +03:00
|
|
|
}
|
|
|
|
|
2019-08-30 22:05:00 +03:00
|
|
|
// process outgoing messages sent by all local sessions
|
2019-08-07 20:44:33 +03:00
|
|
|
func (t *tun) process() {
|
|
|
|
// manage the send buffer
|
2019-08-30 22:05:00 +03:00
|
|
|
// all pseudo sessions throw everything down this
|
2019-08-07 20:44:33 +03:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case msg := <-t.send:
|
2019-08-10 18:37:49 +03:00
|
|
|
newMsg := &transport.Message{
|
|
|
|
Header: make(map[string]string),
|
2019-08-07 20:44:33 +03:00
|
|
|
Body: msg.data.Body,
|
|
|
|
}
|
|
|
|
|
2019-08-10 18:37:49 +03:00
|
|
|
for k, v := range msg.data.Header {
|
|
|
|
newMsg.Header[k] = v
|
2019-08-08 14:45:37 +03:00
|
|
|
}
|
|
|
|
|
2019-08-20 19:20:21 +03:00
|
|
|
// set message head
|
2019-08-29 14:42:27 +03:00
|
|
|
newMsg.Header["Micro-Tunnel"] = msg.typ
|
2019-08-20 19:20:21 +03:00
|
|
|
|
2019-08-07 20:44:33 +03:00
|
|
|
// set the tunnel id on the outgoing message
|
2019-08-10 18:37:49 +03:00
|
|
|
newMsg.Header["Micro-Tunnel-Id"] = msg.id
|
2019-08-07 20:44:33 +03:00
|
|
|
|
2019-08-30 22:05:00 +03:00
|
|
|
// set the tunnel channel on the outgoing message
|
|
|
|
newMsg.Header["Micro-Tunnel-Channel"] = msg.channel
|
|
|
|
|
2019-08-07 20:44:33 +03:00
|
|
|
// set the session id
|
2019-08-10 18:37:49 +03:00
|
|
|
newMsg.Header["Micro-Tunnel-Session"] = msg.session
|
2019-08-07 20:44:33 +03:00
|
|
|
|
2019-08-14 15:00:10 +03:00
|
|
|
// set the tunnel token
|
|
|
|
newMsg.Header["Micro-Tunnel-Token"] = t.token
|
|
|
|
|
2019-08-07 20:44:33 +03:00
|
|
|
// send the message via the interface
|
2019-08-15 18:52:16 +03:00
|
|
|
t.Lock()
|
2019-08-29 14:42:27 +03:00
|
|
|
|
2019-08-11 20:11:33 +03:00
|
|
|
if len(t.links) == 0 {
|
2019-08-15 20:18:58 +03:00
|
|
|
log.Debugf("No links to send to")
|
2019-08-11 20:11:33 +03:00
|
|
|
}
|
2019-08-29 14:42:27 +03:00
|
|
|
|
2019-08-30 22:05:00 +03:00
|
|
|
var sent bool
|
|
|
|
var err error
|
|
|
|
|
2019-08-15 20:18:58 +03:00
|
|
|
for node, link := range t.links {
|
2019-08-29 01:12:22 +03:00
|
|
|
// if the link is not connected skip it
|
|
|
|
if !link.connected {
|
|
|
|
log.Debugf("Link for node %s not connected", node)
|
2019-08-30 22:05:00 +03:00
|
|
|
err = errors.New("link not connected")
|
2019-08-29 01:12:22 +03:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-08-29 14:42:27 +03:00
|
|
|
// if we're picking the link check the id
|
|
|
|
// this is where we explicitly set the link
|
|
|
|
// in a message received via the listen method
|
|
|
|
if len(msg.link) > 0 && link.id != msg.link {
|
2019-08-30 22:05:00 +03:00
|
|
|
err = errors.New("link not found")
|
2019-08-29 14:42:27 +03:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-08-29 01:12:22 +03:00
|
|
|
// if the link was a loopback accepted connection
|
|
|
|
// and the message is being sent outbound via
|
|
|
|
// a dialled connection don't use this link
|
2019-08-14 19:14:39 +03:00
|
|
|
if link.loopback && msg.outbound {
|
2019-08-30 22:05:00 +03:00
|
|
|
err = errors.New("link is loopback")
|
2019-08-14 19:14:39 +03:00
|
|
|
continue
|
|
|
|
}
|
2019-08-29 01:12:22 +03:00
|
|
|
|
|
|
|
// if the message was being returned by the loopback listener
|
|
|
|
// send it back up the loopback link only
|
|
|
|
if msg.loopback && !link.loopback {
|
2019-08-30 22:05:00 +03:00
|
|
|
err = errors.New("link is not loopback")
|
2019-08-29 01:12:22 +03:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-08-29 14:42:27 +03:00
|
|
|
// send the message via the current link
|
2019-08-15 20:18:58 +03:00
|
|
|
log.Debugf("Sending %+v to %s", newMsg, node)
|
2019-08-30 22:05:00 +03:00
|
|
|
if errr := link.Send(newMsg); errr != nil {
|
|
|
|
log.Debugf("Tunnel error sending %+v to %s: %v", newMsg, node, errr)
|
|
|
|
err = errors.New(errr.Error())
|
2019-08-16 17:18:34 +03:00
|
|
|
delete(t.links, node)
|
|
|
|
continue
|
2019-08-15 18:52:16 +03:00
|
|
|
}
|
2019-08-30 22:05:00 +03:00
|
|
|
// is sent
|
|
|
|
sent = true
|
2019-08-07 20:44:33 +03:00
|
|
|
}
|
2019-08-29 14:42:27 +03:00
|
|
|
|
2019-08-15 18:52:16 +03:00
|
|
|
t.Unlock()
|
2019-08-30 22:05:00 +03:00
|
|
|
|
|
|
|
var gerr error
|
|
|
|
if !sent {
|
|
|
|
gerr = err
|
|
|
|
}
|
|
|
|
|
|
|
|
// return error non blocking
|
|
|
|
select {
|
|
|
|
case msg.errChan <- gerr:
|
|
|
|
default:
|
|
|
|
}
|
2019-08-07 20:44:33 +03:00
|
|
|
case <-t.closed:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2019-08-05 21:41:48 +03:00
|
|
|
}
|
|
|
|
|
2019-08-07 20:44:33 +03:00
|
|
|
// process incoming messages
|
2019-08-14 19:14:39 +03:00
|
|
|
func (t *tun) listen(link *link) {
|
2019-08-29 01:12:22 +03:00
|
|
|
// remove the link on exit
|
|
|
|
defer func() {
|
|
|
|
log.Debugf("Tunnel deleting connection from %s", link.Remote())
|
|
|
|
t.Lock()
|
|
|
|
delete(t.links, link.Remote())
|
|
|
|
t.Unlock()
|
|
|
|
}()
|
|
|
|
|
|
|
|
// let us know if its a loopback
|
|
|
|
var loopback bool
|
|
|
|
|
2019-08-07 20:44:33 +03:00
|
|
|
for {
|
|
|
|
// process anything via the net interface
|
|
|
|
msg := new(transport.Message)
|
2019-08-29 01:12:22 +03:00
|
|
|
if err := link.Recv(msg); err != nil {
|
2019-08-16 17:18:34 +03:00
|
|
|
log.Debugf("Tunnel link %s receive error: %#v", link.Remote(), err)
|
2019-08-07 20:44:33 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-08-30 22:05:00 +03:00
|
|
|
// always ensure we have the correct auth token
|
|
|
|
// TODO: segment the tunnel based on token
|
|
|
|
// e.g use it as the basis
|
|
|
|
token := msg.Header["Micro-Tunnel-Token"]
|
|
|
|
if token != t.token {
|
|
|
|
log.Debugf("Tunnel link %s received invalid token %s", token)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-08-14 15:00:10 +03:00
|
|
|
switch msg.Header["Micro-Tunnel"] {
|
|
|
|
case "connect":
|
2019-08-15 18:52:16 +03:00
|
|
|
log.Debugf("Tunnel link %s received connect message", link.Remote())
|
2019-08-30 22:05:00 +03:00
|
|
|
|
|
|
|
id := msg.Header["Micro-Tunnel-Id"]
|
2019-08-14 03:23:03 +03:00
|
|
|
|
2019-08-13 22:11:23 +03:00
|
|
|
// are we connecting to ourselves?
|
2019-08-30 22:05:00 +03:00
|
|
|
if id == t.id {
|
2019-08-14 19:14:39 +03:00
|
|
|
link.loopback = true
|
2019-08-29 01:12:22 +03:00
|
|
|
loopback = true
|
2019-08-13 22:11:23 +03:00
|
|
|
}
|
2019-08-20 19:20:21 +03:00
|
|
|
|
2019-08-29 01:12:22 +03:00
|
|
|
// set as connected
|
|
|
|
link.connected = true
|
|
|
|
|
|
|
|
// save the link once connected
|
|
|
|
t.Lock()
|
|
|
|
t.links[link.Remote()] = link
|
|
|
|
t.Unlock()
|
|
|
|
|
2019-08-20 19:20:21 +03:00
|
|
|
// nothing more to do
|
2019-08-14 15:00:10 +03:00
|
|
|
continue
|
2019-08-13 22:11:23 +03:00
|
|
|
case "close":
|
2019-08-15 18:52:16 +03:00
|
|
|
log.Debugf("Tunnel link %s closing connection", link.Remote())
|
2019-08-13 22:11:23 +03:00
|
|
|
// TODO: handle the close message
|
|
|
|
// maybe report io.EOF or kill the link
|
2019-08-29 14:42:27 +03:00
|
|
|
return
|
2019-08-15 20:18:58 +03:00
|
|
|
case "keepalive":
|
2019-08-15 18:52:16 +03:00
|
|
|
log.Debugf("Tunnel link %s received keepalive", link.Remote())
|
2019-08-22 18:31:37 +03:00
|
|
|
t.Lock()
|
2019-08-29 14:42:27 +03:00
|
|
|
// save the keepalive
|
2019-08-15 18:52:16 +03:00
|
|
|
link.lastKeepAlive = time.Now()
|
2019-08-22 18:31:37 +03:00
|
|
|
t.Unlock()
|
2019-08-15 18:52:16 +03:00
|
|
|
continue
|
2019-08-20 19:20:21 +03:00
|
|
|
case "message":
|
|
|
|
// process message
|
|
|
|
log.Debugf("Received %+v from %s", msg, link.Remote())
|
|
|
|
default:
|
|
|
|
// blackhole it
|
|
|
|
continue
|
2019-08-07 20:44:33 +03:00
|
|
|
}
|
|
|
|
|
2019-08-29 01:12:22 +03:00
|
|
|
// if its not connected throw away the link
|
|
|
|
if !link.connected {
|
2019-08-29 14:42:27 +03:00
|
|
|
log.Debugf("Tunnel link %s not connected", link.id)
|
2019-08-29 01:12:22 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-08-07 20:44:33 +03:00
|
|
|
// the tunnel id
|
|
|
|
id := msg.Header["Micro-Tunnel-Id"]
|
2019-08-30 22:05:00 +03:00
|
|
|
// the tunnel channel
|
|
|
|
channel := msg.Header["Micro-Tunnel-Channel"]
|
2019-08-07 20:44:33 +03:00
|
|
|
// the session id
|
2019-08-30 22:05:00 +03:00
|
|
|
sessionId := msg.Header["Micro-Tunnel-Session"]
|
2019-08-07 20:44:33 +03:00
|
|
|
|
2019-08-30 22:05:00 +03:00
|
|
|
// strip tunnel message header
|
|
|
|
for k, _ := range msg.Header {
|
|
|
|
if strings.HasPrefix(k, "Micro-Tunnel") {
|
|
|
|
delete(msg.Header, k)
|
|
|
|
}
|
|
|
|
}
|
2019-08-20 19:20:21 +03:00
|
|
|
|
2019-08-07 20:44:33 +03:00
|
|
|
// if the session id is blank there's nothing we can do
|
|
|
|
// TODO: check this is the case, is there any reason
|
|
|
|
// why we'd have a blank session? Is the tunnel
|
|
|
|
// used for some other purpose?
|
2019-08-30 22:05:00 +03:00
|
|
|
if len(channel) == 0 || len(sessionId) == 0 {
|
2019-08-07 20:44:33 +03:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-08-30 22:05:00 +03:00
|
|
|
var s *session
|
2019-08-07 20:44:33 +03:00
|
|
|
var exists bool
|
|
|
|
|
2019-08-29 01:12:22 +03:00
|
|
|
// If its a loopback connection then we've enabled link direction
|
|
|
|
// listening side is used for listening, the dialling side for dialling
|
2019-08-13 22:11:23 +03:00
|
|
|
switch {
|
2019-08-29 01:12:22 +03:00
|
|
|
case loopback:
|
2019-08-30 22:05:00 +03:00
|
|
|
s, exists = t.getSession(channel, "listener")
|
2019-08-13 22:11:23 +03:00
|
|
|
default:
|
2019-08-30 22:05:00 +03:00
|
|
|
// get the session based on the tunnel id and session
|
2019-08-13 22:11:23 +03:00
|
|
|
// this could be something we dialed in which case
|
|
|
|
// we have a session for it otherwise its a listener
|
2019-08-30 22:05:00 +03:00
|
|
|
s, exists = t.getSession(channel, sessionId)
|
2019-08-14 03:23:03 +03:00
|
|
|
if !exists {
|
|
|
|
// try get it based on just the tunnel id
|
|
|
|
// the assumption here is that a listener
|
|
|
|
// has no session but its set a listener session
|
2019-08-30 22:05:00 +03:00
|
|
|
s, exists = t.getSession(channel, "listener")
|
2019-08-14 03:23:03 +03:00
|
|
|
}
|
2019-08-07 20:44:33 +03:00
|
|
|
}
|
2019-08-29 01:12:22 +03:00
|
|
|
|
2019-08-30 22:05:00 +03:00
|
|
|
// bail if no session has been found
|
2019-08-07 20:44:33 +03:00
|
|
|
if !exists {
|
2019-08-30 22:05:00 +03:00
|
|
|
log.Debugf("Tunnel skipping no session exists")
|
2019-08-07 20:44:33 +03:00
|
|
|
// drop it, we don't care about
|
|
|
|
// messages we don't know about
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-08-30 22:05:00 +03:00
|
|
|
log.Debugf("Tunnel using session %s %s", s.channel, s.session)
|
|
|
|
|
|
|
|
// is the session closed?
|
2019-08-07 20:44:33 +03:00
|
|
|
select {
|
|
|
|
case <-s.closed:
|
|
|
|
// closed
|
2019-08-30 22:05:00 +03:00
|
|
|
delete(t.sessions, channel)
|
2019-08-07 20:44:33 +03:00
|
|
|
continue
|
|
|
|
default:
|
|
|
|
// process
|
|
|
|
}
|
|
|
|
|
2019-08-30 22:05:00 +03:00
|
|
|
// is the session new?
|
2019-08-07 20:44:33 +03:00
|
|
|
select {
|
2019-08-30 22:05:00 +03:00
|
|
|
// if its new the session is actually blocked waiting
|
2019-08-07 20:44:33 +03:00
|
|
|
// for a connection. so we check if its waiting.
|
|
|
|
case <-s.wait:
|
|
|
|
// if its waiting e.g its new then we close it
|
|
|
|
default:
|
2019-08-30 22:05:00 +03:00
|
|
|
// set remote address of the session
|
2019-08-07 20:44:33 +03:00
|
|
|
s.remote = msg.Header["Remote"]
|
|
|
|
close(s.wait)
|
|
|
|
}
|
|
|
|
|
|
|
|
// construct a new transport message
|
|
|
|
tmsg := &transport.Message{
|
|
|
|
Header: msg.Header,
|
|
|
|
Body: msg.Body,
|
|
|
|
}
|
|
|
|
|
|
|
|
// construct the internal message
|
|
|
|
imsg := &message{
|
2019-08-29 01:12:22 +03:00
|
|
|
id: id,
|
2019-08-30 22:05:00 +03:00
|
|
|
channel: channel,
|
|
|
|
session: sessionId,
|
2019-08-29 01:12:22 +03:00
|
|
|
data: tmsg,
|
2019-08-29 14:42:27 +03:00
|
|
|
link: link.id,
|
2019-08-29 01:12:22 +03:00
|
|
|
loopback: loopback,
|
2019-08-30 22:05:00 +03:00
|
|
|
errChan: make(chan error, 1),
|
2019-08-07 20:44:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// append to recv backlog
|
|
|
|
// we don't block if we can't pass it on
|
|
|
|
select {
|
|
|
|
case s.recv <- imsg:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
2019-08-05 21:41:48 +03:00
|
|
|
}
|
|
|
|
|
2019-08-15 20:18:58 +03:00
|
|
|
// keepalive periodically sends keepalive messages to link
|
2019-08-15 18:52:16 +03:00
|
|
|
func (t *tun) keepalive(link *link) {
|
2019-08-15 20:18:58 +03:00
|
|
|
keepalive := time.NewTicker(KeepAliveTime)
|
|
|
|
defer keepalive.Stop()
|
2019-08-15 18:52:16 +03:00
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-t.closed:
|
|
|
|
return
|
2019-08-15 20:18:58 +03:00
|
|
|
case <-keepalive.C:
|
|
|
|
// send keepalive message
|
|
|
|
log.Debugf("Tunnel sending keepalive to link: %v", link.Remote())
|
2019-08-15 18:52:16 +03:00
|
|
|
if err := link.Send(&transport.Message{
|
|
|
|
Header: map[string]string{
|
2019-08-15 20:18:58 +03:00
|
|
|
"Micro-Tunnel": "keepalive",
|
2019-08-30 22:05:00 +03:00
|
|
|
"Micro-Tunnel-Id": t.id,
|
2019-08-15 18:52:16 +03:00
|
|
|
"Micro-Tunnel-Token": t.token,
|
|
|
|
},
|
|
|
|
}); err != nil {
|
2019-08-15 20:18:58 +03:00
|
|
|
log.Debugf("Error sending keepalive to link %v: %v", link.Remote(), err)
|
2019-08-16 17:18:34 +03:00
|
|
|
t.Lock()
|
|
|
|
delete(t.links, link.Remote())
|
|
|
|
t.Unlock()
|
|
|
|
return
|
2019-08-15 18:52:16 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-15 21:24:24 +03:00
|
|
|
// setupLink connects to node and returns link if successful
|
|
|
|
// It returns error if the link failed to be established
|
|
|
|
func (t *tun) setupLink(node string) (*link, error) {
|
2019-08-29 18:58:07 +03:00
|
|
|
log.Debugf("Tunnel setting up link: %s", node)
|
2019-08-15 20:18:58 +03:00
|
|
|
c, err := t.options.Transport.Dial(node)
|
|
|
|
if err != nil {
|
|
|
|
log.Debugf("Tunnel failed to connect to %s: %v", node, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
log.Debugf("Tunnel connected to %s", node)
|
|
|
|
|
|
|
|
if err := c.Send(&transport.Message{
|
|
|
|
Header: map[string]string{
|
|
|
|
"Micro-Tunnel": "connect",
|
2019-08-30 22:05:00 +03:00
|
|
|
"Micro-Tunnel-Id": t.id,
|
2019-08-15 20:18:58 +03:00
|
|
|
"Micro-Tunnel-Token": t.token,
|
|
|
|
},
|
|
|
|
}); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-08-29 01:12:22 +03:00
|
|
|
// create a new link
|
2019-08-29 14:42:27 +03:00
|
|
|
link := newLink(c)
|
|
|
|
link.connected = true
|
|
|
|
// we made the outbound connection
|
|
|
|
// and sent the connect message
|
2019-08-15 20:18:58 +03:00
|
|
|
|
|
|
|
// process incoming messages
|
|
|
|
go t.listen(link)
|
|
|
|
|
|
|
|
// start keepalive monitor
|
|
|
|
go t.keepalive(link)
|
|
|
|
|
|
|
|
return link, nil
|
|
|
|
}
|
|
|
|
|
2019-08-14 15:26:23 +03:00
|
|
|
// connect the tunnel to all the nodes and listen for incoming tunnel connections
|
2019-08-07 20:44:33 +03:00
|
|
|
func (t *tun) connect() error {
|
|
|
|
l, err := t.options.Transport.Listen(t.options.Address)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// save the listener
|
|
|
|
t.listener = l
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
// accept inbound connections
|
|
|
|
err := l.Accept(func(sock transport.Socket) {
|
2019-08-08 15:15:30 +03:00
|
|
|
log.Debugf("Tunnel accepted connection from %s", sock.Remote())
|
2019-08-29 01:12:22 +03:00
|
|
|
|
|
|
|
// create a new link
|
2019-08-29 14:42:27 +03:00
|
|
|
link := newLink(sock)
|
2019-08-07 20:44:33 +03:00
|
|
|
|
2019-08-29 01:12:22 +03:00
|
|
|
// listen for inbound messages.
|
|
|
|
// only save the link once connected.
|
|
|
|
// we do this inside liste
|
2019-08-14 19:14:39 +03:00
|
|
|
t.listen(link)
|
2019-08-07 20:44:33 +03:00
|
|
|
})
|
|
|
|
|
2019-08-29 14:42:27 +03:00
|
|
|
t.RLock()
|
|
|
|
defer t.RUnlock()
|
2019-08-07 20:44:33 +03:00
|
|
|
|
|
|
|
// still connected but the tunnel died
|
|
|
|
if err != nil && t.connected {
|
|
|
|
log.Logf("Tunnel listener died: %v", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
for _, node := range t.options.Nodes {
|
2019-08-08 15:15:30 +03:00
|
|
|
// skip zero length nodes
|
|
|
|
if len(node) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-08-15 20:18:58 +03:00
|
|
|
// connect to node and return link
|
2019-08-15 21:24:24 +03:00
|
|
|
link, err := t.setupLink(node)
|
2019-08-07 20:44:33 +03:00
|
|
|
if err != nil {
|
2019-08-15 20:18:58 +03:00
|
|
|
log.Debugf("Tunnel failed to establish node link to %s: %v", node, err)
|
2019-08-07 20:44:33 +03:00
|
|
|
continue
|
|
|
|
}
|
2019-08-29 01:12:22 +03:00
|
|
|
|
2019-08-07 20:44:33 +03:00
|
|
|
// save the link
|
2019-08-15 20:18:58 +03:00
|
|
|
t.links[node] = link
|
2019-08-07 20:44:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// process outbound messages to be sent
|
|
|
|
// process sends to all links
|
|
|
|
go t.process()
|
|
|
|
|
2019-08-15 20:18:58 +03:00
|
|
|
// monitor links
|
|
|
|
go t.monitor()
|
|
|
|
|
2019-08-05 21:41:48 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-08-14 15:26:23 +03:00
|
|
|
// Connect the tunnel
|
|
|
|
func (t *tun) Connect() error {
|
|
|
|
t.Lock()
|
|
|
|
defer t.Unlock()
|
|
|
|
|
|
|
|
// already connected
|
|
|
|
if t.connected {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// send the connect message
|
|
|
|
if err := t.connect(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// set as connected
|
|
|
|
t.connected = true
|
|
|
|
// create new close channel
|
|
|
|
t.closed = make(chan bool)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-08-07 20:44:33 +03:00
|
|
|
func (t *tun) close() error {
|
|
|
|
// close all the links
|
2019-08-15 20:18:58 +03:00
|
|
|
for node, link := range t.links {
|
2019-08-07 20:44:33 +03:00
|
|
|
link.Send(&transport.Message{
|
|
|
|
Header: map[string]string{
|
2019-08-14 15:00:10 +03:00
|
|
|
"Micro-Tunnel": "close",
|
2019-08-30 22:05:00 +03:00
|
|
|
"Micro-Tunnel-Id": t.id,
|
2019-08-14 15:00:10 +03:00
|
|
|
"Micro-Tunnel-Token": t.token,
|
2019-08-07 20:44:33 +03:00
|
|
|
},
|
|
|
|
})
|
|
|
|
link.Close()
|
2019-08-15 20:18:58 +03:00
|
|
|
delete(t.links, node)
|
2019-08-07 20:44:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// close the listener
|
|
|
|
return t.listener.Close()
|
2019-08-05 21:41:48 +03:00
|
|
|
}
|
|
|
|
|
2019-08-21 14:55:10 +03:00
|
|
|
func (t *tun) Address() string {
|
|
|
|
t.RLock()
|
|
|
|
defer t.RUnlock()
|
|
|
|
|
|
|
|
if !t.connected {
|
|
|
|
return t.options.Address
|
|
|
|
}
|
|
|
|
|
|
|
|
return t.listener.Addr()
|
|
|
|
}
|
|
|
|
|
2019-08-07 20:44:33 +03:00
|
|
|
// Close the tunnel
|
|
|
|
func (t *tun) Close() error {
|
|
|
|
t.Lock()
|
|
|
|
defer t.Unlock()
|
|
|
|
|
|
|
|
if !t.connected {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-08-05 21:41:48 +03:00
|
|
|
select {
|
|
|
|
case <-t.closed:
|
2019-08-07 20:44:33 +03:00
|
|
|
return nil
|
2019-08-05 21:41:48 +03:00
|
|
|
default:
|
2019-08-30 22:05:00 +03:00
|
|
|
// close all the sessions
|
|
|
|
for id, s := range t.sessions {
|
2019-08-07 20:44:33 +03:00
|
|
|
s.Close()
|
2019-08-30 22:05:00 +03:00
|
|
|
delete(t.sessions, id)
|
2019-08-07 20:44:33 +03:00
|
|
|
}
|
|
|
|
// close the connection
|
|
|
|
close(t.closed)
|
|
|
|
t.connected = false
|
|
|
|
|
|
|
|
// send a close message
|
|
|
|
// we don't close the link
|
|
|
|
// just the tunnel
|
|
|
|
return t.close()
|
2019-08-05 21:41:48 +03:00
|
|
|
}
|
2019-08-07 20:44:33 +03:00
|
|
|
|
|
|
|
return nil
|
2019-08-05 21:41:48 +03:00
|
|
|
}
|
|
|
|
|
2019-08-07 20:44:33 +03:00
|
|
|
// Dial an address
|
2019-08-30 22:05:00 +03:00
|
|
|
func (t *tun) Dial(channel string) (Session, error) {
|
|
|
|
log.Debugf("Tunnel dialing %s", channel)
|
|
|
|
c, ok := t.newSession(channel, t.newSessionId())
|
2019-08-07 20:44:33 +03:00
|
|
|
if !ok {
|
2019-08-30 22:05:00 +03:00
|
|
|
return nil, errors.New("error dialing " + channel)
|
2019-08-07 20:44:33 +03:00
|
|
|
}
|
|
|
|
// set remote
|
2019-08-30 22:05:00 +03:00
|
|
|
c.remote = channel
|
2019-08-07 20:44:33 +03:00
|
|
|
// set local
|
|
|
|
c.local = "local"
|
2019-08-30 22:05:00 +03:00
|
|
|
// outbound session
|
2019-08-14 19:14:39 +03:00
|
|
|
c.outbound = true
|
2019-08-07 20:44:33 +03:00
|
|
|
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Accept a connection on the address
|
2019-08-30 22:05:00 +03:00
|
|
|
func (t *tun) Listen(channel string) (Listener, error) {
|
|
|
|
log.Debugf("Tunnel listening on %s", channel)
|
|
|
|
// create a new session by hashing the address
|
|
|
|
c, ok := t.newSession(channel, "listener")
|
2019-08-07 20:44:33 +03:00
|
|
|
if !ok {
|
2019-08-30 22:05:00 +03:00
|
|
|
return nil, errors.New("already listening on " + channel)
|
2019-08-07 20:44:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// set remote. it will be replaced by the first message received
|
|
|
|
c.remote = "remote"
|
|
|
|
// set local
|
2019-08-30 22:05:00 +03:00
|
|
|
c.local = channel
|
2019-08-07 20:44:33 +03:00
|
|
|
|
|
|
|
tl := &tunListener{
|
2019-08-30 22:05:00 +03:00
|
|
|
channel: channel,
|
2019-08-07 20:44:33 +03:00
|
|
|
// the accept channel
|
2019-08-30 22:05:00 +03:00
|
|
|
accept: make(chan *session, 128),
|
2019-08-07 20:44:33 +03:00
|
|
|
// the channel to close
|
|
|
|
closed: make(chan bool),
|
2019-08-08 17:20:53 +03:00
|
|
|
// tunnel closed channel
|
|
|
|
tunClosed: t.closed,
|
2019-08-30 22:05:00 +03:00
|
|
|
// the listener session
|
|
|
|
session: c,
|
2019-08-07 20:44:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// this kicks off the internal message processor
|
2019-08-30 22:05:00 +03:00
|
|
|
// for the listener so it can create pseudo sessions
|
2019-08-07 20:44:33 +03:00
|
|
|
// per session if they do not exist or pass messages
|
|
|
|
// to the existign sessions
|
|
|
|
go tl.process()
|
|
|
|
|
|
|
|
// return the listener
|
|
|
|
return tl, nil
|
2019-08-05 21:41:48 +03:00
|
|
|
}
|
2019-08-20 19:21:35 +03:00
|
|
|
|
|
|
|
func (t *tun) String() string {
|
|
|
|
return "mucp"
|
|
|
|
}
|