mirror of
https://github.com/SunBK201/UA3F.git
synced 2025-12-18 01:35:52 +00:00
feat: use ua regex pattern rule to match target dataflow
This commit is contained in:
parent
eacdf80b0f
commit
7b1377d5e9
12
README.md
12
README.md
@ -3,7 +3,8 @@
|
|||||||
UA3F 是下一代 HTTP User-Agent 修改方法,对外作为一个 SOCK5 服务,可以部署在路由器等设备等设备进行透明 UA 修改。
|
UA3F 是下一代 HTTP User-Agent 修改方法,对外作为一个 SOCK5 服务,可以部署在路由器等设备等设备进行透明 UA 修改。
|
||||||
|
|
||||||
## 特性
|
## 特性
|
||||||
- User-Agent 自定义
|
- 支持正则表达式规则匹配修改 User-Agent
|
||||||
|
- 自定义 User-Agent 内容
|
||||||
- 与其他网络加速代理工具共存
|
- 与其他网络加速代理工具共存
|
||||||
- LRU 高速缓存非 HTTP 域名,加速非 HTTP 流量转发
|
- LRU 高速缓存非 HTTP 域名,加速非 HTTP 流量转发
|
||||||
- 支持 LuCI Web 图形页面
|
- 支持 LuCI Web 图形页面
|
||||||
@ -34,6 +35,14 @@ UA3F 已支持 LuCI Web 页面,可以打开 Services -> UA3F 进行相关配
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
> [!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:
|
安装脚本执行成功后可通过以下命令启动 UA3F:
|
||||||
@ -89,6 +98,7 @@ sudo -u shellcrash /usr/bin/ua3f
|
|||||||
|
|
||||||
- `-p <port>`: 端口号,默认 1080
|
- `-p <port>`: 端口号,默认 1080
|
||||||
- `-f <UA>`: 自定义 UA,默认 FFF
|
- `-f <UA>`: 自定义 UA,默认 FFF
|
||||||
|
- `-r <regex>`: 自定义正则匹配 User-Agent, 默认 `(iPhone|iPad|Android|Macintosh|Windows|Linux)`
|
||||||
- `-b <bind addr>`: 自定义绑定监听地址,默认 127.0.0.1
|
- `-b <bind addr>`: 自定义绑定监听地址,默认 127.0.0.1
|
||||||
- `-l <log level>`: 日志等级,默认 info,可选:debug,默认日志位置:`/var/log/ua3f.log`
|
- `-l <log level>`: 日志等级,默认 info,可选:debug,默认日志位置:`/var/log/ua3f.log`
|
||||||
|
|
||||||
|
|||||||
2
build.sh
2
build.sh
@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
project_name="ua3f"
|
project_name="ua3f"
|
||||||
release_version="0.4.0"
|
release_version="0.5.0"
|
||||||
target=cmd/ua3f.go
|
target=cmd/ua3f.go
|
||||||
dist=./dist
|
dist=./dist
|
||||||
release_dir=./bin
|
release_dir=./bin
|
||||||
|
|||||||
34
cmd/ua3f.go
34
cmd/ua3f.go
@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -17,8 +18,10 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "0.4.0"
|
var version = "0.5.0"
|
||||||
var payloadByte []byte
|
var payloadByte []byte
|
||||||
|
var uaPattern string
|
||||||
|
var uaRegexp *regexp.Regexp
|
||||||
var cache *expirable.LRU[string, string]
|
var cache *expirable.LRU[string, string]
|
||||||
var HTTP_METHOD = []string{"GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS", "TRACE", "CONNECT"}
|
var HTTP_METHOD = []string{"GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS", "TRACE", "CONNECT"}
|
||||||
var whitelist = []string{
|
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.StringVar(&addr, "b", "127.0.0.1", "bind address (default: 127.0.0.1)")
|
||||||
flag.IntVar(&port, "p", 1080, "port")
|
flag.IntVar(&port, "p", 1080, "port")
|
||||||
flag.StringVar(&payload, "f", "FFF", "User-Agent")
|
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.StringVar(&loglevel, "l", "info", "Log level (default: info)")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
@ -55,6 +59,7 @@ func main() {
|
|||||||
logrus.Info("UA3F v" + version)
|
logrus.Info("UA3F v" + version)
|
||||||
logrus.Info(fmt.Sprintf("Port: %d", port))
|
logrus.Info(fmt.Sprintf("Port: %d", port))
|
||||||
logrus.Info(fmt.Sprintf("User-Agent: %s", payload))
|
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))
|
logrus.Info(fmt.Sprintf("Log level: %s", loglevel))
|
||||||
|
|
||||||
cache = expirable.NewLRU[string, string](300, nil, time.Second*600)
|
cache = expirable.NewLRU[string, string](300, nil, time.Second*600)
|
||||||
@ -72,6 +77,11 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
logrus.Info(fmt.Sprintf("Listen on %s:%d", addr, port))
|
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 {
|
for {
|
||||||
client, err := server.Accept()
|
client, err := server.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -425,15 +435,29 @@ func CopyPileline(dst io.Writer, src io.Reader, destAddrPort string) {
|
|||||||
httpBodyOffset, err = parser.Parse(buf[:nr])
|
httpBodyOffset, err = parser.Parse(buf[:nr])
|
||||||
}
|
}
|
||||||
value, start, end := parser.FindHeader([]byte("User-Agent"))
|
value, start, end := parser.FindHeader([]byte("User-Agent"))
|
||||||
|
uaStr := string(value)
|
||||||
if value != nil && end > start {
|
if value != nil && end > start {
|
||||||
if slices.Contains(whitelist, string(value)) {
|
isInWhiteList := false
|
||||||
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()))
|
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])
|
dst.Write(buf[0:nr])
|
||||||
io.Copy(dst, src)
|
io.Copy(dst, src)
|
||||||
cache.Add(destAddrPort, destAddrPort)
|
|
||||||
return
|
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++ {
|
for i := start; i < end; i++ {
|
||||||
buf[i] = 32
|
buf[i] = 32
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ ckcmd() {
|
|||||||
cd /root
|
cd /root
|
||||||
getcpucore
|
getcpucore
|
||||||
|
|
||||||
version=0.4.0
|
version=0.5.0
|
||||||
ua3f_tar=ua3f-$version-$cpucore.tar.gz
|
ua3f_tar=ua3f-$version-$cpucore.tar.gz
|
||||||
|
|
||||||
if id -u shellclash >/dev/null 2>&1; then
|
if id -u shellclash >/dev/null 2>&1; then
|
||||||
|
|||||||
@ -3,7 +3,7 @@ local uci = require("luci.model.uci").cursor()
|
|||||||
ua3f = Map("ua3f",
|
ua3f = Map("ua3f",
|
||||||
"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>
|
<br>
|
||||||
Across the Campus we can reach every corner in the world.
|
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")
|
bind:value("0.0.0.0")
|
||||||
ua = main:taboption("general", Value, "ua", "User-Agent")
|
ua = main:taboption("general", Value, "ua", "User-Agent")
|
||||||
ua.placeholder = "FFF"
|
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 = main:taboption("general", ListValue, "log_level", "Log Level")
|
||||||
log_level:value("debug")
|
log_level:value("debug")
|
||||||
log_level:value("info")
|
log_level:value("info")
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
Package: ua3f
|
Package: ua3f
|
||||||
Version: 0.4.0-1
|
Version: 0.5.0-1
|
||||||
Depends: libc, luci-compat
|
Depends: libc, luci-compat
|
||||||
Source: /feed/openwrt
|
Source: /feed/openwrt
|
||||||
SourceName: UA3F
|
SourceName: UA3F
|
||||||
@ -7,5 +7,5 @@ License: GPL-3.0-only
|
|||||||
Section: net
|
Section: net
|
||||||
SourceDateEpoch: 1711267200
|
SourceDateEpoch: 1711267200
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Installed-Size: 2181120
|
Installed-Size: 2641920
|
||||||
Description: Implementation of the new generation of HTTP User-Agent modification methodology.
|
Description: Implementation of the new generation of HTTP User-Agent modification methodology.
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
Package: ua3f
|
Package: ua3f
|
||||||
Version: 0.4.0-1
|
Version: 0.5.0-1
|
||||||
Depends: libc, luci-compat
|
Depends: libc, luci-compat
|
||||||
Source: /feed/openwrt
|
Source: /feed/openwrt
|
||||||
SourceName: UA3F
|
SourceName: UA3F
|
||||||
@ -7,5 +7,5 @@ License: GPL-3.0-only
|
|||||||
Section: net
|
Section: net
|
||||||
SourceDateEpoch: 1711267200
|
SourceDateEpoch: 1711267200
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Installed-Size: 2181120
|
Installed-Size: 2641920
|
||||||
Description: Implementation of the new generation of HTTP User-Agent modification methodology.
|
Description: Implementation of the new generation of HTTP User-Agent modification methodology.
|
||||||
|
|||||||
@ -5,4 +5,5 @@ config 'ua3f' 'main'
|
|||||||
option port '1080'
|
option port '1080'
|
||||||
option bind '127.0.0.1'
|
option bind '127.0.0.1'
|
||||||
option ua 'FFF'
|
option ua 'FFF'
|
||||||
|
option ua_regex '(iPhone|iPad|Android|Macintosh|Windows|Linux)'
|
||||||
option log_level 'info'
|
option log_level 'info'
|
||||||
|
|||||||
@ -23,6 +23,7 @@ start_service() {
|
|||||||
config_get port "main" "port" "1080"
|
config_get port "main" "port" "1080"
|
||||||
config_get bind "main" "bind" "127.0.0.1"
|
config_get bind "main" "bind" "127.0.0.1"
|
||||||
config_get ua "main" "ua" "FFF"
|
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"
|
config_get log_level "main" "log_level" "info"
|
||||||
|
|
||||||
chmod o+w /var/log
|
chmod o+w /var/log
|
||||||
@ -32,6 +33,7 @@ start_service() {
|
|||||||
procd_append_param command -b "$bind"
|
procd_append_param command -b "$bind"
|
||||||
procd_append_param command -p $port
|
procd_append_param command -p $port
|
||||||
procd_append_param command -f "$ua"
|
procd_append_param command -f "$ua"
|
||||||
|
procd_append_param command -r "$ua_regex"
|
||||||
procd_append_param command -l $log_level
|
procd_append_param command -l $log_level
|
||||||
|
|
||||||
procd_set_param respawn
|
procd_set_param respawn
|
||||||
|
|||||||
@ -3,7 +3,7 @@ local uci = require("luci.model.uci").cursor()
|
|||||||
ua3f = Map("ua3f",
|
ua3f = Map("ua3f",
|
||||||
"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>
|
<br>
|
||||||
Across the Campus we can reach every corner in the world.
|
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")
|
bind:value("0.0.0.0")
|
||||||
ua = main:taboption("general", Value, "ua", "User-Agent")
|
ua = main:taboption("general", Value, "ua", "User-Agent")
|
||||||
ua.placeholder = "FFF"
|
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 = main:taboption("general", ListValue, "log_level", "Log Level")
|
||||||
log_level:value("debug")
|
log_level:value("debug")
|
||||||
log_level:value("info")
|
log_level:value("info")
|
||||||
|
|||||||
@ -23,6 +23,7 @@ start_service() {
|
|||||||
config_get port "main" "port" "1080"
|
config_get port "main" "port" "1080"
|
||||||
config_get bind "main" "bind" "127.0.0.1"
|
config_get bind "main" "bind" "127.0.0.1"
|
||||||
config_get ua "main" "ua" "FFF"
|
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"
|
config_get log_level "main" "log_level" "info"
|
||||||
|
|
||||||
chmod o+w /var/log
|
chmod o+w /var/log
|
||||||
@ -32,6 +33,7 @@ start_service() {
|
|||||||
procd_append_param command -b "$bind"
|
procd_append_param command -b "$bind"
|
||||||
procd_append_param command -p $port
|
procd_append_param command -p $port
|
||||||
procd_append_param command -f "$ua"
|
procd_append_param command -f "$ua"
|
||||||
|
procd_append_param command -r "$ua_regex"
|
||||||
procd_append_param command -l $log_level
|
procd_append_param command -l $log_level
|
||||||
|
|
||||||
procd_set_param respawn
|
procd_set_param respawn
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user