feat: use ua regex pattern rule to match target dataflow

This commit is contained in:
SunBK201 2024-10-31 14:39:40 +08:00
parent eacdf80b0f
commit 7b1377d5e9
12 changed files with 58 additions and 14 deletions

View File

@ -3,7 +3,8 @@
UA3F 是下一代 HTTP User-Agent 修改方法,对外作为一个 SOCK5 服务,可以部署在路由器等设备等设备进行透明 UA 修改。
## 特性
- User-Agent 自定义
- 支持正则表达式规则匹配修改 User-Agent
- 自定义 User-Agent 内容
- 与其他网络加速代理工具共存
- LRU 高速缓存非 HTTP 域名,加速非 HTTP 流量转发
- 支持 LuCI Web 图形页面
@ -34,6 +35,14 @@ UA3F 已支持 LuCI Web 页面,可以打开 Services -> UA3F 进行相关配
![UA3F-LuCI](https://sunbk201.oss-cn-beijing.aliyuncs.com/img/ua3f-luci)
> [!NOTE]
> 设置说明:
> - Port 为 UA3F 监听端口,默认 `1080`
> - Bind Address 为 UA3F 监听地址,默认 `127.0.0.1`
> - User-Agent 为自定义 User-Agent默认 `FFF`
> - User-Agent Regex Pattern 为 User-Agent 正则表达式规则。如果流量中的 User-Agent 匹配该正则表达式,则会被修改为 User-Agent 字段的内容,否则不会被修改;如果该字段为空,则所有流量 User-Agent 都会被修改。默认 `(iPhone|iPad|Android|Macintosh|Windows|Linux)`,即只修改携带设备与系统信息的 User-Agent。
> - Log Level 为日志等级,默认 `info`, 如果需要调试排查错误可以设置为 `debug`
### 作为后台服务运行
安装脚本执行成功后可通过以下命令启动 UA3F
@ -89,6 +98,7 @@ sudo -u shellcrash /usr/bin/ua3f
- `-p <port>`: 端口号,默认 1080
- `-f <UA>`: 自定义 UA默认 FFF
- `-r <regex>`: 自定义正则匹配 User-Agent, 默认 `(iPhone|iPad|Android|Macintosh|Windows|Linux)`
- `-b <bind addr>`: 自定义绑定监听地址,默认 127.0.0.1
- `-l <log level>`: 日志等级,默认 info可选debug默认日志位置`/var/log/ua3f.log`

View File

@ -1,7 +1,7 @@
#!/bin/sh
project_name="ua3f"
release_version="0.4.0"
release_version="0.5.0"
target=cmd/ua3f.go
dist=./dist
release_dir=./bin

View File

@ -7,6 +7,7 @@ import (
"fmt"
"io"
"net"
"regexp"
"slices"
"strings"
"time"
@ -17,8 +18,10 @@ import (
"github.com/sirupsen/logrus"
)
var version = "0.4.0"
var version = "0.5.0"
var payloadByte []byte
var uaPattern string
var uaRegexp *regexp.Regexp
var cache *expirable.LRU[string, string]
var HTTP_METHOD = []string{"GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS", "TRACE", "CONNECT"}
var whitelist = []string{
@ -47,6 +50,7 @@ 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(&loglevel, "l", "info", "Log level (default: info)")
flag.Parse()
@ -55,6 +59,7 @@ func main() {
logrus.Info("UA3F v" + version)
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("Log level: %s", loglevel))
cache = expirable.NewLRU[string, string](300, nil, time.Second*600)
@ -72,6 +77,11 @@ func main() {
return
}
logrus.Info(fmt.Sprintf("Listen on %s:%d", addr, port))
uaRegexp, err = regexp.Compile(uaPattern)
if err != nil {
logrus.Fatal("Invalid User-Agent Regex Pattern: ", err)
return
}
for {
client, err := server.Accept()
if err != nil {
@ -425,15 +435,29 @@ func CopyPileline(dst io.Writer, src io.Reader, destAddrPort string) {
httpBodyOffset, err = parser.Parse(buf[:nr])
}
value, start, end := parser.FindHeader([]byte("User-Agent"))
uaStr := string(value)
if value != nil && end > start {
if slices.Contains(whitelist, string(value)) {
logrus.Debug(fmt.Sprintf("[%s][%s] Hit User-Agent Whitelist: %s, Add LRU Relay Cache, Cache Len: %d", destAddrPort, src.(*net.TCPConn).RemoteAddr().String(), string(value), cache.Len()))
isInWhiteList := false
isMatchUaPattern := true
if uaPattern != "" {
isMatchUaPattern = uaRegexp.MatchString(uaStr)
}
if slices.Contains(whitelist, uaStr) {
isInWhiteList = true
}
if isInWhiteList || !isMatchUaPattern {
if !isMatchUaPattern {
logrus.Debug(fmt.Sprintf("[%s][%s] Not Hit User-Agent Pattern: %s", destAddrPort, src.(*net.TCPConn).RemoteAddr().String(), uaStr))
}
if isInWhiteList {
logrus.Debug(fmt.Sprintf("[%s][%s] Hit User-Agent Whitelist: %s, Add LRU Relay Cache, Cache Len: %d", destAddrPort, src.(*net.TCPConn).RemoteAddr().String(), uaStr, cache.Len()))
cache.Add(destAddrPort, destAddrPort)
}
dst.Write(buf[0:nr])
io.Copy(dst, src)
cache.Add(destAddrPort, destAddrPort)
return
}
logrus.Debug(fmt.Sprintf("[%s][%s] Hit User-Agent: %s", destAddrPort, src.(*net.TCPConn).RemoteAddr().String(), string(value)))
logrus.Debug(fmt.Sprintf("[%s][%s] Hit User-Agent: %s", destAddrPort, src.(*net.TCPConn).RemoteAddr().String(), uaStr))
for i := start; i < end; i++ {
buf[i] = 32
}

View File

@ -20,7 +20,7 @@ ckcmd() {
cd /root
getcpucore
version=0.4.0
version=0.5.0
ua3f_tar=ua3f-$version-$cpucore.tar.gz
if id -u shellclash >/dev/null 2>&1; then

View File

@ -3,7 +3,7 @@ local uci = require("luci.model.uci").cursor()
ua3f = Map("ua3f",
"UA3F",
[[
<a href="https://github.com/SunBK201/UA3F" target="_blank">Version: 0.4.0</a>
<a href="https://github.com/SunBK201/UA3F" target="_blank">Version: 0.5.0</a>
<br>
Across the Campus we can reach every corner in the world.
]]
@ -34,6 +34,8 @@ 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")

View File

@ -1,5 +1,5 @@
Package: ua3f
Version: 0.4.0-1
Version: 0.5.0-1
Depends: libc, luci-compat
Source: /feed/openwrt
SourceName: UA3F
@ -7,5 +7,5 @@ License: GPL-3.0-only
Section: net
SourceDateEpoch: 1711267200
Architecture: all
Installed-Size: 2181120
Installed-Size: 2641920
Description: Implementation of the new generation of HTTP User-Agent modification methodology.

View File

@ -1,5 +1,5 @@
Package: ua3f
Version: 0.4.0-1
Version: 0.5.0-1
Depends: libc, luci-compat
Source: /feed/openwrt
SourceName: UA3F
@ -7,5 +7,5 @@ License: GPL-3.0-only
Section: net
SourceDateEpoch: 1711267200
Architecture: all
Installed-Size: 2181120
Installed-Size: 2641920
Description: Implementation of the new generation of HTTP User-Agent modification methodology.

View File

@ -5,4 +5,5 @@ 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 log_level 'info'

View File

@ -23,6 +23,7 @@ start_service() {
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 log_level "main" "log_level" "info"
chmod o+w /var/log
@ -32,6 +33,7 @@ start_service() {
procd_append_param command -b "$bind"
procd_append_param command -p $port
procd_append_param command -f "$ua"
procd_append_param command -r "$ua_regex"
procd_append_param command -l $log_level
procd_set_param respawn

View File

@ -3,7 +3,7 @@ local uci = require("luci.model.uci").cursor()
ua3f = Map("ua3f",
"UA3F",
[[
<a href="https://github.com/SunBK201/UA3F" target="_blank">Version: 0.4.0</a>
<a href="https://github.com/SunBK201/UA3F" target="_blank">Version: 0.5.0</a>
<br>
Across the Campus we can reach every corner in the world.
]]
@ -34,6 +34,8 @@ 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")

View File

@ -23,6 +23,7 @@ start_service() {
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 log_level "main" "log_level" "info"
chmod o+w /var/log
@ -32,6 +33,7 @@ start_service() {
procd_append_param command -b "$bind"
procd_append_param command -p $port
procd_append_param command -f "$ua"
procd_append_param command -r "$ua_regex"
procd_append_param command -l $log_level
procd_set_param respawn

View File

@ -5,4 +5,5 @@ 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 log_level 'info'