From ec53583b356217d43e3ef7c69d8cf0e4950ebadb Mon Sep 17 00:00:00 2001 From: SunBK201 Date: Sat, 9 Nov 2024 15:00:30 +0800 Subject: [PATCH] feat: support regex partial replacement --- openwrt/files/luci/cbi.lua | 24 ++++++++++++++++-------- openwrt/files/ua3f.init | 4 ++++ openwrt/files/ua3f.uci | 3 ++- src/main.go | 16 ++++++++++++++-- 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/openwrt/files/luci/cbi.lua b/openwrt/files/luci/cbi.lua index a16d4fc..dac14d3 100644 --- a/openwrt/files/luci/cbi.lua +++ b/openwrt/files/luci/cbi.lua @@ -29,13 +29,11 @@ main:tab("log", "Log") port = main:taboption("general", Value, "port", "Port") port.placeholder = "1080" + bind = main:taboption("general", Value, "bind", "Bind Address") bind:value("127.0.0.1") bind:value("0.0.0.0") -ua = main:taboption("general", Value, "ua", "User-Agent") -ua.placeholder = "FFF" -uaRegexPattern = main:taboption("general", Value, "ua_regex", "User-Agent Regex Pattern") -uaRegexPattern.placeholder = "(iPhone|iPad|Android|Macintosh|Windows|Linux)" + log_level = main:taboption("general", ListValue, "log_level", "Log Level") log_level:value("debug") log_level:value("info") @@ -43,7 +41,6 @@ log_level:value("warn") log_level:value("error") log_level:value("fatal") log_level:value("panic") - log = main:taboption("log", TextValue, "") log.readonly = true log.cfgvalue = function(self, section) @@ -51,9 +48,20 @@ log.cfgvalue = function(self, section) end log.rows = 30 +ua = main:taboption("general", Value, "ua", "User-Agent") +ua.placeholder = "FFF" + +uaRegexPattern = main:taboption("general", Value, "ua_regex", "User-Agent Regex Pattern") +uaRegexPattern.placeholder = "(iPhone|iPad|Android|Macintosh|Windows|Linux|Apple|Mac OS X)" +uaRegexPattern.description = "Regular expression pattern for matching User-Agent" + +partialRepalce = main:taboption("general", Flag, "partial_replace", "Partial Replace") +partialRepalce.description = "Replace only the matched part of the User-Agent, only works when User-Agent Regex Pattern is not empty" +partialRepalce.default = "0" + local apply = luci.http.formvalue("cbi.apply") -if apply then - io.popen("/etc/init.d/ua3f restart") -end +-- if apply then +-- io.popen("/etc/init.d/ua3f restart") +-- end return ua3f diff --git a/openwrt/files/ua3f.init b/openwrt/files/ua3f.init index 04ff66f..c961050 100755 --- a/openwrt/files/ua3f.init +++ b/openwrt/files/ua3f.init @@ -20,10 +20,13 @@ start_service() { local bind local ua local log_level + local ua_regex + local partial_replace config_get port "main" "port" "1080" config_get bind "main" "bind" "127.0.0.1" config_get ua "main" "ua" "FFF" config_get ua_regex "main" "ua_regex" "(iPhone|iPad|Android|Macintosh|Windows|Linux)" + config_get_bool partial_replace "main" "partial_replace" 0 config_get log_level "main" "log_level" "info" chmod o+w /var/log @@ -35,6 +38,7 @@ start_service() { procd_append_param command -f "$ua" procd_append_param command -r "$ua_regex" procd_append_param command -l $log_level + [ "$partial_replace" = "1" ] && procd_append_param command -s procd_set_param respawn procd_set_param stdout 1 diff --git a/openwrt/files/ua3f.uci b/openwrt/files/ua3f.uci index 68a40f2..f765cbc 100644 --- a/openwrt/files/ua3f.uci +++ b/openwrt/files/ua3f.uci @@ -5,5 +5,6 @@ config 'ua3f' 'main' option port '1080' option bind '127.0.0.1' option ua 'FFF' - option ua_regex '(iPhone|iPad|Android|Macintosh|Windows|Linux)' + option ua_regex '(iPhone|iPad|Android|Macintosh|Windows|Linux|Apple|Mac OS X)' + option partial_replace false option log_level 'info' \ No newline at end of file diff --git a/src/main.go b/src/main.go index 9d6b715..b802050 100644 --- a/src/main.go +++ b/src/main.go @@ -19,8 +19,10 @@ import ( var version = "0.5.1" var payloadByte []byte +var payload string var uaPattern string var uaRegexp *regexp2.Regexp +var enablePartialReplace bool var cache *expirable.LRU[string, string] var HTTP_METHOD = []string{"GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS", "TRACE", "CONNECT"} var whitelist = []string{ @@ -41,7 +43,6 @@ const RDBUF = 1024 * 8 // } func main() { - var payload string var addr string var port int var loglevel string @@ -49,7 +50,8 @@ func main() { flag.StringVar(&addr, "b", "127.0.0.1", "bind address (default: 127.0.0.1)") flag.IntVar(&port, "p", 1080, "port") flag.StringVar(&payload, "f", "FFF", "User-Agent") - flag.StringVar(&uaPattern, "r", "(iPhone|iPad|Android|Macintosh|Windows|Linux)", "UA-Pattern") + flag.StringVar(&uaPattern, "r", "(iPhone|iPad|Android|Macintosh|Windows|Linux|Apple|Mac OS X)", "UA-Pattern") + flag.BoolVar(&enablePartialReplace, "s", false, "Enable Regex Partial Replace") flag.StringVar(&loglevel, "l", "info", "Log level (default: info)") flag.Parse() @@ -59,6 +61,7 @@ func main() { logrus.Info(fmt.Sprintf("Port: %d", port)) logrus.Info(fmt.Sprintf("User-Agent: %s", payload)) logrus.Info(fmt.Sprintf("User-Agent Regex Pattern: %s", uaPattern)) + logrus.Info(fmt.Sprintf("Enable Partial Replace: %v", enablePartialReplace)) logrus.Info(fmt.Sprintf("Log level: %s", loglevel)) cache = expirable.NewLRU[string, string](300, nil, time.Second*600) @@ -474,6 +477,15 @@ func CopyPileline(dst io.Writer, src io.Reader, destAddrPort string) { for i := start; i < end; i++ { buf[i] = 32 } + if enablePartialReplace && uaRegexp != nil { + newUaHearder, err := uaRegexp.Replace(uaStr, payload, -1, -1) + if err != nil { + logrus.Error(fmt.Sprintf("[%s][%s] User-Agent Replace Error: %s", destAddrPort, src.(*net.TCPConn).RemoteAddr().String(), err.Error())) + payloadByte = []byte(payload) + } else { + payloadByte = []byte(newUaHearder) + } + } for i := range payloadByte { if start+i >= end { break