package gateway import ( "context" "errors" "fmt" "net" "os" "next-terminal/server/config" "next-terminal/server/utils" "golang.org/x/crypto/ssh" ) // Gateway 接入网关 type Gateway struct { ID string // 接入网关ID Connected bool // 是否已连接 LocalHost string // 隧道映射到本地的IP地址 SshClient *ssh.Client Message string // 失败原因 tunnels map[string]*Tunnel Add chan *Tunnel Del chan string exit chan bool } func NewGateway(id, localhost string, connected bool, message string, client *ssh.Client) *Gateway { return &Gateway{ ID: id, LocalHost: localhost, Connected: connected, Message: message, SshClient: client, Add: make(chan *Tunnel), Del: make(chan string), tunnels: map[string]*Tunnel{}, exit: make(chan bool, 1), } } func (g *Gateway) Run() { for { select { case t := <-g.Add: g.tunnels[t.ID] = t go t.Run() case k := <-g.Del: if _, ok := g.tunnels[k]; ok { g.tunnels[k].Close() delete(g.tunnels, k) } case <-g.exit: return } } } func (g *Gateway) Close() { g.exit <- true if g.SshClient != nil { _ = g.SshClient.Close() } if len(g.tunnels) > 0 { for _, tunnel := range g.tunnels { tunnel.Close() } } } func (g *Gateway) OpenSshTunnel(id, ip string, port int) (exposedIP string, exposedPort int, err error) { if !g.Connected { return "", 0, errors.New(g.Message) } localPort, err := utils.GetAvailablePort() if err != nil { return "", 0, err } localHost := g.LocalHost if localHost == "" { if config.GlobalCfg.Container { localIp, err := utils.GetLocalIp() if err != nil { hostname, err := os.Hostname() if err != nil { return "", 0, err } else { localHost = hostname } } else { localHost = localIp } } else { localHost = "localhost" } } localAddr := fmt.Sprintf("%s:%d", localHost, localPort) listener, err := net.Listen("tcp", localAddr) if err != nil { return "", 0, err } ctx, cancel := context.WithCancel(context.Background()) tunnel := &Tunnel{ ID: id, LocalHost: g.LocalHost, LocalPort: localPort, Gateway: g, RemoteHost: ip, RemotePort: port, ctx: ctx, cancel: cancel, listener: listener, } g.Add <- tunnel return tunnel.LocalHost, tunnel.LocalPort, nil } func (g Gateway) CloseSshTunnel(id string) { if g.tunnels[id] != nil { g.tunnels[id].Close() } }