You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

133 lines
4.6 KiB

package portservice
import (
"bufio"
"context"
"crypto/ed25519"
"crypto/rand"
"fmt"
"io"
"net"
"os"
"path/filepath"
"testing"
"time"
"golang.org/x/crypto/ssh"
"tera-clone/services/tera-api/internal/directstream"
"tera-clone/services/tera-api/internal/domain"
)
func TestBothOperationsShareCentralStreamWithDirectSession(t *testing.T) {
for _, modes := range [][2]string{{"reverse_ssh", "reverse_telnet"}, {"reverse_ssh", "reverse_ssh"}, {"reverse_telnet", "reverse_telnet"}} {
t.Run(modes[0]+"-"+modes[1], func(t *testing.T) {
factory := &pipeFactory{peers: make(chan net.Conn, 1)}
broker, _ := directstream.NewBroker(factory, directstream.BrokerOptions{SharedPortSessions: true})
defer broker.Close()
dir, err := os.MkdirTemp("", "gateway-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
path := filepath.Join(dir, "s.sock")
listener, err := directstream.ListenTicketSocket(path)
if err != nil {
t.Fatal(err)
}
defer listener.Close()
go directstream.ServeSharedSocket(listener, broker)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
direct, err := broker.Attach(ctx, directstream.AttachRequest{SessionID: "web", Channel: "CH20", Actor: "web", Access: directstream.AccessWriter, SharedWrite: true, MaxAttachments: 4})
if err != nil {
t.Fatal(err)
}
defer direct.Close()
peer := <-factory.peers
defer peer.Close()
_ = peer.SetDeadline(time.Now().Add(8 * time.Second))
_, key, _ := ed25519.GenerateKey(rand.Reader)
signer, _ := ssh.NewSignerFromKey(key)
listeners := map[string]net.Listener{}
gateway, err := New(Options{TransportFactory: factory, AttachShared: (directstream.SharedSocketClient{Path: path}).Attach, SSHSigner: signer,
Authenticate: func(context.Context, string, string, string) (string, error) { return "tester", nil },
ListenInterface: func(device, address string) (net.Listener, error) {
if address != "0.0.0.0:10020" {
t.Errorf("address = %s", address)
}
value, err := net.Listen("tcp", "127.0.0.1:0")
listeners[device] = value
return value, err
},
})
if err != nil {
t.Fatal(err)
}
defer gateway.Close()
port := domain.Port{Channel: "CH20", Operation1: domain.PortOperation{Mode: modes[0], Port: 10020, MaxClients: 4, Authentication: "none", Newline: "cr"}, Operation2: domain.PortOperation{Mode: modes[1], Port: 10020, MaxClients: 4, Authentication: "none", Newline: "cr"}}
if err := gateway.Apply(ctx, []domain.Port{port}); err != nil {
t.Fatal(err)
}
var readers []*bufio.Reader
for index, mode := range modes {
address := listeners[fmt.Sprintf("eth%d", index)].Addr().String()
var input io.Writer
var output io.Reader
if mode == "reverse_ssh" {
client, err := ssh.Dial("tcp", address, &ssh.ClientConfig{User: "tester", Auth: []ssh.AuthMethod{ssh.Password("test")}, HostKeyCallback: ssh.InsecureIgnoreHostKey(), Timeout: time.Second})
if err != nil {
t.Fatal(err)
}
defer client.Close()
session, err := client.NewSession()
if err != nil {
t.Fatal(err)
}
defer session.Close()
input, _ = session.StdinPipe()
output, _ = session.StdoutPipe()
if err := session.Shell(); err != nil {
t.Fatal(err)
}
} else {
connection, err := net.DialTimeout("tcp", address, time.Second)
if err != nil {
t.Fatal(err)
}
defer connection.Close()
_ = connection.SetDeadline(time.Now().Add(8 * time.Second))
input, output = connection, connection
}
reader := bufio.NewReader(output)
if _, err := readUntilContains(reader, "Connected at:"); err != nil {
t.Fatal(err)
}
if _, err := reader.ReadString('\n'); err != nil {
t.Fatal(err)
}
readers = append(readers, reader)
want := fmt.Sprintf("op%d\r", index+1)
go input.Write([]byte(want))
actual := make([]byte, len(want))
if _, err := io.ReadFull(peer, actual); err != nil || string(actual) != want {
t.Fatalf("operation TX %q: %v", actual, err)
}
}
go peer.Write([]byte("shared-output\r\n"))
for _, reader := range readers {
line, err := reader.ReadString('\n')
if err != nil || line != "shared-output\r\n" {
t.Fatalf("operation RX %q: %v", line, err)
}
}
payload, err := direct.Receive(ctx)
if err != nil || string(payload) != "shared-output\r\n" {
t.Fatalf("web RX %q: %v", payload, err)
}
if snapshot := broker.Snapshot(); snapshot.TotalAttachments != 3 || snapshot.TransportOpens != 1 {
t.Fatalf("snapshot: %+v", snapshot)
}
})
}
}