From f4d15229e3290a85e2afa1eab0b6235a00f77202 Mon Sep 17 00:00:00 2001 From: SunBK201 Date: Thu, 23 Oct 2025 01:35:27 +0800 Subject: [PATCH] feat: support record ua mock times and i18n --- build.sh | 4 ++ ipkg/CONTROL/control-e | 2 +- openwrt/Makefile | 5 ++ openwrt/files/luci/cbi.lua | 63 ++++++++++---------- openwrt/files/luci/controller.lua | 2 +- openwrt/files/luci/statistics.htm | 82 ++++++++++++++++++++++++++ openwrt/po/zh_cn/ua3f.po | 66 +++++++++++++++++++++ po2lmo | Bin 0 -> 52648 bytes src/{ => internal}/http/http.go | 0 src/{ => internal}/log/log.go | 3 +- src/internal/statistics/statistics.go | 78 ++++++++++++++++++++++++ src/main.go | 14 ++++- 12 files changed, 284 insertions(+), 35 deletions(-) create mode 100644 openwrt/files/luci/statistics.htm create mode 100644 openwrt/po/zh_cn/ua3f.po create mode 100755 po2lmo rename src/{ => internal}/http/http.go (100%) rename src/{ => internal}/log/log.go (96%) create mode 100644 src/internal/statistics/statistics.go diff --git a/build.sh b/build.sh index 54a2f47..7f7c605 100755 --- a/build.sh +++ b/build.sh @@ -64,12 +64,16 @@ ipkg_build=ipkg-build.sh mkdir -p $opkg_template/usr/bin mkdir -p $opkg_template/usr/lib/lua/luci/controller mkdir -p $opkg_template/usr/lib/lua/luci/model/cbi +mkdir -p $opkg_template/usr/lib/lua/luci/view/ua3f +mkdir -p $opkg_template/usr/lib/lua/luci/i18n mkdir -p $opkg_template/etc/init.d mkdir -p $opkg_template/etc/config cp openwrt/files/luci/controller.lua $opkg_template/usr/lib/lua/luci/controller/ua3f.lua cp openwrt/files/luci/cbi.lua $opkg_template/usr/lib/lua/luci/model/cbi/ua3f.lua +cp openwrt/files/luci/statistics.htm $opkg_template/usr/lib/lua/luci/view/ua3f/statistics.htm cp openwrt/files/ua3f.init $opkg_template/etc/init.d/ua3f cp openwrt/files/ua3f.uci $opkg_template/etc/config/ua3f +./po2lmo openwrt/po/zh_cn/ua3f.po $opkg_template/usr/lib/lua/luci/i18n/ua3f.zh-cn.lmo for goarch in "amd64" "arm" "arm64" "mipsle" "mips64" "riscv64" "386" "mipsle-softfloat" "mipsle-hardfloat" "armv7" "armv8"; do obj_name=$project_name-$release_version-$goarch mv $dist/bin/$obj_name $opkg_template/usr/bin/ua3f diff --git a/ipkg/CONTROL/control-e b/ipkg/CONTROL/control-e index 8dc7168..1d7ab1b 100644 --- a/ipkg/CONTROL/control-e +++ b/ipkg/CONTROL/control-e @@ -7,5 +7,5 @@ License: GPL-3.0-only Section: net SourceDateEpoch: 1711267200 Architecture: all -Installed-Size: 3686400 +Installed-Size: 3696640 Description: Implementation of the next generation of HTTP User-Agent modification methodology. diff --git a/openwrt/Makefile b/openwrt/Makefile index 3c3a597..4625dd9 100644 --- a/openwrt/Makefile +++ b/openwrt/Makefile @@ -37,6 +37,7 @@ endef define Build/Prepare $(CP) ../src/* $(PKG_BUILD_DIR) + po2lmo ./po/zh_cn/ua3f.po $(PKG_BUILD_DIR)/ua3f.zh-cn.lmo endef define Package/ua3f/conffiles @@ -57,6 +58,10 @@ define Package/ua3f/install $(INSTALL_CONF) ./files/luci/cbi.lua $(1)/usr/lib/lua/luci/model/cbi/ua3f.lua $(INSTALL_DIR) $(1)/usr/lib/lua/luci/controller/ $(INSTALL_CONF) ./files/luci/controller.lua $(1)/usr/lib/lua/luci/controller/ua3f.lua + $(INSTALL_DIR) $(1)/usr/lib/lua/luci/view/ua3f/ + $(INSTALL_CONF) ./files/luci/statistics.htm $(1)/usr/lib/lua/luci/view/ua3f/statistics.htm + $(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/ua3f.zh-cn.lmo $(1)/usr/lib/lua/luci/i18n/ua3f.zh-cn.lmo endef diff --git a/openwrt/files/luci/cbi.lua b/openwrt/files/luci/cbi.lua index 32b8a3c..6b7ff8f 100644 --- a/openwrt/files/luci/cbi.lua +++ b/openwrt/files/luci/cbi.lua @@ -9,60 +9,63 @@ ua3f = Map("ua3f", ]] ) -enable = ua3f:section(NamedSection, "enabled", "ua3f", "Status") -main = ua3f:section(NamedSection, "main", "ua3f", "Settings") +status = ua3f:section(NamedSection, "enabled", "ua3f", translate("Status")) +general = ua3f:section(NamedSection, "main", "ua3f", translate("General")) -enable:option(Flag, "enabled", "Enabled") -status = enable:option(DummyValue, "status", "Status") -status.rawhtml = true -status.cfgvalue = function(self, section) +status:option(Flag, "enabled", translate("Enabled")) + +running = status:option(DummyValue, "running", translate("Status")) +running.rawhtml = true +running.cfgvalue = function(self, section) local pid = luci.sys.exec("pidof ua3f") if pid == "" then - return "" .. "Stopped" .. "" + return "" else - return "" .. "Running" .. "" + return "" end end -main:tab("general", "General Settings") -main:tab("log", "Log") +general:tab("general", translate("Settings")) +general:tab("stats", translate("Statistics")) +general:tab("log", translate("Log")) -port = main:taboption("general", Value, "port", "Port") +port = general:taboption("general", Value, "port", translate("Port")) port.placeholder = "1080" -bind = main:taboption("general", Value, "bind", "Bind Address") +bind = general:taboption("general", Value, "bind", translate("Bind Address")) bind:value("127.0.0.1") bind:value("0.0.0.0") -log_level = main:taboption("general", ListValue, "log_level", "Log Level") +log_level = general:taboption("general", ListValue, "log_level", translate("Log Level")) log_level:value("debug") log_level:value("info") log_level:value("warn") log_level:value("error") log_level:value("fatal") log_level:value("panic") -log = main:taboption("log", TextValue, "") + +ua = general:taboption("general", Value, "ua", translate("User-Agent")) +ua.placeholder = "FFF" + +uaRegexPattern = general:taboption("general", Value, "ua_regex", translate("User-Agent Regex Pattern")) +uaRegexPattern.placeholder = "(iPhone|iPad|Android|Macintosh|Windows|Linux|Apple|Mac OS X|Mobile)" +uaRegexPattern.description = translate("Regular expression pattern for matching User-Agent") + +partialRepalce = general:taboption("general", Flag, "partial_replace", translate("Partial Replace")) +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" + +log = general:taboption("log", TextValue, "") log.readonly = true log.cfgvalue = function(self, section) return luci.sys.exec("cat /var/log/ua3f/ua3f.log") 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|Mobile)" -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 +stats = general:taboption("stats", DummyValue, "") +stats.template = "ua3f/statistics" return ua3f diff --git a/openwrt/files/luci/controller.lua b/openwrt/files/luci/controller.lua index 569b3bd..2baff4f 100644 --- a/openwrt/files/luci/controller.lua +++ b/openwrt/files/luci/controller.lua @@ -1,5 +1,5 @@ module("luci.controller.ua3f", package.seeall) function index() - entry({"admin", "services", "ua3f"}, cbi("ua3f"), "UA3F", 1) + entry({"admin", "services", "ua3f"}, cbi("ua3f"), _("UA3F"), 1) end \ No newline at end of file diff --git a/openwrt/files/luci/statistics.htm b/openwrt/files/luci/statistics.htm new file mode 100644 index 0000000..1ed7870 --- /dev/null +++ b/openwrt/files/luci/statistics.htm @@ -0,0 +1,82 @@ +<% +local stats = {} +local file = io.open("/var/log/ua3f/stats", "r") +if file then + for line in file:lines() do + local host, count, origin_ua, mocked_ua = line:match("^(%S+)%s+(%d+)%s+(.-)SEQSEQ(.-)%s*$") + if host and count then + table.insert(stats, {host = host, count = count, origin_ua = origin_ua, mocked_ua = mocked_ua}) + end + end + file:close() +end + +local function rowstyle(i) + return (i % 2 == 0) and "cbi-rowstyle-2" or "cbi-rowstyle-1" +end +%> + +

<%:Statistics%>

+
<%:User-Agent Mock Statistics%>
+ + + + + + + + + + <% for i, item in ipairs(stats) do %> + + + + + + + <% end %> +
<%:Host%><%:Modified Count%><%:Original User-Agent%><%:Mocked User-Agent%>
<%= item.host %><%= item.count %><%= item.origin_ua %><%= item.mocked_ua %>
+ + + + diff --git a/openwrt/po/zh_cn/ua3f.po b/openwrt/po/zh_cn/ua3f.po new file mode 100644 index 0000000..609e876 --- /dev/null +++ b/openwrt/po/zh_cn/ua3f.po @@ -0,0 +1,66 @@ +"Language: zh_CN\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Status" +msgstr "状态" + +msgid "General" +msgstr "常规" + +msgid "Enabled" +msgstr "启用" + +msgid "Running" +msgstr "运行中" + +msgid "Stopped" +msgstr "未运行" + +msgid "Settings" +msgstr "设置" + +msgid "Statistics" +msgstr "统计信息" + +msgid "Log" +msgstr "运行日志" + +msgid "Port" +msgstr "端口" + +msgid "Bind Address" +msgstr "绑定地址" + +msgid "Log Level" +msgstr "日志级别" + +msgid "User-Agent" +msgstr "User-Agent" + +msgid "User-Agent Regex Pattern" +msgstr "User-Agent 正则表达式" + +msgid "Regular expression pattern for matching User-Agent" +msgstr "用于匹配 User-Agent,只有匹配成功的 User-Agent 才会被修改,为空则匹配所有 User-Agent" + +msgid "Partial Replace" +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 "User-Agent Mock Statistics" +msgstr "User-Agent 修改次数实时统计" + +msgid "Host" +msgstr "地址" + +msgid "Modified Count" +msgstr "修改次数" + +msgid "Original User-Agent" +msgstr "原始 User-Agent" + +msgid "Mocked User-Agent" +msgstr "修改后 User-Agent" \ No newline at end of file diff --git a/po2lmo b/po2lmo new file mode 100755 index 0000000000000000000000000000000000000000..8b5e9ef558e9f1b3d1e392c1ae1a515a45abe7f9 GIT binary patch literal 52648 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 statList[j].Count + }) + + for _, record := range statList { + line := fmt.Sprintf("%s %d %sSEQSEQ%s\n", record.Host, record.Count, record.OriginUA, record.MockedUA) + f.WriteString(line) + } +} diff --git a/src/main.go b/src/main.go index 766c41c..646ff86 100644 --- a/src/main.go +++ b/src/main.go @@ -15,7 +15,8 @@ import ( "github.com/dlclark/regexp2" "github.com/hashicorp/golang-lru/v2/expirable" "github.com/sirupsen/logrus" - "github.com/sunbk201/ua3f/log" + "github.com/sunbk201/ua3f/internal/log" + "github.com/sunbk201/ua3f/internal/statistics" ) var ( @@ -79,6 +80,9 @@ func main() { logrus.Fatal("Invalid User-Agent Regex Pattern: ", err) return } + + go statistics.StartStatWorker() + for { client, err := server.Accept() if err != nil { @@ -468,11 +472,17 @@ func transfer(dst net.Conn, src net.Conn, destAddrPort string) { return } logrus.Debug(fmt.Sprintf("[%s][%s] Hit User-Agent: %s", destAddrPort, src.(*net.TCPConn).RemoteAddr().String(), uaStr)) - request.Header.Set("User-Agent", buildNewUA(uaStr, payload, uaRegexp, enablePartialReplace)) + mockedUA := buildNewUA(uaStr, payload, uaRegexp, enablePartialReplace) + request.Header.Set("User-Agent", mockedUA) err = request.Write(dst) if err != nil { logrus.Error(fmt.Sprintf("[%s][%s] write error after replace user-agent: %s", destAddrPort, src.(*net.TCPConn).RemoteAddr().String(), err.Error())) break } + statistics.AddStat(&statistics.StatRecord{ + Host: destAddrPort, + OriginUA: uaStr, + MockedUA: mockedUA, + }) } }