forked from SagerNet/sing-tun
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathredirect_server.go
More file actions
133 lines (121 loc) · 3.52 KB
/
Copy pathredirect_server.go
File metadata and controls
133 lines (121 loc) · 3.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//go:build linux
package tun
import (
"context"
"errors"
"net"
"net/netip"
"sync/atomic"
"syscall"
"time"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"golang.org/x/sys/unix"
)
type RedirectServer struct {
ctx context.Context
handler N.TCPConnectionHandlerEx
logger logger.Logger
listenAddress netip.Addr
transparent bool
externalTransparent bool
listenMark uint32
listener *net.TCPListener
inShutdown atomic.Bool
}
func (s *RedirectServer) SetExternalTransparent() {
s.transparent = true
s.externalTransparent = true
}
func (s *RedirectServer) ListenerFileDescriptor() (int, error) {
return control.Conn0(s.listener, func(fd uintptr) (int, error) {
return unix.Dup(int(fd))
})
}
func NewRedirectServer(ctx context.Context, handler N.TCPConnectionHandlerEx, logger logger.Logger, listenAddress netip.Addr) *RedirectServer {
return &RedirectServer{
ctx: ctx,
handler: handler,
logger: logger,
listenAddress: listenAddress,
}
}
func (s *RedirectServer) Start() error {
var listenConfig net.ListenConfig
// listenConfig.KeepAlive = C.TCPKeepAliveInitial
listenConfig.KeepAlive = 10 * time.Minute
if s.transparent && !s.externalTransparent {
listenConfig.Control = control.Append(func(network string, address string, conn syscall.RawConn) error {
return control.Raw(conn, func(fd uintptr) error {
err := syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_TRANSPARENT, 1)
if err != nil {
return E.Cause(err, "set IPV6_TRANSPARENT")
}
return nil
})
}, control.RoutingMark(s.listenMark))
}
listener, err := listenConfig.Listen(s.ctx, M.NetworkFromNetAddr("tcp", s.listenAddress), M.SocksaddrFrom(s.listenAddress, 0).String())
if err != nil {
return err
}
s.listener = listener.(*net.TCPListener)
go s.loopIn()
return nil
}
func (s *RedirectServer) Port() uint16 {
return M.AddrPortFromNet(s.listener.Addr()).Port()
}
func (s *RedirectServer) Close() error {
s.inShutdown.Store(true)
return s.listener.Close()
}
func (s *RedirectServer) loopIn() {
var retryDelay time.Duration
for {
conn, err := s.listener.AcceptTCP()
if err != nil {
if s.inShutdown.Load() && E.IsClosed(err) {
return
}
var netError net.Error
//nolint:staticcheck
if errors.As(err, &netError) && netError.Temporary() {
if retryDelay == 0 {
retryDelay = 5 * time.Millisecond
} else {
retryDelay *= 2
}
if retryDelay > time.Second {
retryDelay = time.Second
}
s.logger.Error("accept: ", err, ": retrying in ", retryDelay)
time.Sleep(retryDelay)
continue
}
s.listener.Close()
s.logger.Error("serve error: ", err)
return
}
retryDelay = 0
source := M.SocksaddrFromNet(conn.RemoteAddr()).Unwrap()
var destination M.Socksaddr
if s.transparent && !source.IsIPv4() {
destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
} else {
var originalDestination netip.AddrPort
originalDestination, err = control.GetOriginalDestination(conn)
if err != nil {
_ = conn.SetLinger(0)
_ = conn.Close()
s.logger.Error("process redirect connection from ", source, ": invalid connection: ", err)
continue
}
destination = M.SocksaddrFromNetIP(originalDestination).Unwrap()
}
go s.handler.NewConnectionEx(s.ctx, conn, source, destination, nil)
}
}