mirror of
https://github.com/SunBK201/UA3F.git
synced 2025-12-16 16:57:08 +00:00
feat: add direct forwarding option
This commit is contained in:
parent
47a2a1d29a
commit
77ca782fa2
@ -66,6 +66,11 @@ partialRepalce.description =
|
|||||||
translate("Replace only the matched part of the User-Agent, only works when User-Agent Regex Pattern is not empty")
|
translate("Replace only the matched part of the User-Agent, only works when User-Agent Regex Pattern is not empty")
|
||||||
partialRepalce.default = "0"
|
partialRepalce.default = "0"
|
||||||
|
|
||||||
|
directForward = general:taboption("general", Flag, "direct_forward", translate("Direct Forward"))
|
||||||
|
directForward.description =
|
||||||
|
translate("Directly forward packets without rewriting")
|
||||||
|
directForward.default = "0"
|
||||||
|
|
||||||
log = general:taboption("log", TextValue, "log")
|
log = general:taboption("log", TextValue, "log")
|
||||||
log.readonly = true
|
log.readonly = true
|
||||||
log.rows = 30
|
log.rows = 30
|
||||||
|
|||||||
@ -455,7 +455,7 @@ start_service() {
|
|||||||
|
|
||||||
LOG "Starting $NAME service..."
|
LOG "Starting $NAME service..."
|
||||||
|
|
||||||
local port bind ua log_level ua_regex partial_replace set_ttl
|
local port bind ua log_level ua_regex partial_replace set_ttl direct_forward
|
||||||
config_get server_mode "main" "server_mode" "SOCKS5"
|
config_get server_mode "main" "server_mode" "SOCKS5"
|
||||||
config_get port "main" "port" "1080"
|
config_get port "main" "port" "1080"
|
||||||
config_get bind "main" "bind" "127.0.0.1"
|
config_get bind "main" "bind" "127.0.0.1"
|
||||||
@ -464,6 +464,7 @@ start_service() {
|
|||||||
config_get_bool partial_replace "main" "partial_replace" 0
|
config_get_bool partial_replace "main" "partial_replace" 0
|
||||||
config_get log_level "main" "log_level" "info"
|
config_get log_level "main" "log_level" "info"
|
||||||
config_get_bool set_ttl "main" "set_ttl" 0
|
config_get_bool set_ttl "main" "set_ttl" 0
|
||||||
|
config_get_bool direct_forward "main" "direct_forward" 0
|
||||||
|
|
||||||
SERVER_MODE="$(echo "$server_mode" | tr '[:lower:]' '[:upper:]')"
|
SERVER_MODE="$(echo "$server_mode" | tr '[:lower:]' '[:upper:]')"
|
||||||
SERVER_MODE="$server_mode"
|
SERVER_MODE="$server_mode"
|
||||||
@ -477,6 +478,7 @@ start_service() {
|
|||||||
LOG "User-Agent Regex: $ua_regex"
|
LOG "User-Agent Regex: $ua_regex"
|
||||||
LOG "Log level: $log_level"
|
LOG "Log level: $log_level"
|
||||||
LOG "Partial Replace: $partial_replace"
|
LOG "Partial Replace: $partial_replace"
|
||||||
|
LOG "Direct Forward: $direct_forward"
|
||||||
LOG "Set TTL: $SET_TTL"
|
LOG "Set TTL: $SET_TTL"
|
||||||
|
|
||||||
set_ua3f_group
|
set_ua3f_group
|
||||||
@ -579,6 +581,7 @@ start_service() {
|
|||||||
procd_append_param command -r "$ua_regex"
|
procd_append_param command -r "$ua_regex"
|
||||||
procd_append_param command -l "$log_level"
|
procd_append_param command -l "$log_level"
|
||||||
[ "$partial_replace" = "1" ] && procd_append_param command -s
|
[ "$partial_replace" = "1" ] && procd_append_param command -s
|
||||||
|
[ "$direct_forward" = "1" ] && procd_append_param command -d
|
||||||
|
|
||||||
procd_set_param respawn
|
procd_set_param respawn
|
||||||
procd_set_param stdout 1
|
procd_set_param stdout 1
|
||||||
|
|||||||
@ -7,7 +7,8 @@ config 'ua3f' 'main'
|
|||||||
option bind '0.0.0.0'
|
option bind '0.0.0.0'
|
||||||
option ua 'FFF'
|
option ua 'FFF'
|
||||||
option ua_regex '(Apple|iPhone|iPad|Macintosh|Mac OS X|Mac|Darwin|Microsoft|Windows|Linux|Android|OpenHarmony|HUAWEI|OPPO|Vivo|XiaoMi|Mobile|Dalvik)'
|
option ua_regex '(Apple|iPhone|iPad|Macintosh|Mac OS X|Mac|Darwin|Microsoft|Windows|Linux|Android|OpenHarmony|HUAWEI|OPPO|Vivo|XiaoMi|Mobile|Dalvik)'
|
||||||
option partial_replace false
|
option partial_replace '0'
|
||||||
|
option direct_forward '0'
|
||||||
option log_level 'error'
|
option log_level 'error'
|
||||||
option log_lines '1000'
|
option log_lines '1000'
|
||||||
option set_ttl '0'
|
option set_ttl '0'
|
||||||
@ -56,6 +56,12 @@ msgstr "部分替换"
|
|||||||
msgid "Replace only the matched part of the User-Agent, only works when User-Agent Regex Pattern is not empty"
|
msgid "Replace only the matched part of the User-Agent, only works when User-Agent Regex Pattern is not empty"
|
||||||
msgstr "仅替换 User-Agent 正则匹配的部分,仅在 User-Agent 正则表达式非空时有效"
|
msgstr "仅替换 User-Agent 正则匹配的部分,仅在 User-Agent 正则表达式非空时有效"
|
||||||
|
|
||||||
|
msgid "Direct Forward"
|
||||||
|
msgstr "直接转发"
|
||||||
|
|
||||||
|
msgid "Directly forward packets without rewriting"
|
||||||
|
msgstr "不进行重写直接转发数据包"
|
||||||
|
|
||||||
msgid "User-Agent Rewrite Statistics"
|
msgid "User-Agent Rewrite Statistics"
|
||||||
msgstr "User-Agent 重写次数实时统计"
|
msgstr "User-Agent 重写次数实时统计"
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ require (
|
|||||||
github.com/dlclark/regexp2 v1.11.4
|
github.com/dlclark/regexp2 v1.11.4
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
golang.org/x/sys v0.26.0
|
golang.org/x/sys v0.30.0
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -14,8 +14,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||||
|
|||||||
@ -14,47 +14,51 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ServerMode string
|
ServerMode string
|
||||||
BindAddr string
|
BindAddr string
|
||||||
Port int
|
Port int
|
||||||
ListenAddr string
|
ListenAddr string
|
||||||
LogLevel string
|
LogLevel string
|
||||||
PayloadUA string
|
PayloadUA string
|
||||||
UARegex string
|
UARegex string
|
||||||
EnablePartialReplace bool
|
PartialReplace bool
|
||||||
|
DirectForward bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func Parse() (*Config, bool) {
|
func Parse() (*Config, bool) {
|
||||||
var (
|
var (
|
||||||
serverMode string
|
serverMode string
|
||||||
bindAddr string
|
bindAddr string
|
||||||
port int
|
port int
|
||||||
loglevel string
|
loglevel string
|
||||||
payloadUA string
|
payloadUA string
|
||||||
uaRegx string
|
uaRegx string
|
||||||
partial bool
|
partial bool
|
||||||
showVer bool
|
directForward bool
|
||||||
|
showVer bool
|
||||||
)
|
)
|
||||||
|
|
||||||
flag.StringVar(&serverMode, "m", ServerModeSocks5, "server mode: HTTP, SOCKS5, TPROXY, REDIRECT (default: SOCKS5)")
|
flag.StringVar(&serverMode, "m", ServerModeSocks5, "Server mode: HTTP, SOCKS5, TPROXY, REDIRECT")
|
||||||
flag.StringVar(&bindAddr, "b", "127.0.0.1", "bind address (default: 127.0.0.1)")
|
flag.StringVar(&bindAddr, "b", "127.0.0.1", "Bind address")
|
||||||
flag.IntVar(&port, "p", 1080, "port")
|
flag.IntVar(&port, "p", 1080, "Port")
|
||||||
|
flag.StringVar(&loglevel, "l", "info", "Log level")
|
||||||
flag.StringVar(&payloadUA, "f", "FFF", "User-Agent")
|
flag.StringVar(&payloadUA, "f", "FFF", "User-Agent")
|
||||||
flag.StringVar(&uaRegx, "r", "", "UA-Pattern")
|
flag.StringVar(&uaRegx, "r", "", "User-Agent regex")
|
||||||
flag.BoolVar(&partial, "s", false, "Enable Regex Partial Replace")
|
flag.BoolVar(&partial, "s", false, "Enable regex partial replace")
|
||||||
flag.StringVar(&loglevel, "l", "info", "Log level (default: info)")
|
flag.BoolVar(&directForward, "d", false, "Pure Forwarding (no User-Agent rewriting)")
|
||||||
flag.BoolVar(&showVer, "v", false, "show version")
|
flag.BoolVar(&showVer, "v", false, "Show version")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
cfg := &Config{
|
cfg := &Config{
|
||||||
ServerMode: strings.ToUpper(serverMode),
|
ServerMode: strings.ToUpper(serverMode),
|
||||||
BindAddr: bindAddr,
|
BindAddr: bindAddr,
|
||||||
Port: port,
|
Port: port,
|
||||||
ListenAddr: fmt.Sprintf("%s:%d", bindAddr, port),
|
ListenAddr: fmt.Sprintf("%s:%d", bindAddr, port),
|
||||||
LogLevel: loglevel,
|
LogLevel: loglevel,
|
||||||
PayloadUA: payloadUA,
|
PayloadUA: payloadUA,
|
||||||
UARegex: uaRegx,
|
UARegex: uaRegx,
|
||||||
EnablePartialReplace: partial,
|
DirectForward: directForward,
|
||||||
|
PartialReplace: partial,
|
||||||
}
|
}
|
||||||
if serverMode == ServerModeRedirect {
|
if serverMode == ServerModeRedirect {
|
||||||
cfg.BindAddr = "0.0.0.0"
|
cfg.BindAddr = "0.0.0.0"
|
||||||
|
|||||||
@ -74,7 +74,8 @@ func LogHeader(version string, cfg *config.Config) {
|
|||||||
logrus.Infof("Listen on %s", cfg.ListenAddr)
|
logrus.Infof("Listen on %s", cfg.ListenAddr)
|
||||||
logrus.Infof("User-Agent: %s", cfg.PayloadUA)
|
logrus.Infof("User-Agent: %s", cfg.PayloadUA)
|
||||||
logrus.Infof("User-Agent Regex: '%s'", cfg.UARegex)
|
logrus.Infof("User-Agent Regex: '%s'", cfg.UARegex)
|
||||||
logrus.Infof("Partial Replace: %v", cfg.EnablePartialReplace)
|
logrus.Infof("Partial Replace: %v", cfg.PartialReplace)
|
||||||
|
logrus.Infof("Direct Forward: %v", cfg.DirectForward)
|
||||||
logrus.Infof("Log level: %s", cfg.LogLevel)
|
logrus.Infof("Log level: %s", cfg.LogLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -44,7 +44,7 @@ func New(cfg *config.Config) (*Rewriter, error) {
|
|||||||
return &Rewriter{
|
return &Rewriter{
|
||||||
payloadUA: cfg.PayloadUA,
|
payloadUA: cfg.PayloadUA,
|
||||||
pattern: cfg.UARegex,
|
pattern: cfg.UARegex,
|
||||||
partialReplace: cfg.EnablePartialReplace,
|
partialReplace: cfg.PartialReplace,
|
||||||
uaRegex: uaRegex,
|
uaRegex: uaRegex,
|
||||||
Cache: expirable.NewLRU[string, struct{}](1024, nil, 30*time.Minute),
|
Cache: expirable.NewLRU[string, struct{}](1024, nil, 30*time.Minute),
|
||||||
whitelist: []string{
|
whitelist: []string{
|
||||||
|
|||||||
@ -26,9 +26,9 @@ func (m *mockConn) SetWriteDeadline(t time.Time) error { return nil }
|
|||||||
|
|
||||||
func newTestRewriter(t *testing.T) *Rewriter {
|
func newTestRewriter(t *testing.T) *Rewriter {
|
||||||
cfg := &config.Config{
|
cfg := &config.Config{
|
||||||
UARegex: "TestUA",
|
UARegex: "TestUA",
|
||||||
PayloadUA: "MockUA/1.0",
|
PayloadUA: "MockUA/1.0",
|
||||||
EnablePartialReplace: false,
|
PartialReplace: false,
|
||||||
}
|
}
|
||||||
rewriter, err := New(cfg)
|
rewriter, err := New(cfg)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -37,15 +37,15 @@ func newTestRewriter(t *testing.T) *Rewriter {
|
|||||||
|
|
||||||
func TestNewRewriter(t *testing.T) {
|
func TestNewRewriter(t *testing.T) {
|
||||||
cfg := &config.Config{
|
cfg := &config.Config{
|
||||||
UARegex: "TestUA",
|
UARegex: "TestUA",
|
||||||
PayloadUA: "FFF0",
|
PayloadUA: "FFF0",
|
||||||
EnablePartialReplace: false,
|
PartialReplace: false,
|
||||||
}
|
}
|
||||||
rewriter, err := New(cfg)
|
rewriter, err := New(cfg)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, cfg.PayloadUA, rewriter.payloadUA)
|
assert.Equal(t, cfg.PayloadUA, rewriter.payloadUA)
|
||||||
assert.Equal(t, cfg.UARegex, rewriter.pattern)
|
assert.Equal(t, cfg.UARegex, rewriter.pattern)
|
||||||
assert.Equal(t, cfg.EnablePartialReplace, rewriter.partialReplace)
|
assert.Equal(t, cfg.PartialReplace, rewriter.partialReplace)
|
||||||
assert.NotNil(t, rewriter.uaRegex)
|
assert.NotNil(t, rewriter.uaRegex)
|
||||||
assert.NotNil(t, rewriter.Cache)
|
assert.NotNil(t, rewriter.Cache)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,10 +58,18 @@ func (s *Server) handleHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
defer target.Close()
|
defer target.Close()
|
||||||
|
|
||||||
err = s.rewriteAndForward(target, req, req.Host, req.RemoteAddr)
|
if s.cfg.DirectForward {
|
||||||
if err != nil {
|
err = req.Write(target)
|
||||||
http.Error(w, err.Error(), http.StatusServiceUnavailable)
|
if err != nil {
|
||||||
return
|
http.Error(w, err.Error(), http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = s.rewriteAndForward(target, req, req.Host, req.RemoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
resp, err := http.ReadResponse(bufio.NewReader(target), req)
|
resp, err := http.ReadResponse(bufio.NewReader(target), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -123,6 +131,11 @@ func (s *Server) ForwardTCP(client, target net.Conn, destAddr string) {
|
|||||||
// Server -> Client (raw)
|
// Server -> Client (raw)
|
||||||
go utils.CopyHalf(client, target)
|
go utils.CopyHalf(client, target)
|
||||||
|
|
||||||
|
if s.cfg.DirectForward {
|
||||||
|
// Client -> Server (raw)
|
||||||
|
go utils.CopyHalf(target, client)
|
||||||
|
return
|
||||||
|
}
|
||||||
// Client -> Server (rewriter)
|
// Client -> Server (rewriter)
|
||||||
go utils.ProxyHalf(target, client, s.rw, destAddr)
|
go utils.ProxyHalf(target, client, s.rw, destAddr)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,6 +71,11 @@ func (s *Server) ForwardTCP(client, target net.Conn, destAddr string) {
|
|||||||
// Server -> Client (raw)
|
// Server -> Client (raw)
|
||||||
go utils.CopyHalf(client, target)
|
go utils.CopyHalf(client, target)
|
||||||
|
|
||||||
|
if s.cfg.DirectForward {
|
||||||
|
// Client -> Server (raw)
|
||||||
|
go utils.CopyHalf(target, client)
|
||||||
|
return
|
||||||
|
}
|
||||||
// Client -> Server (rewriter)
|
// Client -> Server (rewriter)
|
||||||
go utils.ProxyHalf(target, client, s.rw, destAddr)
|
go utils.ProxyHalf(target, client, s.rw, destAddr)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -213,6 +213,11 @@ func (s *Server) ForwardTCP(client, target net.Conn, destAddr string) {
|
|||||||
// Server -> Client (raw)
|
// Server -> Client (raw)
|
||||||
go utils.CopyHalf(client, target)
|
go utils.CopyHalf(client, target)
|
||||||
|
|
||||||
|
if s.cfg.DirectForward {
|
||||||
|
// Client -> Server (raw)
|
||||||
|
go utils.CopyHalf(target, client)
|
||||||
|
return
|
||||||
|
}
|
||||||
// Client -> Server (rewriter)
|
// Client -> Server (rewriter)
|
||||||
go utils.ProxyHalf(target, client, s.rw, destAddr)
|
go utils.ProxyHalf(target, client, s.rw, destAddr)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -92,6 +92,11 @@ func (s *Server) ForwardTCP(client, target net.Conn, destAddr string) {
|
|||||||
// Server -> Client (raw)
|
// Server -> Client (raw)
|
||||||
go utils.CopyHalf(client, target)
|
go utils.CopyHalf(client, target)
|
||||||
|
|
||||||
|
if s.cfg.DirectForward {
|
||||||
|
// Client -> Server (raw)
|
||||||
|
go utils.CopyHalf(target, client)
|
||||||
|
return
|
||||||
|
}
|
||||||
// Client -> Server (rewriter)
|
// Client -> Server (rewriter)
|
||||||
go utils.ProxyHalf(target, client, s.rw, destAddr)
|
go utils.ProxyHalf(target, client, s.rw, destAddr)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user