Skip to content
File

Blob: cmd/client/connect.go

go82 lines
1package client
2 
3import (
4 "context"
5 "fmt"
6 "io"
7 "net"
8 "os"
9 "os/signal"
10 "syscall"
11 
12 "go.miragespace.co/specter/tun/client/connector"
13 "go.miragespace.co/specter/tun/client/dialer"
14 
15 "github.com/urfave/cli/v3"
16 "go.uber.org/zap"
17)
18 
19func cmdConnect(ctx context.Context, cmd *cli.Command) error {
20 logger := cmd.Root().Metadata["logger"].(*zap.Logger)
21 
22 hostname := cmd.Args().First()
23 if hostname == "" {
24 return fmt.Errorf("missing hostname in argument")
25 }
26 
27 var (
28 remote net.Addr
29 dial dialer.TransportDialer
30 err error
31 )
32 
33 parsed, err := dialer.ParseApex(hostname)
34 if err != nil {
35 return fmt.Errorf("error parsing hostname: %w", err)
36 }
37 
38 if cmd.IsSet("tcp") {
39 remote, dial, err = tlsDialer(ctx, cmd, logger, parsed, true)
40 } else {
41 remote, dial, err = quicDialer(ctx, cmd, logger, parsed, true)
42 }
43 if err != nil {
44 return fmt.Errorf("error dialing specter gateway: %w", err)
45 }
46 
47 rw, err := connector.GetConnection(dial)
48 if err != nil {
49 return err
50 }
51 defer rw.Close()
52 
53 logger.Info("Tunnel established", zap.String("via", remote.String()))
54 
55 sigs := make(chan os.Signal, 1)
56 signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
57 
58 go func() {
59 _, err := io.Copy(cmd.Root().Writer, rw)
60 if err != nil {
61 logger.Error("error piping to target", zap.Error(err))
62 sigs <- syscall.SIGTERM
63 }
64 }()
65 go func() {
66 _, err := io.Copy(rw, cmd.Root().Reader)
67 if err != nil {
68 logger.Error("error piping to target", zap.Error(err))
69 sigs <- syscall.SIGTERM
70 }
71 }()
72 
73 select {
74 case sig := <-sigs:
75 logger.Info("received signal to stop", zap.String("signal", sig.String()))
76 case <-ctx.Done():
77 logger.Info("context done", zap.Error(ctx.Err()))
78 }
79 
80 return nil
81}