diff --git a/openwrt/files/luci/cbi.lua b/openwrt/files/luci/cbi.lua index ae7dd33..4b0cb8a 100644 --- a/openwrt/files/luci/cbi.lua +++ b/openwrt/files/luci/cbi.lua @@ -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") 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.readonly = true log.rows = 30 diff --git a/openwrt/files/ua3f.init b/openwrt/files/ua3f.init index e9e9c95..68289a6 100755 --- a/openwrt/files/ua3f.init +++ b/openwrt/files/ua3f.init @@ -455,7 +455,7 @@ start_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 port "main" "port" "1080" 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 log_level "main" "log_level" "info" 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="$server_mode" @@ -477,6 +478,7 @@ start_service() { LOG "User-Agent Regex: $ua_regex" LOG "Log level: $log_level" LOG "Partial Replace: $partial_replace" + LOG "Direct Forward: $direct_forward" LOG "Set TTL: $SET_TTL" set_ua3f_group @@ -579,6 +581,7 @@ start_service() { procd_append_param command -r "$ua_regex" procd_append_param command -l "$log_level" [ "$partial_replace" = "1" ] && procd_append_param command -s + [ "$direct_forward" = "1" ] && procd_append_param command -d procd_set_param respawn procd_set_param stdout 1 diff --git a/openwrt/files/ua3f.uci b/openwrt/files/ua3f.uci index 921df61..1593060 100644 --- a/openwrt/files/ua3f.uci +++ b/openwrt/files/ua3f.uci @@ -7,7 +7,8 @@ config 'ua3f' 'main' option bind '0.0.0.0' 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 partial_replace false + option partial_replace '0' + option direct_forward '0' option log_level 'error' option log_lines '1000' option set_ttl '0' \ No newline at end of file diff --git a/openwrt/po/zh_cn/ua3f.po b/openwrt/po/zh_cn/ua3f.po index c3557e5..d03b061 100644 --- a/openwrt/po/zh_cn/ua3f.po +++ b/openwrt/po/zh_cn/ua3f.po @@ -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" msgstr "仅替换 User-Agent 正则匹配的部分,仅在 User-Agent 正则表达式非空时有效" +msgid "Direct Forward" +msgstr "直接转发" + +msgid "Directly forward packets without rewriting" +msgstr "不进行重写直接转发数据包" + msgid "User-Agent Rewrite Statistics" msgstr "User-Agent 重写次数实时统计" diff --git a/src/go.mod b/src/go.mod index 0584ce6..f0065c1 100644 --- a/src/go.mod +++ b/src/go.mod @@ -6,7 +6,7 @@ require ( github.com/dlclark/regexp2 v1.11.4 github.com/hashicorp/golang-lru/v2 v2.0.7 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 ) diff --git a/src/go.sum b/src/go.sum index e08cf79..cf8e4c1 100644 --- a/src/go.sum +++ b/src/go.sum @@ -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/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 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.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= diff --git a/src/internal/config/config.go b/src/internal/config/config.go index e896c83..35135c7 100644 --- a/src/internal/config/config.go +++ b/src/internal/config/config.go @@ -14,47 +14,51 @@ const ( ) type Config struct { - ServerMode string - BindAddr string - Port int - ListenAddr string - LogLevel string - PayloadUA string - UARegex string - EnablePartialReplace bool + ServerMode string + BindAddr string + Port int + ListenAddr string + LogLevel string + PayloadUA string + UARegex string + PartialReplace bool + DirectForward bool } func Parse() (*Config, bool) { var ( - serverMode string - bindAddr string - port int - loglevel string - payloadUA string - uaRegx string - partial bool - showVer bool + serverMode string + bindAddr string + port int + loglevel string + payloadUA string + uaRegx string + partial bool + directForward bool + showVer bool ) - flag.StringVar(&serverMode, "m", ServerModeSocks5, "server mode: HTTP, SOCKS5, TPROXY, REDIRECT (default: SOCKS5)") - flag.StringVar(&bindAddr, "b", "127.0.0.1", "bind address (default: 127.0.0.1)") - flag.IntVar(&port, "p", 1080, "port") + flag.StringVar(&serverMode, "m", ServerModeSocks5, "Server mode: HTTP, SOCKS5, TPROXY, REDIRECT") + flag.StringVar(&bindAddr, "b", "127.0.0.1", "Bind address") + flag.IntVar(&port, "p", 1080, "Port") + flag.StringVar(&loglevel, "l", "info", "Log level") flag.StringVar(&payloadUA, "f", "FFF", "User-Agent") - flag.StringVar(&uaRegx, "r", "", "UA-Pattern") - flag.BoolVar(&partial, "s", false, "Enable Regex Partial Replace") - flag.StringVar(&loglevel, "l", "info", "Log level (default: info)") - flag.BoolVar(&showVer, "v", false, "show version") + flag.StringVar(&uaRegx, "r", "", "User-Agent regex") + flag.BoolVar(&partial, "s", false, "Enable regex partial replace") + flag.BoolVar(&directForward, "d", false, "Pure Forwarding (no User-Agent rewriting)") + flag.BoolVar(&showVer, "v", false, "Show version") flag.Parse() cfg := &Config{ - ServerMode: strings.ToUpper(serverMode), - BindAddr: bindAddr, - Port: port, - ListenAddr: fmt.Sprintf("%s:%d", bindAddr, port), - LogLevel: loglevel, - PayloadUA: payloadUA, - UARegex: uaRegx, - EnablePartialReplace: partial, + ServerMode: strings.ToUpper(serverMode), + BindAddr: bindAddr, + Port: port, + ListenAddr: fmt.Sprintf("%s:%d", bindAddr, port), + LogLevel: loglevel, + PayloadUA: payloadUA, + UARegex: uaRegx, + DirectForward: directForward, + PartialReplace: partial, } if serverMode == ServerModeRedirect { cfg.BindAddr = "0.0.0.0" diff --git a/src/internal/log/log.go b/src/internal/log/log.go index 522ff55..89b7233 100644 --- a/src/internal/log/log.go +++ b/src/internal/log/log.go @@ -74,7 +74,8 @@ func LogHeader(version string, cfg *config.Config) { logrus.Infof("Listen on %s", cfg.ListenAddr) logrus.Infof("User-Agent: %s", cfg.PayloadUA) 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) } diff --git a/src/internal/rewrite/rewriter.go b/src/internal/rewrite/rewriter.go index 1efbcf1..168a99b 100644 --- a/src/internal/rewrite/rewriter.go +++ b/src/internal/rewrite/rewriter.go @@ -44,7 +44,7 @@ func New(cfg *config.Config) (*Rewriter, error) { return &Rewriter{ payloadUA: cfg.PayloadUA, pattern: cfg.UARegex, - partialReplace: cfg.EnablePartialReplace, + partialReplace: cfg.PartialReplace, uaRegex: uaRegex, Cache: expirable.NewLRU[string, struct{}](1024, nil, 30*time.Minute), whitelist: []string{ diff --git a/src/internal/rewrite/rewriter_test.go b/src/internal/rewrite/rewriter_test.go index d96ab78..5356c2a 100644 --- a/src/internal/rewrite/rewriter_test.go +++ b/src/internal/rewrite/rewriter_test.go @@ -26,9 +26,9 @@ func (m *mockConn) SetWriteDeadline(t time.Time) error { return nil } func newTestRewriter(t *testing.T) *Rewriter { cfg := &config.Config{ - UARegex: "TestUA", - PayloadUA: "MockUA/1.0", - EnablePartialReplace: false, + UARegex: "TestUA", + PayloadUA: "MockUA/1.0", + PartialReplace: false, } rewriter, err := New(cfg) assert.NoError(t, err) @@ -37,15 +37,15 @@ func newTestRewriter(t *testing.T) *Rewriter { func TestNewRewriter(t *testing.T) { cfg := &config.Config{ - UARegex: "TestUA", - PayloadUA: "FFF0", - EnablePartialReplace: false, + UARegex: "TestUA", + PayloadUA: "FFF0", + PartialReplace: false, } rewriter, err := New(cfg) assert.NoError(t, err) assert.Equal(t, cfg.PayloadUA, rewriter.payloadUA) 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.Cache) } diff --git a/src/internal/server/http/http.go b/src/internal/server/http/http.go index 5255764..da1b43c 100644 --- a/src/internal/server/http/http.go +++ b/src/internal/server/http/http.go @@ -58,10 +58,18 @@ func (s *Server) handleHTTP(w http.ResponseWriter, req *http.Request) { } defer target.Close() - err = s.rewriteAndForward(target, req, req.Host, req.RemoteAddr) - if err != nil { - http.Error(w, err.Error(), http.StatusServiceUnavailable) - return + if s.cfg.DirectForward { + err = req.Write(target) + if err != nil { + 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) if err != nil { @@ -123,6 +131,11 @@ func (s *Server) ForwardTCP(client, target net.Conn, destAddr string) { // Server -> Client (raw) go utils.CopyHalf(client, target) + if s.cfg.DirectForward { + // Client -> Server (raw) + go utils.CopyHalf(target, client) + return + } // Client -> Server (rewriter) go utils.ProxyHalf(target, client, s.rw, destAddr) } diff --git a/src/internal/server/redirect/redirect_linux.go b/src/internal/server/redirect/redirect_linux.go index cbf247f..c436400 100644 --- a/src/internal/server/redirect/redirect_linux.go +++ b/src/internal/server/redirect/redirect_linux.go @@ -71,6 +71,11 @@ func (s *Server) ForwardTCP(client, target net.Conn, destAddr string) { // Server -> Client (raw) go utils.CopyHalf(client, target) + if s.cfg.DirectForward { + // Client -> Server (raw) + go utils.CopyHalf(target, client) + return + } // Client -> Server (rewriter) go utils.ProxyHalf(target, client, s.rw, destAddr) } diff --git a/src/internal/server/socks5/socks5.go b/src/internal/server/socks5/socks5.go index 1a9c4c9..d3a7302 100644 --- a/src/internal/server/socks5/socks5.go +++ b/src/internal/server/socks5/socks5.go @@ -213,6 +213,11 @@ func (s *Server) ForwardTCP(client, target net.Conn, destAddr string) { // Server -> Client (raw) go utils.CopyHalf(client, target) + if s.cfg.DirectForward { + // Client -> Server (raw) + go utils.CopyHalf(target, client) + return + } // Client -> Server (rewriter) go utils.ProxyHalf(target, client, s.rw, destAddr) } diff --git a/src/internal/server/tproxy/tproxy_linux.go b/src/internal/server/tproxy/tproxy_linux.go index 695ed26..1e68d82 100644 --- a/src/internal/server/tproxy/tproxy_linux.go +++ b/src/internal/server/tproxy/tproxy_linux.go @@ -92,6 +92,11 @@ func (s *Server) ForwardTCP(client, target net.Conn, destAddr string) { // Server -> Client (raw) go utils.CopyHalf(client, target) + if s.cfg.DirectForward { + // Client -> Server (raw) + go utils.CopyHalf(target, client) + return + } // Client -> Server (rewriter) go utils.ProxyHalf(target, client, s.rw, destAddr) }