mirror of
https://github.com/lwch/natpass.git
synced 2024-04-21 12:41:54 +00:00
实现多路复用
This commit is contained in:
@@ -1,187 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"natpass/code/client/global"
|
||||
"natpass/code/network"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lwch/logging"
|
||||
"github.com/lwch/runtime"
|
||||
)
|
||||
|
||||
// Client client
|
||||
type Client struct {
|
||||
sync.RWMutex
|
||||
cfg *global.Configure
|
||||
conn *network.Conn
|
||||
links map[string]*link
|
||||
}
|
||||
|
||||
// New create client
|
||||
func New(cfg *global.Configure, conn *network.Conn) *Client {
|
||||
return &Client{
|
||||
cfg: cfg,
|
||||
conn: conn,
|
||||
links: make(map[string]*link),
|
||||
}
|
||||
}
|
||||
|
||||
// Run main loop
|
||||
func (c *Client) Run() {
|
||||
err := c.writeHandshake()
|
||||
runtime.Assert(err)
|
||||
logging.Info("%s connected", c.cfg.Server)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
go c.keepalive(ctx)
|
||||
|
||||
for _, t := range c.cfg.Tunnels {
|
||||
if t.Type == "tcp" {
|
||||
go c.handleTcpTunnel(ctx, t)
|
||||
} else {
|
||||
go c.handleUdpTunnel(ctx, t)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
msg, err := c.conn.ReadMessage(time.Second)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "i/o timeout") {
|
||||
continue
|
||||
}
|
||||
logging.Error("read message: %v", err)
|
||||
return
|
||||
}
|
||||
switch msg.GetXType() {
|
||||
case network.Msg_connect_req:
|
||||
c.handleConnect(ctx, msg.GetFrom(), msg.GetTo(), msg.GetCreq())
|
||||
case network.Msg_disconnect:
|
||||
c.handleDisconnect(msg.GetXDisconnect())
|
||||
case network.Msg_forward:
|
||||
c.handleData(msg.GetXData())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handleTcpTunnel local listen to tcp tunnel
|
||||
func (c *Client) handleTcpTunnel(ctx context.Context, t global.Tunnel) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logging.Error("close tcp tunnel: %s, err=%v", t.Name, err)
|
||||
}
|
||||
}()
|
||||
l, err := net.ListenTCP("tcp", &net.TCPAddr{
|
||||
IP: net.ParseIP(t.LocalAddr),
|
||||
Port: int(t.LocalPort),
|
||||
})
|
||||
runtime.Assert(err)
|
||||
defer l.Close()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
logging.Error("accept from %s tunnel, err=%v", t.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
id, err := runtime.UUID(16, "0123456789abcdef")
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
logging.Error("generate link id failed, err=%v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
link := newLink(id, t.Name, t.Target, c, conn)
|
||||
c.sendConnect(link.id, t)
|
||||
|
||||
c.Lock()
|
||||
c.links[link.id] = link
|
||||
c.Unlock()
|
||||
|
||||
go link.loop(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) keepalive(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
time.Sleep(10 * time.Second)
|
||||
c.sendKeepalive()
|
||||
}
|
||||
}
|
||||
|
||||
// handleUdpTunnel local listen to udp tunnel
|
||||
func (c *Client) handleUdpTunnel(ctx context.Context, t global.Tunnel) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// handleConnect handle connect request message from remote, local dial to remomte addr
|
||||
func (c *Client) handleConnect(ctx context.Context, from, to string, req *network.ConnectRequest) {
|
||||
dial := "tcp"
|
||||
if req.GetXType() == network.ConnectRequest_udp {
|
||||
dial = "udp"
|
||||
}
|
||||
conn, err := net.Dial(dial, fmt.Sprintf("%s:%d", req.GetAddr(), req.GetPort()))
|
||||
if err != nil {
|
||||
c.connectError(to, req.GetId(), err.Error())
|
||||
return
|
||||
}
|
||||
link := newLink(req.GetId(), req.GetName(), from, c, conn)
|
||||
c.Lock()
|
||||
c.links[link.id] = link
|
||||
c.Unlock()
|
||||
c.connectOK(to, req.GetId())
|
||||
go link.loop(ctx)
|
||||
}
|
||||
|
||||
// handleDisconnect handle disconnect message from remote, this means remote connection is closed
|
||||
func (c *Client) handleDisconnect(data *network.Disconnect) {
|
||||
id := data.GetId()
|
||||
|
||||
c.RLock()
|
||||
tn := c.links[id]
|
||||
c.RUnlock()
|
||||
|
||||
if tn != nil {
|
||||
tn.close()
|
||||
|
||||
c.Lock()
|
||||
delete(c.links, id)
|
||||
c.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// handleData handle forward data message, write data to local connection
|
||||
func (c *Client) handleData(data *network.Data) {
|
||||
id := data.GetLid()
|
||||
c.RLock()
|
||||
tn := c.links[id]
|
||||
c.RUnlock()
|
||||
if tn == nil {
|
||||
logging.Error("link %s not found", id)
|
||||
return
|
||||
}
|
||||
tn.write(data.GetData())
|
||||
}
|
||||
|
||||
func (c *Client) closeLink(l *link) {
|
||||
l.close()
|
||||
c.Lock()
|
||||
delete(c.links, l.id)
|
||||
c.Unlock()
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"natpass/code/client/global"
|
||||
"natpass/code/network"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (c *Client) writeHandshake() error {
|
||||
var msg network.Msg
|
||||
msg.XType = network.Msg_handshake
|
||||
msg.From = c.cfg.ID
|
||||
msg.To = "server"
|
||||
msg.Payload = &network.Msg_Hsp{
|
||||
Hsp: &network.HandshakePayload{
|
||||
Enc: c.cfg.Enc[:],
|
||||
},
|
||||
}
|
||||
return c.conn.WriteMessage(&msg, 5*time.Second)
|
||||
}
|
||||
|
||||
func (c *Client) sendConnect(id string, t global.Tunnel) {
|
||||
tp := network.ConnectRequest_tcp
|
||||
if t.Type != "tcp" {
|
||||
tp = network.ConnectRequest_udp
|
||||
}
|
||||
var msg network.Msg
|
||||
msg.From = c.cfg.ID
|
||||
msg.To = t.Target
|
||||
msg.XType = network.Msg_connect_req
|
||||
msg.Payload = &network.Msg_Creq{
|
||||
Creq: &network.ConnectRequest{
|
||||
Id: id,
|
||||
Name: t.Name,
|
||||
XType: tp,
|
||||
Addr: t.RemoteAddr,
|
||||
Port: uint32(t.RemotePort),
|
||||
},
|
||||
}
|
||||
c.conn.WriteMessage(&msg, 5*time.Second)
|
||||
}
|
||||
|
||||
func (c *Client) connectError(to, id, m string) {
|
||||
var msg network.Msg
|
||||
msg.From = c.cfg.ID
|
||||
msg.To = to
|
||||
msg.XType = network.Msg_connect_rep
|
||||
msg.Payload = &network.Msg_Crep{
|
||||
Crep: &network.ConnectResponse{
|
||||
Id: id,
|
||||
Ok: false,
|
||||
Msg: m,
|
||||
},
|
||||
}
|
||||
c.conn.WriteMessage(&msg, time.Second)
|
||||
}
|
||||
|
||||
func (c *Client) connectOK(to, id string) {
|
||||
var msg network.Msg
|
||||
msg.From = c.cfg.ID
|
||||
msg.To = to
|
||||
msg.XType = network.Msg_connect_rep
|
||||
msg.Payload = &network.Msg_Crep{
|
||||
Crep: &network.ConnectResponse{
|
||||
Id: id,
|
||||
Ok: true,
|
||||
},
|
||||
}
|
||||
c.conn.WriteMessage(&msg, time.Second)
|
||||
}
|
||||
|
||||
func (c *Client) send(id, target string, data []byte) {
|
||||
var msg network.Msg
|
||||
msg.From = c.cfg.ID
|
||||
msg.To = target
|
||||
msg.XType = network.Msg_forward
|
||||
msg.Payload = &network.Msg_XData{
|
||||
XData: &network.Data{
|
||||
Lid: id,
|
||||
Data: data,
|
||||
},
|
||||
}
|
||||
c.conn.WriteMessage(&msg, time.Second)
|
||||
}
|
||||
|
||||
func (c *Client) disconnect(id, to string) {
|
||||
var msg network.Msg
|
||||
msg.From = c.cfg.ID
|
||||
msg.To = to
|
||||
msg.XType = network.Msg_disconnect
|
||||
msg.Payload = &network.Msg_XDisconnect{
|
||||
XDisconnect: &network.Disconnect{
|
||||
Id: id,
|
||||
},
|
||||
}
|
||||
c.conn.WriteMessage(&msg, time.Second)
|
||||
c.Lock()
|
||||
delete(c.links, id)
|
||||
c.Unlock()
|
||||
}
|
||||
|
||||
func (c *Client) sendKeepalive() {
|
||||
var msg network.Msg
|
||||
msg.From = c.cfg.ID
|
||||
msg.To = "server"
|
||||
msg.XType = network.Msg_keepalive
|
||||
c.conn.WriteMessage(&msg, time.Second)
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/lwch/logging"
|
||||
)
|
||||
|
||||
type link struct {
|
||||
cli *Client
|
||||
id string // link id
|
||||
name string // tunnel name
|
||||
target string // remote client id
|
||||
c net.Conn
|
||||
}
|
||||
|
||||
func newLink(id, name, target string, cli *Client, conn net.Conn) *link {
|
||||
logging.Info("create link %s: %s", name, id)
|
||||
return &link{
|
||||
cli: cli,
|
||||
id: id,
|
||||
name: name,
|
||||
target: target,
|
||||
c: conn,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *link) close() {
|
||||
logging.Info("disconnect tunnel %s on link %s", l.name, l.id)
|
||||
err := l.c.Close()
|
||||
if err == nil {
|
||||
l.cli.disconnect(l.id, l.target)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *link) loop(ctx context.Context) {
|
||||
defer l.cli.closeLink(l)
|
||||
buf := make([]byte, 32*1024)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
n, err := l.c.Read(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if n == 0 {
|
||||
continue
|
||||
}
|
||||
logging.Debug("link %s on tunnel %s read from local %d bytes", l.id, l.name, n)
|
||||
l.cli.send(l.id, l.target, buf[:n])
|
||||
}
|
||||
}
|
||||
|
||||
func (l *link) write(data []byte) error {
|
||||
logging.Debug("link %s on tunnel %s write from remote %d bytes", l.id, l.name, len(data))
|
||||
_, err := io.Copy(l.c, bytes.NewReader(data))
|
||||
return err
|
||||
}
|
||||
@@ -25,6 +25,7 @@ type Configure struct {
|
||||
ID string
|
||||
Server string
|
||||
Enc [md5.Size]byte
|
||||
Links int
|
||||
LogDir string
|
||||
LogSize utils.Bytes
|
||||
LogRotate int
|
||||
@@ -37,6 +38,7 @@ func LoadConf(dir string) *Configure {
|
||||
ID string `yaml:"id"`
|
||||
Server string `yaml:"server"`
|
||||
Secret string `yaml:"secret"`
|
||||
Links int `yaml:"links"`
|
||||
Log struct {
|
||||
Dir string `yaml:"dir"`
|
||||
Size utils.Bytes `yaml:"size"`
|
||||
@@ -54,10 +56,14 @@ func LoadConf(dir string) *Configure {
|
||||
}
|
||||
cfg.Tunnel[i] = t
|
||||
}
|
||||
if cfg.Links <= 0 {
|
||||
cfg.Links = 3
|
||||
}
|
||||
return &Configure{
|
||||
ID: cfg.ID,
|
||||
Server: cfg.Server,
|
||||
Enc: md5.Sum([]byte(cfg.Secret)),
|
||||
Links: cfg.Links,
|
||||
LogDir: cfg.Log.Dir,
|
||||
LogSize: cfg.Log.Size,
|
||||
LogRotate: cfg.Log.Rotate,
|
||||
|
||||
+10
-10
@@ -1,17 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"fmt"
|
||||
"natpass/code/client/client"
|
||||
"natpass/code/client/global"
|
||||
"natpass/code/network"
|
||||
"natpass/code/client/pool"
|
||||
"natpass/code/client/tunnel"
|
||||
"os"
|
||||
|
||||
"github.com/lwch/daemon"
|
||||
"github.com/lwch/logging"
|
||||
"github.com/lwch/runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -57,11 +55,13 @@ func main() {
|
||||
logging.SetSizeRotate(cfg.LogDir, "np-cli", int(cfg.LogSize.Bytes()), cfg.LogRotate, true)
|
||||
defer logging.Flush()
|
||||
|
||||
conn, err := tls.Dial("tcp", cfg.Server, nil)
|
||||
runtime.Assert(err)
|
||||
c := network.NewConn(conn)
|
||||
defer c.Close()
|
||||
pl := pool.New(cfg.Links)
|
||||
|
||||
cli := client.New(cfg, c)
|
||||
cli.Run()
|
||||
for _, t := range cfg.Tunnels {
|
||||
tn := tunnel.New(t, pl)
|
||||
pl.Add(tn)
|
||||
go tn.Handle()
|
||||
}
|
||||
|
||||
pl.Loop(cfg)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package pool
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"natpass/code/client/global"
|
||||
"natpass/code/client/tunnel"
|
||||
"natpass/code/network"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/lwch/logging"
|
||||
)
|
||||
|
||||
func (p *Pool) handleConnect(conn *network.Conn, from, to string, req *network.ConnectRequest) {
|
||||
dial := "tcp"
|
||||
if req.GetXType() == network.ConnectRequest_udp {
|
||||
dial = "udp"
|
||||
}
|
||||
link, err := net.Dial(dial, fmt.Sprintf("%s:%d", req.GetAddr(), req.GetPort()))
|
||||
if err != nil {
|
||||
p.sendConnectError(conn, to, from, req.GetId(), err.Error())
|
||||
return
|
||||
}
|
||||
host, pt, _ := net.SplitHostPort(link.LocalAddr().String())
|
||||
port, _ := strconv.ParseUint(pt, 10, 16)
|
||||
tn := tunnel.New(global.Tunnel{
|
||||
Name: req.GetName(),
|
||||
Target: to,
|
||||
Type: dial,
|
||||
LocalAddr: host,
|
||||
LocalPort: uint16(port),
|
||||
RemoteAddr: req.GetAddr(),
|
||||
RemotePort: uint16(req.GetPort()),
|
||||
}, p)
|
||||
tn.NewLink(req.GetId(), req.GetName(), link, p.writeChannel)
|
||||
p.Add(tn)
|
||||
}
|
||||
|
||||
func (p *Pool) handleDisconnect(data *network.Disconnect) {
|
||||
id := data.GetId()
|
||||
|
||||
p.RLock()
|
||||
link := p.links[id]
|
||||
p.RUnlock()
|
||||
|
||||
if link != nil {
|
||||
link.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Pool) handleData(data *network.Data) {
|
||||
id := data.GetLid()
|
||||
p.RLock()
|
||||
link := p.links[id]
|
||||
p.RUnlock()
|
||||
if link == nil {
|
||||
logging.Error("link %s not found", id)
|
||||
return
|
||||
}
|
||||
link.WriteData(data.GetData())
|
||||
}
|
||||
|
||||
func (p *Pool) sendConnectError(conn *network.Conn, from, to, id, m string) {
|
||||
var msg network.Msg
|
||||
msg.From = from
|
||||
msg.To = to
|
||||
msg.XType = network.Msg_connect_rep
|
||||
msg.Payload = &network.Msg_Crep{
|
||||
Crep: &network.ConnectResponse{
|
||||
Id: id,
|
||||
Ok: false,
|
||||
Msg: m,
|
||||
},
|
||||
}
|
||||
conn.WriteMessage(&msg, time.Second)
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package pool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"natpass/code/client/global"
|
||||
"natpass/code/client/tunnel"
|
||||
"natpass/code/network"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lwch/logging"
|
||||
"github.com/lwch/runtime"
|
||||
)
|
||||
|
||||
// Pool connection pool
|
||||
type Pool struct {
|
||||
sync.RWMutex
|
||||
count int
|
||||
writeChannel chan *network.Msg
|
||||
tunnels map[string]*tunnel.Tunnel // tunnel name => tunnel
|
||||
links map[string]*tunnel.Link // link id => link
|
||||
}
|
||||
|
||||
// New create connection pool
|
||||
func New(count int) *Pool {
|
||||
return &Pool{
|
||||
count: count,
|
||||
writeChannel: make(chan *network.Msg, 100),
|
||||
tunnels: make(map[string]*tunnel.Tunnel),
|
||||
links: make(map[string]*tunnel.Link),
|
||||
}
|
||||
}
|
||||
|
||||
// WriteChan get write channel
|
||||
func (p *Pool) WriteChan() chan *network.Msg {
|
||||
return p.writeChannel
|
||||
}
|
||||
|
||||
// Loop main loop
|
||||
func (p *Pool) Loop(cfg *global.Configure) {
|
||||
for i := 0; i < p.count; i++ {
|
||||
go func() {
|
||||
for {
|
||||
p.connect(cfg)
|
||||
}
|
||||
}()
|
||||
}
|
||||
select {}
|
||||
}
|
||||
|
||||
// LinkClose on close link
|
||||
func (p *Pool) LinkClose(name, id string) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
if tunnel, ok := p.tunnels[name]; ok {
|
||||
if len(tunnel.GetLinks()) == 0 {
|
||||
delete(p.tunnels, name)
|
||||
}
|
||||
}
|
||||
delete(p.links, id)
|
||||
}
|
||||
|
||||
// connect connect server
|
||||
func (p *Pool) connect(cfg *global.Configure) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logging.Error("connect error: %v", err)
|
||||
}
|
||||
}()
|
||||
conn, err := tls.Dial("tcp", cfg.Server, nil)
|
||||
runtime.Assert(err)
|
||||
c := network.NewConn(conn)
|
||||
defer c.Close()
|
||||
err = p.writeHandshake(c, cfg)
|
||||
runtime.Assert(err)
|
||||
logging.Info("%s connected", cfg.Server)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case msg := <-p.writeChannel:
|
||||
msg.From = cfg.ID
|
||||
c.WriteMessage(msg, time.Second)
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
for {
|
||||
msg, err := c.ReadMessage(time.Second)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "i/o timeout") {
|
||||
continue
|
||||
}
|
||||
logging.Error("read message: %v", err)
|
||||
return
|
||||
}
|
||||
switch msg.GetXType() {
|
||||
case network.Msg_connect_req:
|
||||
p.handleConnect(c, msg.GetFrom(), msg.GetTo(), msg.GetCreq())
|
||||
case network.Msg_connect_rep:
|
||||
logging.Info("connected")
|
||||
case network.Msg_disconnect:
|
||||
p.handleDisconnect(msg.GetXDisconnect())
|
||||
case network.Msg_forward:
|
||||
p.handleData(msg.GetXData())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add add tunnel
|
||||
func (p *Pool) Add(tunnel *tunnel.Tunnel) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
p.tunnels[tunnel.Name] = tunnel
|
||||
for _, link := range tunnel.GetLinks() {
|
||||
p.links[link.ID] = link
|
||||
}
|
||||
}
|
||||
|
||||
// AddLink add link
|
||||
func (p *Pool) AddLink(link *tunnel.Link) {
|
||||
p.links[link.ID] = link
|
||||
}
|
||||
|
||||
func (p *Pool) writeHandshake(conn *network.Conn, cfg *global.Configure) error {
|
||||
var msg network.Msg
|
||||
msg.XType = network.Msg_handshake
|
||||
msg.From = cfg.ID
|
||||
msg.To = "server"
|
||||
msg.Payload = &network.Msg_Hsp{
|
||||
Hsp: &network.HandshakePayload{
|
||||
Enc: cfg.Enc[:],
|
||||
},
|
||||
}
|
||||
return conn.WriteMessage(&msg, 5*time.Second)
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"natpass/code/network"
|
||||
"net"
|
||||
|
||||
"github.com/lwch/logging"
|
||||
)
|
||||
|
||||
type Link struct {
|
||||
tunnel *Tunnel
|
||||
ID string // link id
|
||||
target string // remote client id
|
||||
conn net.Conn
|
||||
write chan *network.Msg
|
||||
onWork bool
|
||||
closed bool
|
||||
}
|
||||
|
||||
func newLink(id, target string, tunnel *Tunnel, conn net.Conn, write chan *network.Msg) *Link {
|
||||
logging.Info("create link %s: %s", tunnel.Name, id)
|
||||
return &Link{
|
||||
tunnel: tunnel,
|
||||
ID: id,
|
||||
target: target,
|
||||
conn: conn,
|
||||
write: write,
|
||||
onWork: false,
|
||||
closed: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Close close link
|
||||
func (link *Link) Close() {
|
||||
if link.closed {
|
||||
return
|
||||
}
|
||||
logging.Info("disconnect tunnel %s on link %s", link.tunnel.Name, link.ID)
|
||||
link.closed = true
|
||||
err := link.conn.Close()
|
||||
if err == nil {
|
||||
link.sendDisconnect(link.ID, link.target)
|
||||
}
|
||||
link.tunnel.Close(link)
|
||||
}
|
||||
|
||||
func (link *Link) loop() {
|
||||
defer link.Close()
|
||||
buf := make([]byte, 32*1024)
|
||||
for {
|
||||
n, err := link.conn.Read(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if n == 0 {
|
||||
continue
|
||||
}
|
||||
logging.Debug("link %s on tunnel %s read from local %d bytes", link.ID, link.tunnel.Name, n)
|
||||
link.sendData(link.ID, link.target, buf[:n])
|
||||
}
|
||||
}
|
||||
|
||||
// WriteData write data from remote
|
||||
func (link *Link) WriteData(data []byte) error {
|
||||
logging.Debug("link %s on tunnel %s write from remote %d bytes", link.ID, link.tunnel.Name, len(data))
|
||||
_, err := io.Copy(link.conn, bytes.NewReader(data))
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"natpass/code/client/global"
|
||||
"natpass/code/network"
|
||||
)
|
||||
|
||||
func (link *Link) sendConnect(id string, t global.Tunnel) {
|
||||
tp := network.ConnectRequest_tcp
|
||||
if t.Type != "tcp" {
|
||||
tp = network.ConnectRequest_udp
|
||||
}
|
||||
var msg network.Msg
|
||||
msg.To = t.Target
|
||||
msg.XType = network.Msg_connect_req
|
||||
msg.Payload = &network.Msg_Creq{
|
||||
Creq: &network.ConnectRequest{
|
||||
Id: id,
|
||||
Name: t.Name,
|
||||
XType: tp,
|
||||
Addr: t.RemoteAddr,
|
||||
Port: uint32(t.RemotePort),
|
||||
},
|
||||
}
|
||||
link.write <- &msg
|
||||
}
|
||||
|
||||
func (link *Link) sendDisconnect(id, to string) {
|
||||
var msg network.Msg
|
||||
msg.To = to
|
||||
msg.XType = network.Msg_disconnect
|
||||
msg.Payload = &network.Msg_XDisconnect{
|
||||
XDisconnect: &network.Disconnect{
|
||||
Id: id,
|
||||
},
|
||||
}
|
||||
link.write <- &msg
|
||||
}
|
||||
|
||||
func (link *Link) sendData(id, target string, data []byte) {
|
||||
var msg network.Msg
|
||||
msg.To = target
|
||||
msg.XType = network.Msg_forward
|
||||
msg.Payload = &network.Msg_XData{
|
||||
XData: &network.Data{
|
||||
Lid: id,
|
||||
Data: data,
|
||||
},
|
||||
}
|
||||
link.write <- &msg
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"natpass/code/client/global"
|
||||
"natpass/code/network"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/lwch/logging"
|
||||
"github.com/lwch/runtime"
|
||||
)
|
||||
|
||||
type Super interface {
|
||||
LinkClose(string, string) // tunnel name, link id
|
||||
WriteChan() chan *network.Msg
|
||||
AddLink(*Link)
|
||||
}
|
||||
|
||||
// Tunnel tunnel
|
||||
type Tunnel struct {
|
||||
sync.RWMutex
|
||||
super Super
|
||||
Name string
|
||||
cfg global.Tunnel
|
||||
links map[string]*Link
|
||||
}
|
||||
|
||||
// New create tunnel
|
||||
func New(cfg global.Tunnel, super Super) *Tunnel {
|
||||
return &Tunnel{
|
||||
super: super,
|
||||
Name: cfg.Name,
|
||||
cfg: cfg,
|
||||
links: make(map[string]*Link),
|
||||
}
|
||||
}
|
||||
|
||||
func (tunnel *Tunnel) NewLink(id, target string, conn net.Conn, write chan *network.Msg) {
|
||||
link := newLink(id, target, tunnel, conn, write)
|
||||
tunnel.Lock()
|
||||
tunnel.links[link.ID] = link
|
||||
tunnel.Unlock()
|
||||
}
|
||||
|
||||
// Handle tunnel handler
|
||||
func (tunnel *Tunnel) Handle() {
|
||||
if tunnel.cfg.Type == "tcp" {
|
||||
tunnel.handleTcp()
|
||||
} else {
|
||||
// TODO
|
||||
func() {}()
|
||||
}
|
||||
}
|
||||
|
||||
// handleTcp local listen to tcp tunnel
|
||||
func (tunnel *Tunnel) handleTcp() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logging.Error("close tcp tunnel: %s, err=%v", tunnel.cfg.Name, err)
|
||||
}
|
||||
}()
|
||||
l, err := net.ListenTCP("tcp", &net.TCPAddr{
|
||||
IP: net.ParseIP(tunnel.cfg.LocalAddr),
|
||||
Port: int(tunnel.cfg.LocalPort),
|
||||
})
|
||||
runtime.Assert(err)
|
||||
defer l.Close()
|
||||
for {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
logging.Error("accept from %s tunnel, err=%v", tunnel.cfg.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
id, err := runtime.UUID(16, "0123456789abcdef")
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
logging.Error("generate link id failed, err=%v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
link := newLink(id, tunnel.cfg.Target, tunnel, conn, tunnel.super.WriteChan())
|
||||
link.sendConnect(link.ID, tunnel.cfg)
|
||||
|
||||
tunnel.super.AddLink(link)
|
||||
tunnel.Lock()
|
||||
tunnel.links[link.ID] = link
|
||||
tunnel.Unlock()
|
||||
|
||||
go link.loop()
|
||||
}
|
||||
}
|
||||
|
||||
// Close close link
|
||||
func (tunnel *Tunnel) Close(link *Link) {
|
||||
link.Close()
|
||||
tunnel.Lock()
|
||||
delete(tunnel.links, link.ID)
|
||||
tunnel.Unlock()
|
||||
tunnel.super.LinkClose(tunnel.Name, link.ID)
|
||||
}
|
||||
|
||||
// GetLinks get tunnel links
|
||||
func (tunnel *Tunnel) GetLinks() []*Link {
|
||||
ret := make([]*Link, 0, len(tunnel.links))
|
||||
tunnel.RLock()
|
||||
for _, l := range tunnel.links {
|
||||
ret = append(ret, l)
|
||||
}
|
||||
tunnel.RUnlock()
|
||||
return ret
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
id: this # 客户端ID
|
||||
server: 127.0.0.1:6154 # 服务器地址
|
||||
links: 3 # 与server的连接数
|
||||
secret: 0123456789 # 预共享密钥,必须与server端相同,否则握手失败
|
||||
log:
|
||||
dir: ./logs # 路径
|
||||
|
||||
Reference in New Issue
Block a user