Skip to content

Commit fba55ba

Browse files
made it compilable on non linux systems
1 parent 9bb1ee7 commit fba55ba

File tree

3 files changed

+246
-186
lines changed

3 files changed

+246
-186
lines changed

gohpts.go

Lines changed: 8 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"math/rand"
1414
"net"
1515
"net/http"
16-
"net/netip"
1716
"os"
1817
"os/signal"
1918
"runtime"
@@ -22,14 +21,11 @@ import (
2221
"strings"
2322
"sync"
2423
"sync/atomic"
25-
"syscall"
2624
"time"
27-
"unsafe"
2825

2926
"github.com/goccy/go-yaml"
3027
"github.com/rs/zerolog"
3128
"golang.org/x/net/proxy"
32-
"golang.org/x/sys/unix"
3329
)
3430

3531
const (
@@ -81,7 +77,7 @@ func delHopHeaders(header http.Header) {
8177
// https://datatracker.ietf.org/doc/html/rfc7230#section-6.1
8278
func delConnectionHeaders(h http.Header) {
8379
for _, f := range h["Connection"] {
84-
for _, sf := range strings.Split(f, ",") {
80+
for sf := range strings.SplitSeq(f, ",") {
8581
if sf = strings.TrimSpace(sf); sf != "" {
8682
h.Del(sf)
8783
}
@@ -270,7 +266,7 @@ func (p *proxyapp) getSocks() (proxy.Dialer, *http.Client, error) {
270266
}
271267
}
272268
startIdx := int(start % uint32(len(p.availProxyList)))
273-
for i := 0; i < chainLength; i++ {
269+
for i := range chainLength {
274270
idx := (startIdx + i) % len(p.availProxyList)
275271
copyProxyList = append(copyProxyList, p.availProxyList[idx])
276272
}
@@ -632,188 +628,14 @@ func (p *proxyapp) handler() http.HandlerFunc {
632628
}
633629
}
634630

635-
type tproxyServer struct {
636-
listener net.Listener
637-
quit chan struct{}
638-
wg sync.WaitGroup
639-
pa *proxyapp
640-
}
641-
642-
func newTproxyServer(pa *proxyapp) *tproxyServer {
643-
ts := &tproxyServer{
644-
quit: make(chan struct{}),
645-
pa: pa,
646-
}
647-
// https://iximiuz.com/en/posts/go-net-http-setsockopt-example/
648-
lc := net.ListenConfig{
649-
Control: func(network, address string, conn syscall.RawConn) error {
650-
var operr error
651-
if err := conn.Control(func(fd uintptr) {
652-
operr = unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_USER_TIMEOUT, int(timeout*1000))
653-
operr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
654-
if ts.pa.tproxyMode == "tproxy" {
655-
operr = unix.SetsockoptInt(int(fd), unix.SOL_IP, unix.IP_TRANSPARENT, 1)
656-
}
657-
}); err != nil {
658-
return err
659-
}
660-
return operr
661-
},
662-
}
663-
664-
ln, err := lc.Listen(context.Background(), "tcp4", ts.pa.tproxyAddr)
665-
if err != nil {
666-
var msg string
667-
if errors.Is(err, unix.EPERM) {
668-
msg = "try `sudo setcap 'cap_net_admin+ep` for the binary:"
669-
}
670-
ts.pa.logger.Fatal().Err(err).Msg(msg)
671-
}
672-
ts.listener = ln
673-
return ts
674-
}
675-
676-
func (ts *tproxyServer) ListenAndServe() {
677-
ts.wg.Add(1)
678-
go ts.serve()
679-
}
680-
681-
func (ts *tproxyServer) serve() {
682-
defer ts.wg.Done()
683-
684-
for {
685-
conn, err := ts.listener.Accept()
686-
if err != nil {
687-
select {
688-
case <-ts.quit:
689-
return
690-
default:
691-
ts.pa.logger.Error().Err(err).Msg("")
692-
}
693-
} else {
694-
ts.wg.Add(1)
695-
err := conn.SetDeadline(time.Now().Add(timeout))
696-
if err != nil {
697-
ts.pa.logger.Error().Err(err).Msg("")
698-
}
699-
go func() {
700-
ts.handleConnection(conn)
701-
ts.wg.Done()
702-
}()
703-
}
704-
}
705-
}
706-
707-
func getsockopt(s int, level int, optname int, optval unsafe.Pointer, optlen *uint32) (err error) {
708-
_, _, e := unix.Syscall6(unix.SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(optname), uintptr(optval), uintptr(unsafe.Pointer(optlen)), 0)
709-
if e != 0 {
710-
return e
711-
}
712-
return nil
713-
}
714-
715-
func (ts *tproxyServer) getOriginalDst(rawConn syscall.RawConn) (string, error) {
716-
var originalDst unix.RawSockaddrInet4
717-
err := rawConn.Control(func(fd uintptr) {
718-
optlen := uint32(unsafe.Sizeof(originalDst))
719-
err := getsockopt(int(fd), unix.SOL_IP, unix.SO_ORIGINAL_DST, unsafe.Pointer(&originalDst), &optlen)
720-
if err != nil {
721-
ts.pa.logger.Error().Err(err).Msg("[tproxy] getsockopt SO_ORIGINAL_DST failed")
722-
}
723-
})
724-
if err != nil {
725-
ts.pa.logger.Error().Err(err).Msg("[tproxy] Failed invoking control connection")
726-
return "", err
727-
}
728-
dstHost := netip.AddrFrom4(originalDst.Addr)
729-
dstPort := uint16(originalDst.Port<<8) | originalDst.Port>>8
730-
return fmt.Sprintf("%s:%d", dstHost, dstPort), nil
731-
}
732-
733-
func (ts *tproxyServer) handleConnection(srcConn net.Conn) {
734-
var (
735-
dstConn net.Conn
736-
dst string
737-
err error
738-
)
739-
defer srcConn.Close()
740-
switch ts.pa.tproxyMode {
741-
case "redirect":
742-
rawConn, err := srcConn.(*net.TCPConn).SyscallConn()
743-
if err != nil {
744-
ts.pa.logger.Error().Err(err).Msg("[tproxy] Failed to get raw connection")
745-
return
746-
}
747-
dst, err = ts.getOriginalDst(rawConn)
748-
if err != nil {
749-
ts.pa.logger.Error().Err(err).Msg("[tproxy] Failed to get destination address")
750-
return
751-
}
752-
ts.pa.logger.Debug().Msgf("[tproxy] getsockopt SO_ORIGINAL_DST %s", dst)
753-
case "tproxy":
754-
dst = srcConn.LocalAddr().String()
755-
ts.pa.logger.Debug().Msgf("[tproxy] IP_TRANSPARENT %s", dst)
756-
default:
757-
ts.pa.logger.Fatal().Msg("Unknown tproxyMode")
758-
}
759-
if isLocalAddress(dst) {
760-
dstConn, err = net.DialTimeout("tcp", dst, timeout)
761-
if err != nil {
762-
ts.pa.logger.Error().Err(err).Msgf("[tproxy] Failed connecting to %s", dst)
763-
return
764-
}
765-
} else {
766-
sockDialer, _, err := ts.pa.getSocks()
767-
if err != nil {
768-
ts.pa.logger.Error().Err(err).Msg("[tproxy] Failed getting SOCKS5 client")
769-
return
770-
}
771-
ctx, cancel := context.WithTimeout(context.Background(), timeout)
772-
defer cancel()
773-
dstConn, err = sockDialer.(proxy.ContextDialer).DialContext(ctx, "tcp", dst)
774-
if err != nil {
775-
ts.pa.logger.Error().Err(err).Msgf("[tproxy] Failed connecting to %s", dst)
776-
return
777-
}
778-
}
779-
defer dstConn.Close()
780-
781-
dstConnStr := fmt.Sprintf("%s->%s->%s", dstConn.LocalAddr().String(), dstConn.RemoteAddr().String(), dst)
782-
srcConnStr := fmt.Sprintf("%s->%s", srcConn.LocalAddr().String(), srcConn.RemoteAddr().String())
783-
784-
ts.pa.logger.Debug().Msgf("[tproxy] src: %s - dst: %s", srcConnStr, dstConnStr)
785-
786-
var wg sync.WaitGroup
787-
wg.Add(2)
788-
go ts.pa.transfer(&wg, dstConn, srcConn, dstConnStr, srcConnStr)
789-
go ts.pa.transfer(&wg, srcConn, dstConn, srcConnStr, dstConnStr)
790-
wg.Wait()
791-
}
792-
793-
func (ts *tproxyServer) Shutdown() {
794-
close(ts.quit)
795-
ts.listener.Close()
796-
done := make(chan struct{})
797-
go func() {
798-
ts.wg.Wait()
799-
close(done)
800-
}()
801-
802-
select {
803-
case <-done:
804-
ts.pa.logger.Info().Msg("[tproxy] Server gracefully shutdown")
805-
return
806-
case <-time.After(timeout):
807-
ts.pa.logger.Error().Msg("[tproxy] Server timed out waiting for connections to finish")
808-
return
809-
}
810-
}
811-
812631
func (p *proxyapp) Run() {
813632
done := make(chan bool)
814633
quit := make(chan os.Signal, 1)
815634
signal.Notify(quit, os.Interrupt)
816-
tproxyServer := newTproxyServer(p)
635+
var tproxyServer *tproxyServer
636+
if p.tproxyAddr != "" {
637+
tproxyServer = newTproxyServer(p)
638+
}
817639
if p.proxylist != nil {
818640
chainType := p.proxychain.Type
819641
go func() {
@@ -827,7 +649,7 @@ func (p *proxyapp) Run() {
827649
if p.httpServer != nil {
828650
go func() {
829651
<-quit
830-
if p.tproxyAddr != "" {
652+
if tproxyServer != nil {
831653
p.logger.Info().Msg("[tproxy] Server is shutting down...")
832654
tproxyServer.Shutdown()
833655
}
@@ -841,7 +663,7 @@ func (p *proxyapp) Run() {
841663
}
842664
close(done)
843665
}()
844-
if p.tproxyAddr != "" {
666+
if tproxyServer != nil {
845667
go tproxyServer.ListenAndServe()
846668
}
847669
if p.user != "" && p.pass != "" {

0 commit comments

Comments
 (0)