diff --git a/openwrt/files/luci/statistics.htm b/openwrt/files/luci/statistics.htm index b117913..c4c840c 100644 --- a/openwrt/files/luci/statistics.htm +++ b/openwrt/files/luci/statistics.htm @@ -1,14 +1,26 @@ <% -local stats = {} -local file = io.open("/var/log/ua3f/stats", "r") -if file then - for line in file:lines() do +local rewrite_stats = {} +local rewrite_stats_file = io.open("/var/log/ua3f/rewrite_stats", "r") +if rewrite_stats_file then + for line in rewrite_stats_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}) + table.insert(rewrite_stats, {host = host, count = count, origin_ua = origin_ua, mocked_ua = mocked_ua}) end end - file:close() + rewrite_stats_file:close() +end + +local pass_stats = {} +local pass_stats_file = io.open("/var/log/ua3f/passthrough_stats", "r") +if pass_stats_file then + for line in pass_stats_file:lines() do + local host, count, ua = line:match("^(%S+)%s(%d+)%s(.+)$") + if ua and count then + table.insert(pass_stats, {ua = ua, count = count, host = host}) + end + end + pass_stats_file:close() end local function rowstyle(i) @@ -17,9 +29,9 @@ end %>

<%:Statistics%>

-
<%:User-Agent Rewrite Statistics%>
- +
<%:User-Agent Rewrite Statistics%>
+
@@ -27,7 +39,7 @@ end - <% for i, item in ipairs(stats) do %> + <% for i, item in ipairs(rewrite_stats) do %> @@ -37,6 +49,24 @@ end <% end %>
<%:Host%> <%:Rewrite Count%><%:Modified User-Agent%>
<%= item.host %> <%= item.count %>
+
<%:User-Agent Pass-Through Statistics%>
+ + + + + + + + <% for i, item in ipairs(pass_stats) do %> + + + + + + <% end %> +
<%:User-Agent%><%:Pass-Through Count%><%:Last Host%>
<%= item.ua %><%= item.count %><%= item.host %>
+ + diff --git a/openwrt/po/zh_cn/ua3f.po b/openwrt/po/zh_cn/ua3f.po index 33896ce..c66ad80 100644 --- a/openwrt/po/zh_cn/ua3f.po +++ b/openwrt/po/zh_cn/ua3f.po @@ -63,4 +63,13 @@ msgid "Original User-Agent" msgstr "原始 User-Agent" msgid "Modified User-Agent" -msgstr "重写后 User-Agent" \ No newline at end of file +msgstr "重写后 User-Agent" + +msgid "User-Agent Pass-Through Statistics" +msgstr "User-Agent 放行次数实时统计" + +msgid "Pass-Through Count" +msgstr "放行次数" + +msgid "Last Host" +msgstr "最后访问地址" diff --git a/src/internal/rewrite/rewriter.go b/src/internal/rewrite/rewriter.go index 7ddcdcc..ffe2d14 100644 --- a/src/internal/rewrite/rewriter.go +++ b/src/internal/rewrite/rewriter.go @@ -148,6 +148,10 @@ func (r *Rewriter) ProxyHTTPOrRaw(dst net.Conn, src net.Conn, destAddrPort strin destAddrPort, srcAddr, originalUA, r.cache.Len()) r.cache.Add(destAddrPort, destAddrPort) } + statistics.AddPassThroughRecord(&statistics.PassThroughRecord{ + Host: destAddrPort, + UA: originalUA, + }) if err := req.Write(dst); err != nil { logrus.Errorf("[%s][%s] write error: %s", destAddrPort, srcAddr, err.Error()) return err @@ -166,10 +170,10 @@ func (r *Rewriter) ProxyHTTPOrRaw(dst net.Conn, src net.Conn, destAddrPort strin return err } - statistics.AddStat(&statistics.StatRecord{ - Host: destAddrPort, - OriginUA: originalUA, - MockedUA: mockedUA, + statistics.AddRewriteRecord(&statistics.RewriteRecord{ + Host: destAddrPort, + OriginalUA: originalUA, + MockedUA: mockedUA, }) } } @@ -196,7 +200,7 @@ func (r *Rewriter) isHTTP(reader *bufio.Reader) (bool, error) { // buildNewUA returns either a partial replacement (regex) or full overwrite. func (r *Rewriter) buildNewUA(originUA string) string { - if r.enablePartialReplace && r.uaRegex != nil { + if r.enablePartialReplace && r.uaRegex != nil && r.pattern != "" { newUA, err := r.uaRegex.Replace(originUA, r.payloadUA, -1, -1) if err != nil { logrus.Errorf("User-Agent Replace Error: %s, use full overwrite", err.Error()) diff --git a/src/internal/server/socks5/socks5.go b/src/internal/server/socks5/socks5.go index 07357fb..7ffdbe0 100644 --- a/src/internal/server/socks5/socks5.go +++ b/src/internal/server/socks5/socks5.go @@ -56,7 +56,7 @@ func (s *Server) Start() (err error) { } // Start statistics worker - go statistics.StartStatWorker() + go statistics.StartRecorder() var client net.Conn for { diff --git a/src/internal/statistics/pass.go b/src/internal/statistics/pass.go new file mode 100644 index 0000000..78e575f --- /dev/null +++ b/src/internal/statistics/pass.go @@ -0,0 +1,48 @@ +package statistics + +import ( + "fmt" + "os" + "sort" + + "github.com/sirupsen/logrus" +) + +const passthroughStatsFile = "/var/log/ua3f/passthrough_stats" + +type PassThroughRecord struct { + Host string + UA string + Count int +} + +var passThroughRecords = make(map[string]*PassThroughRecord) + +func AddPassThroughRecord(record *PassThroughRecord) { + select { + case passThroughRecordChan <- *record: + default: + } +} + +func dumpPassThroughRecords() { + f, err := os.Create(passthroughStatsFile) + if err != nil { + logrus.Errorf("create stats file error: %v", err) + return + } + defer f.Close() + + var statList []PassThroughRecord + for _, record := range passThroughRecords { + statList = append(statList, *record) + } + sort.SliceStable(statList, func(i, j int) bool { + return statList[i].Count > statList[j].Count + }) + + for _, record := range statList { + line := fmt.Sprintf("%s %d %s\n", record.Host, record.Count, record.UA) + f.WriteString(line) + } +} diff --git a/src/internal/statistics/rewrite.go b/src/internal/statistics/rewrite.go new file mode 100644 index 0000000..dc340b0 --- /dev/null +++ b/src/internal/statistics/rewrite.go @@ -0,0 +1,49 @@ +package statistics + +import ( + "fmt" + "os" + "sort" + + "github.com/sirupsen/logrus" +) + +const rewriteStatsFile = "/var/log/ua3f/rewrite_stats" + +type RewriteRecord struct { + Host string + Count int + OriginalUA string + MockedUA string +} + +var rewriteRecords = make(map[string]*RewriteRecord) + +func AddRewriteRecord(record *RewriteRecord) { + select { + case rewriteRecordChan <- *record: + default: + } +} + +func dumpRewriteRecords() { + f, err := os.Create(rewriteStatsFile) + if err != nil { + logrus.Errorf("create stats file error: %v", err) + return + } + defer f.Close() + + var statList []RewriteRecord + for _, record := range rewriteRecords { + statList = append(statList, *record) + } + sort.SliceStable(statList, func(i, j int) bool { + return statList[i].Count > statList[j].Count + }) + + for _, record := range statList { + line := fmt.Sprintf("%s %d %sSEQSEQ%s\n", record.Host, record.Count, record.OriginalUA, record.MockedUA) + f.WriteString(line) + } +} diff --git a/src/internal/statistics/statistics.go b/src/internal/statistics/statistics.go index 35cb9e7..69e40f2 100644 --- a/src/internal/statistics/statistics.go +++ b/src/internal/statistics/statistics.go @@ -1,78 +1,47 @@ package statistics import ( - "fmt" - "os" - "sort" "time" - - "github.com/sirupsen/logrus" ) -type StatRecord struct { - Host string - Count int - OriginUA string - MockedUA string -} - var ( - statChan = make(chan StatRecord, 3000) - stats = make(map[string]*StatRecord) + rewriteRecordChan = make(chan RewriteRecord, 2000) + passThroughRecordChan = make(chan PassThroughRecord, 2000) ) -const statsFile = "/var/log/ua3f/stats" - -func StartStatWorker() { +func StartRecorder() { ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() for { select { - case dest := <-statChan: - if record, exists := stats[dest.Host]; exists { - record.Count++ - record.OriginUA = dest.OriginUA - record.MockedUA = dest.MockedUA + case record := <-rewriteRecordChan: + if r, exists := rewriteRecords[record.Host]; exists { + r.Count++ + r.OriginalUA = record.OriginalUA + r.MockedUA = record.MockedUA } else { - stats[dest.Host] = &StatRecord{ - Host: dest.Host, - Count: 1, - OriginUA: dest.OriginUA, - MockedUA: dest.MockedUA, + rewriteRecords[record.Host] = &RewriteRecord{ + Host: record.Host, + Count: 1, + OriginalUA: record.OriginalUA, + MockedUA: record.MockedUA, + } + } + case record := <-passThroughRecordChan: + if r, exists := passThroughRecords[record.UA]; exists { + r.Count++ + r.Host = record.Host + } else { + passThroughRecords[record.UA] = &PassThroughRecord{ + Host: record.Host, + UA: record.UA, + Count: 1, } } case <-ticker.C: - dumpStatsToFile() + dumpRewriteRecords() + dumpPassThroughRecords() } } } - -func AddStat(dest *StatRecord) { - select { - case statChan <- *dest: - default: - } -} - -func dumpStatsToFile() { - f, err := os.Create(statsFile) - if err != nil { - logrus.Errorf("create stats file error: %v", err) - return - } - defer f.Close() - - var statList []StatRecord - for _, record := range stats { - statList = append(statList, *record) - } - sort.SliceStable(statList, func(i, j int) bool { - return statList[i].Count > 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) - } -}