diff --git a/README_en.md b/README_en.md index 9df0181..5b0ad61 100644 --- a/README_en.md +++ b/README_en.md @@ -265,7 +265,7 @@ gost -L=:8080 -F=ss://aes-128-cfb:123456@server_ip:8338 #### TLS There is built-in TLS certificate in gost, if you need to use other TLS certificate, there are two ways: * Place two files cert.pem (public key) and key.pem (private key) in the current working directory, gost will automatically load them. -* Use the parameter to specify the path to the certificate file: +* Use the parameter to specify the path to the certificate file: ```bash gost -L="http2://:443?cert=/path/to/my/cert/file&key=/path/to/my/key/file" ``` diff --git a/node.go b/node.go index 71690fb..88d7153 100644 --- a/node.go +++ b/node.go @@ -71,7 +71,7 @@ func ParseProxyNode(s string) (node ProxyNode, err error) { } switch node.Transport { - case "ws", "wss", "tls", "http2", "ssu", "quic", "kcp": + case "ws", "wss", "tls", "http2", "ssu", "quic", "kcp", "redirect": case "https": node.Protocol = "http" node.Transport = "tls" diff --git a/redirect.go b/redirect.go new file mode 100644 index 0000000..7a8b1ea --- /dev/null +++ b/redirect.go @@ -0,0 +1,101 @@ +package gost + +import ( + "errors" + "fmt" + "github.com/golang/glog" + "net" + "syscall" +) + +const ( + SO_ORIGINAL_DST = 80 +) + +type RedsocksTCPServer struct { + Base *ProxyServer +} + +func NewRedsocksTCPServer(base *ProxyServer) *RedsocksTCPServer { + return &RedsocksTCPServer{ + Base: base, + } +} + +func (s *RedsocksTCPServer) ListenAndServe() error { + laddr, err := net.ResolveTCPAddr("tcp", s.Base.Node.Addr) + if err != nil { + return err + } + ln, err := net.ListenTCP("tcp", laddr) + if err != nil { + return err + } + + defer ln.Close() + for { + conn, err := ln.AcceptTCP() + if err != nil { + glog.V(LWARNING).Infoln(err) + continue + } + go s.handleRedirectTCP(conn) + } +} + +func (s *RedsocksTCPServer) handleRedirectTCP(conn *net.TCPConn) { + srcAddr := conn.RemoteAddr() + dstAddr, conn, err := getOriginalDstAddr(conn) + if err != nil { + glog.V(LWARNING).Infof("[red-tcp] %s -> %s : %s", srcAddr, dstAddr, err) + return + } + defer conn.Close() + + glog.V(LINFO).Infof("[red-tcp] %s -> %s", srcAddr, dstAddr) + + cc, err := s.Base.Chain.Dial(dstAddr.String()) + if err != nil { + glog.V(LWARNING).Infof("[red-tcp] %s -> %s : %s", srcAddr, dstAddr, err) + return + } + defer cc.Close() + + glog.V(LINFO).Infof("[red-tcp] %s <-> %s", srcAddr, dstAddr) + s.Base.transport(conn, cc) + glog.V(LINFO).Infof("[red-tcp] %s >-< %s", srcAddr, dstAddr) +} + +func getOriginalDstAddr(conn *net.TCPConn) (addr net.Addr, c *net.TCPConn, err error) { + defer conn.Close() + + fc, err := conn.File() + if err != nil { + return + } + defer fc.Close() + + mreq, err := syscall.GetsockoptIPv6Mreq(int(fc.Fd()), syscall.IPPROTO_IP, SO_ORIGINAL_DST) + if err != nil { + return + } + + // only ipv4 support + ip := net.IPv4(mreq.Multiaddr[4], mreq.Multiaddr[5], mreq.Multiaddr[6], mreq.Multiaddr[7]) + port := uint16(mreq.Multiaddr[2])<<8 + uint16(mreq.Multiaddr[3]) + addr, err = net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", ip.String(), port)) + if err != nil { + return + } + + cc, err := net.FileConn(fc) + if err != nil { + return + } + + c, ok := cc.(*net.TCPConn) + if !ok { + err = errors.New("not a TCP connection") + } + return +} diff --git a/server.go b/server.go index 488f491..ceb804d 100644 --- a/server.go +++ b/server.go @@ -95,6 +95,8 @@ func (s *ProxyServer) Serve() error { config.Key, _ = s.Node.Users[0].Password() } return NewKCPServer(s, config).ListenAndServe() + case "redirect": + return NewRedsocksTCPServer(s).ListenAndServe() default: ln, err = net.Listen("tcp", node.Addr) }