sing/cli/uot-local/main.go
2022-04-29 13:15:42 +08:00

133 lines
3.3 KiB
Go

package main
import (
"context"
"net"
"net/netip"
"os"
"os/signal"
"syscall"
"github.com/sagernet/sing"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/redir"
"github.com/sagernet/sing/common/rw"
"github.com/sagernet/sing/common/uot"
"github.com/sagernet/sing/protocol/socks"
"github.com/sagernet/sing/transport/mixed"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
var (
verbose bool
transproxy string
)
func main() {
command := cobra.Command{
Use: "uot-local <bind> <upstream>",
Short: "SUoT client.",
Long: "SUoT client. \n\nconverts a normal socks server to a SUoT mixed server.",
Example: "uot-local 0.0.0.0:2080 127.0.0.1:1080",
Version: sing.VersionStr,
Args: cobra.ExactArgs(2),
Run: run,
}
command.Flags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose mode")
command.Flags().StringVarP(&transproxy, "transproxy", "t", "", "Enable transparent proxy support [possible values: redirect, tproxy]")
err := command.Execute()
if err != nil {
logrus.Fatal(err)
}
}
func run(cmd *cobra.Command, args []string) {
if verbose {
logrus.SetLevel(logrus.TraceLevel)
}
bind, err := netip.ParseAddrPort(args[0])
if err != nil {
logrus.Fatal("bad bind address: ", err)
}
_, err = netip.ParseAddrPort(args[1])
if err != nil {
logrus.Fatal("bad upstream address: ", err)
}
var transproxyMode redir.TransproxyMode
switch transproxy {
case "redirect":
transproxyMode = redir.ModeRedirect
case "tproxy":
transproxyMode = redir.ModeTProxy
case "":
transproxyMode = redir.ModeDisabled
default:
logrus.Fatal("unknown transproxy mode ", transproxy)
}
client := &localClient{upstream: args[1]}
client.Listener = mixed.NewListener(bind, nil, transproxyMode, client)
err = client.Start()
if err != nil {
logrus.Fatal("start mixed server: ", err)
}
logrus.Info("mixed server started at ", client.TCPListener.Addr())
osSignals := make(chan os.Signal, 1)
signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM)
<-osSignals
client.Close()
}
type localClient struct {
*mixed.Listener
upstream string
}
func (c *localClient) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
logrus.Info("CONNECT ", conn.RemoteAddr(), " ==> ", metadata.Destination)
upstream, err := net.Dial("tcp", c.upstream)
if err != nil {
return E.Cause(err, "connect to upstream")
}
_, err = socks.ClientHandshake(upstream, socks.Version5, socks.CommandConnect, metadata.Destination, "", "")
if err != nil {
return E.Cause(err, "upstream handshake failed")
}
return rw.CopyConn(context.Background(), upstream, conn)
}
func (c *localClient) NewPacketConnection(conn socks.PacketConn, _ M.Metadata) error {
upstream, err := net.Dial("tcp", c.upstream)
if err != nil {
return E.Cause(err, "connect to upstream")
}
_, err = socks.ClientHandshake(upstream, socks.Version5, socks.CommandConnect, M.AddrPortFrom(M.AddrFromFqdn(uot.UOTMagicAddress), 443), "", "")
if err != nil {
return E.Cause(err, "upstream handshake failed")
}
client := uot.NewClientConn(upstream)
return socks.CopyPacketConn(context.Background(), client, conn)
}
func (c *localClient) OnError(err error) {
common.Close(err)
if E.IsClosed(err) {
return
}
logrus.Warn(err)
}