mirror of
https://github.com/SunBK201/UA3F.git
synced 2025-12-16 16:57:08 +00:00
feat: add support for multiple server modes (SOCKS5, TPROXY, REDIRECT)
This commit is contained in:
parent
47d579b339
commit
485c5d5602
@ -1,11 +1,11 @@
|
|||||||
Package: ua3f
|
Package: ua3f
|
||||||
Version: 0.9.0-1
|
Version: 0.9.0-1
|
||||||
Depends: luci-compat
|
Depends: luci-compat, ipset, iptables, kmod-nft-tproxy, iptables-mod-tproxy, iptables-mod-extra, iptables-mod-nat-extra, kmod-ipt-conntrack
|
||||||
Source: /feed/openwrt
|
Source: /feed/openwrt
|
||||||
SourceName: UA3F
|
SourceName: UA3F
|
||||||
License: GPL-3.0-only
|
License: GPL-3.0-only
|
||||||
Section: net
|
Section: net
|
||||||
SourceDateEpoch: 1711267200
|
SourceDateEpoch: 1711267200
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Installed-Size: 3563520
|
Installed-Size: 3573760
|
||||||
Description: Advanced HTTP User-Agent Rewriting Tool.
|
Description: Advanced HTTP User-Agent Rewriting Tool.
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
Package: ua3f
|
Package: ua3f
|
||||||
Version: 0.9.0-1
|
Version: 0.9.0-1
|
||||||
Depends: luci-compat
|
Depends: luci-compat, ipset, iptables, kmod-nft-tproxy, iptables-mod-tproxy, iptables-mod-extra, iptables-mod-nat-extra, kmod-ipt-conntrack
|
||||||
Source: /feed/openwrt
|
Source: /feed/openwrt
|
||||||
SourceName: UA3F
|
SourceName: UA3F
|
||||||
License: GPL-3.0-only
|
License: GPL-3.0-only
|
||||||
Section: net
|
Section: net
|
||||||
SourceDateEpoch: 1711267200
|
SourceDateEpoch: 1711267200
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Installed-Size: 3696640
|
Installed-Size: 3706880
|
||||||
Description: Advanced HTTP User-Agent Rewriting Tool.
|
Description: Advanced HTTP User-Agent Rewriting Tool.
|
||||||
|
|||||||
@ -28,7 +28,7 @@ define Package/ua3f
|
|||||||
SUBMENU:=Web Servers/Proxies
|
SUBMENU:=Web Servers/Proxies
|
||||||
TITLE:=A SOCKS5 Server for User-Agent Rewriting
|
TITLE:=A SOCKS5 Server for User-Agent Rewriting
|
||||||
URL:=https://github.com/SunBK201/UA3F
|
URL:=https://github.com/SunBK201/UA3F
|
||||||
DEPENDS:=$(GO_ARCH_DEPENDS) +luci-compat
|
DEPENDS:=$(GO_ARCH_DEPENDS) +luci-compat +ipset +iptables +kmod-nft-tproxy +iptables-mod-tproxy +iptables-mod-extra +iptables-mod-nat-extra +kmod-ipt-conntrack
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define Package/ua3f/description
|
define Package/ua3f/description
|
||||||
|
|||||||
@ -20,10 +20,10 @@ running.cfgvalue = function(self, section)
|
|||||||
local pid = luci.sys.exec("pidof ua3f")
|
local pid = luci.sys.exec("pidof ua3f")
|
||||||
if pid == "" then
|
if pid == "" then
|
||||||
return "<input disabled type='button' style='opacity: 1;' class='btn cbi-button cbi-button-reset' value='" ..
|
return "<input disabled type='button' style='opacity: 1;' class='btn cbi-button cbi-button-reset' value='" ..
|
||||||
translate("Stop") .. "'/>"
|
translate("Stop") .. "'/>"
|
||||||
else
|
else
|
||||||
return "<input disabled type='button' style='opacity: 1;' class='btn cbi-button cbi-button-add' value='" ..
|
return "<input disabled type='button' style='opacity: 1;' class='btn cbi-button cbi-button-add' value='" ..
|
||||||
translate("Running") .. "'/>"
|
translate("Running") .. "'/>"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -31,6 +31,11 @@ general:tab("general", translate("Settings"))
|
|||||||
general:tab("stats", translate("Statistics"))
|
general:tab("stats", translate("Statistics"))
|
||||||
general:tab("log", translate("Log"))
|
general:tab("log", translate("Log"))
|
||||||
|
|
||||||
|
server_mode = general:taboption("general", ListValue, "server_mode", translate("Server Mode"))
|
||||||
|
server_mode:value("SOCKS5", "SOCKS5")
|
||||||
|
server_mode:value("TPROXY", "TPROXY")
|
||||||
|
server_mode:value("REDIRECT", "REDIRECT")
|
||||||
|
|
||||||
port = general:taboption("general", Value, "port", translate("Port"))
|
port = general:taboption("general", Value, "port", translate("Port"))
|
||||||
port.placeholder = "1080"
|
port.placeholder = "1080"
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,198 @@ START=99
|
|||||||
NAME="ua3f"
|
NAME="ua3f"
|
||||||
PROG="/usr/bin/$NAME"
|
PROG="/usr/bin/$NAME"
|
||||||
|
|
||||||
|
SERVER_MODE=""
|
||||||
|
SERVER_PORT="1080"
|
||||||
|
|
||||||
|
FW_BACKEND=""
|
||||||
|
NFT_TABLE="UA3F"
|
||||||
|
UA3F_CHAIN="UA3F"
|
||||||
|
UA3F_OUT_CHAIN="UA3F_OUTPUT"
|
||||||
|
IPSET_NAME="localnetwork"
|
||||||
|
UA3FMARK="0xc9"
|
||||||
|
FWMARK="0x1c9"
|
||||||
|
ROUTE_TABLE="0x1c9"
|
||||||
|
|
||||||
|
RUNDIR="/var/run/${NAME}"
|
||||||
|
[ -d "$RUNDIR" ] || mkdir -p "$RUNDIR"
|
||||||
|
ROUTE_CREATED_FLAG="$RUNDIR/route_created"
|
||||||
|
IPSET_CREATED_FLAG="$RUNDIR/ipset_created"
|
||||||
|
|
||||||
|
log() { logger -t "$NAME" -- "$*"; }
|
||||||
|
|
||||||
|
try_modprobe() { command -v modprobe >/dev/null 2>&1 && modprobe "$1" 2>/dev/null; }
|
||||||
|
|
||||||
|
nft_available() { command -v nft >/dev/null 2>&1; }
|
||||||
|
ipt_available() { command -v iptables >/dev/null 2>&1; }
|
||||||
|
detect_backend() {
|
||||||
|
if nft_available; then
|
||||||
|
FW_BACKEND="ipt"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if ipt_available; then
|
||||||
|
FW_BACKEND="ipt"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
FW_BACKEND=""
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_tproxy_route() {
|
||||||
|
ip rule add fwmark "$FWMARK" table "$ROUTE_TABLE" 2>/dev/null
|
||||||
|
ip route add local 0.0.0.0/0 dev lo table "$ROUTE_TABLE" 2>/dev/null
|
||||||
|
echo 1 >"$ROUTE_CREATED_FLAG"
|
||||||
|
}
|
||||||
|
cleanup_tproxy_route() {
|
||||||
|
ip route del local 0.0.0.0/0 dev lo table "$ROUTE_TABLE" 2>/dev/null
|
||||||
|
ip rule del fwmark "$FWMARK" table "$ROUTE_TABLE" 2>/dev/null
|
||||||
|
rm -f "$ROUTE_CREATED_FLAG"
|
||||||
|
}
|
||||||
|
|
||||||
|
nft_drop_table() { nft delete table ip "$NFT_TABLE" 2>/dev/null; }
|
||||||
|
|
||||||
|
nft_reinit_table() {
|
||||||
|
nft_drop_table
|
||||||
|
nft add table ip "$NFT_TABLE" || return 1
|
||||||
|
|
||||||
|
# set: localnetwork
|
||||||
|
nft "add set ip $NFT_TABLE $IPSET_NAME { type ipv4_addr; flags interval; auto-merge; }" || return 1
|
||||||
|
nft "add element ip $NFT_TABLE $IPSET_NAME { 0.0.0.0/8, 127.0.0.0/8, 10.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.168.0.0/16, 224.0.0.0/4, 240.0.0.0/4, 100.64.0.0/10 }" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
fw_setup_nft_tproxy_tcp() {
|
||||||
|
nft_reinit_table || return 1
|
||||||
|
ensure_tproxy_route
|
||||||
|
|
||||||
|
# PREROUTING -> UA3F
|
||||||
|
nft add chain ip $NFT_TABLE prerouting '{ type filter hook prerouting priority mangle; }'
|
||||||
|
nft add rule ip $NFT_TABLE prerouting mark $UA3FMARK return
|
||||||
|
nft add rule ip $NFT_TABLE prerouting ip daddr @$IPSET_NAME return
|
||||||
|
nft add rule ip $NFT_TABLE prerouting ct direction reply return
|
||||||
|
nft add rule ip $NFT_TABLE prerouting meta l4proto tcp mark set $FWMARK tproxy to 127.0.0.1:$SERVER_PORT counter accept
|
||||||
|
|
||||||
|
# OUTPUT -> UA3F_OUTPUT
|
||||||
|
nft add chain ip $NFT_TABLE output '{ type route hook output priority mangle; }'
|
||||||
|
nft add rule ip $NFT_TABLE output mark $UA3FMARK return
|
||||||
|
nft add rule ip $NFT_TABLE output meta skgid 65534 return
|
||||||
|
nft add rule ip $NFT_TABLE output ip daddr @$IPSET_NAME return
|
||||||
|
nft add rule ip $NFT_TABLE output meta l4proto tcp mark set $FWMARK counter accept
|
||||||
|
}
|
||||||
|
|
||||||
|
fw_setup_nft_redirect_tcp() {
|
||||||
|
nft_reinit_table || return 1
|
||||||
|
|
||||||
|
# PREROUTING -> UA3F
|
||||||
|
nft add chain ip $NFT_TABLE prerouting '{ type nat hook prerouting priority mangle; }'
|
||||||
|
nft add rule ip $NFT_TABLE prerouting mark $UA3FMARK return
|
||||||
|
nft add rule ip $NFT_TABLE prerouting ip daddr @$IPSET_NAME return
|
||||||
|
nft add rule ip $NFT_TABLE prerouting ct direction reply return
|
||||||
|
nft add rule ip $NFT_TABLE prerouting tcp dport != {22} redirect to :$SERVER_PORT
|
||||||
|
|
||||||
|
# OUTPUT -> UA3F_OUTPUT
|
||||||
|
nft add chain ip $NFT_TABLE output '{ type nat hook output priority mangle; }'
|
||||||
|
nft add rule ip $NFT_TABLE output mark $UA3FMARK return
|
||||||
|
nft add rule ip $NFT_TABLE output meta skgid 65534 return
|
||||||
|
nft add rule ip $NFT_TABLE output ip daddr @$IPSET_NAME return
|
||||||
|
nft add rule ip $NFT_TABLE output tcp dport != {22} redirect to :$SERVER_PORT
|
||||||
|
}
|
||||||
|
|
||||||
|
fw_revert_nft() {
|
||||||
|
nft_drop_table
|
||||||
|
[ -f "$ROUTE_CREATED_FLAG" ] && cleanup_tproxy_route
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_local_set_ipt() {
|
||||||
|
if ! ipset list "$IPSET_NAME" >/dev/null 2>&1; then
|
||||||
|
ipset create "$IPSET_NAME" hash:net maxelem 1048576 || return 1
|
||||||
|
echo 1 >"$IPSET_CREATED_FLAG"
|
||||||
|
ipset add "$IPSET_NAME" 0.0.0.0/8
|
||||||
|
ipset add "$IPSET_NAME" 127.0.0.0/8
|
||||||
|
ipset add "$IPSET_NAME" 10.0.0.0/8
|
||||||
|
ipset add "$IPSET_NAME" 169.254.0.0/16
|
||||||
|
ipset add "$IPSET_NAME" 172.16.0.0/12
|
||||||
|
ipset add "$IPSET_NAME" 192.168.0.0/16
|
||||||
|
ipset add "$IPSET_NAME" 224.0.0.0/4
|
||||||
|
ipset add "$IPSET_NAME" 240.0.0.0/4
|
||||||
|
ipset add "$IPSET_NAME" 100.64.0.0/10
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
fw_setup_ipt_tproxy_tcp() {
|
||||||
|
ensure_local_set_ipt || return 1
|
||||||
|
ensure_tproxy_route
|
||||||
|
|
||||||
|
# PREROUTING
|
||||||
|
iptables -t mangle -F "$UA3F_CHAIN" 2>/dev/null
|
||||||
|
iptables -t mangle -D PREROUTING -p tcp -j "$UA3F_CHAIN" 2>/dev/null
|
||||||
|
iptables -t mangle -X "$UA3F_CHAIN" 2>/dev/null
|
||||||
|
iptables -t mangle -N "$UA3F_CHAIN"
|
||||||
|
iptables -t mangle -A PREROUTING -p tcp -j "$UA3F_CHAIN"
|
||||||
|
iptables -t mangle -A "$UA3F_CHAIN" -p tcp -m mark --mark "$UA3FMARK" -j RETURN
|
||||||
|
iptables -t mangle -A "$UA3F_CHAIN" -m set --match-set "$IPSET_NAME" dst -j RETURN
|
||||||
|
iptables -t mangle -A "$UA3F_CHAIN" -m conntrack --ctdir REPLY -j RETURN
|
||||||
|
iptables -t mangle -A "$UA3F_CHAIN" -p tcp -j TPROXY --on-port "$SERVER_PORT" --tproxy-mark "$FWMARK"
|
||||||
|
|
||||||
|
# OUTPUT
|
||||||
|
iptables -t mangle -F "$UA3F_OUT_CHAIN" 2>/dev/null
|
||||||
|
iptables -t mangle -D OUTPUT -p tcp -j "$UA3F_OUT_CHAIN" 2>/dev/null
|
||||||
|
iptables -t mangle -X "$UA3F_OUT_CHAIN" 2>/dev/null
|
||||||
|
iptables -t mangle -N "$UA3F_OUT_CHAIN"
|
||||||
|
iptables -t mangle -A OUTPUT -p tcp -j "$UA3F_OUT_CHAIN"
|
||||||
|
iptables -t mangle -A "$UA3F_OUT_CHAIN" -p tcp -m mark --mark "$UA3FMARK" -j RETURN
|
||||||
|
iptables -t mangle -A "$UA3F_OUT_CHAIN" -p tcp -m owner --gid-owner 65534 -j RETURN
|
||||||
|
iptables -t mangle -A "$UA3F_OUT_CHAIN" -m set --match-set "$IPSET_NAME" dst -j RETURN
|
||||||
|
iptables -t mangle -A "$UA3F_OUT_CHAIN" -p tcp -j MARK --set-mark "$FWMARK"
|
||||||
|
}
|
||||||
|
|
||||||
|
fw_setup_ipt_redirect_tcp() {
|
||||||
|
ensure_local_set_ipt || return 1
|
||||||
|
|
||||||
|
# PREROUTING
|
||||||
|
iptables -t nat -F "$UA3F_CHAIN" 2>/dev/null
|
||||||
|
iptables -t nat -D PREROUTING -p tcp -j "$UA3F_CHAIN" 2>/dev/null
|
||||||
|
iptables -t nat -X "$UA3F_CHAIN" 2>/dev/null
|
||||||
|
iptables -t nat -N "$UA3F_CHAIN"
|
||||||
|
iptables -t nat -A PREROUTING -p tcp -j "$UA3F_CHAIN"
|
||||||
|
iptables -t nat -A "$UA3F_CHAIN" -p tcp -m mark --mark "$UA3FMARK" -j RETURN
|
||||||
|
iptables -t nat -A "$UA3F_CHAIN" -m set --match-set "$IPSET_NAME" dst -j RETURN
|
||||||
|
iptables -t nat -A "$UA3F_CHAIN" -m conntrack --ctdir REPLY -j RETURN
|
||||||
|
iptables -t nat -A "$UA3F_CHAIN" -p tcp -j REDIRECT --to-ports "$SERVER_PORT"
|
||||||
|
|
||||||
|
# OUTPUT
|
||||||
|
iptables -t nat -F "$UA3F_OUT_CHAIN" 2>/dev/null
|
||||||
|
iptables -t nat -D OUTPUT -p tcp -j "$UA3F_OUT_CHAIN" 2>/dev/null
|
||||||
|
iptables -t nat -X "$UA3F_OUT_CHAIN" 2>/dev/null
|
||||||
|
iptables -t nat -N "$UA3F_OUT_CHAIN"
|
||||||
|
iptables -t nat -A OUTPUT -p tcp -j "$UA3F_OUT_CHAIN"
|
||||||
|
iptables -t nat -A "$UA3F_OUT_CHAIN" -p tcp -m mark --mark "$UA3FMARK" -j RETURN
|
||||||
|
iptables -t nat -A "$UA3F_OUT_CHAIN" -p tcp -m owner --gid-owner 65534 -j RETURN
|
||||||
|
iptables -t nat -A "$UA3F_OUT_CHAIN" -m set --match-set "$IPSET_NAME" dst -j RETURN
|
||||||
|
iptables -t nat -A "$UA3F_OUT_CHAIN" -p tcp -j REDIRECT --to-ports "$SERVER_PORT"
|
||||||
|
}
|
||||||
|
|
||||||
|
fw_revert_ipt() {
|
||||||
|
# mangle
|
||||||
|
iptables -t mangle -D PREROUTING -p tcp -j "$UA3F_CHAIN" 2>/dev/null
|
||||||
|
iptables -t mangle -F "$UA3F_CHAIN" 2>/dev/null
|
||||||
|
iptables -t mangle -X "$UA3F_CHAIN" 2>/dev/null
|
||||||
|
iptables -t mangle -D OUTPUT -p tcp -j "$UA3F_OUT_CHAIN" 2>/dev/null
|
||||||
|
iptables -t mangle -F "$UA3F_OUT_CHAIN" 2>/dev/null
|
||||||
|
iptables -t mangle -X "$UA3F_OUT_CHAIN" 2>/dev/null
|
||||||
|
# nat
|
||||||
|
iptables -t nat -D PREROUTING -p tcp -j "$UA3F_CHAIN" 2>/dev/null
|
||||||
|
iptables -t nat -F "$UA3F_CHAIN" 2>/dev/null
|
||||||
|
iptables -t nat -X "$UA3F_CHAIN" 2>/dev/null
|
||||||
|
iptables -t nat -D OUTPUT -p tcp -j "$UA3F_OUT_CHAIN" 2>/dev/null
|
||||||
|
iptables -t nat -F "$UA3F_OUT_CHAIN" 2>/dev/null
|
||||||
|
iptables -t nat -X "$UA3F_OUT_CHAIN" 2>/dev/null
|
||||||
|
# ipset
|
||||||
|
if [ -f "$IPSET_CREATED_FLAG" ]; then
|
||||||
|
ipset destroy "$IPSET_NAME" 2>/dev/null
|
||||||
|
rm -f "$IPSET_CREATED_FLAG"
|
||||||
|
fi
|
||||||
|
[ -f "$ROUTE_CREATED_FLAG" ] && cleanup_tproxy_route
|
||||||
|
}
|
||||||
|
|
||||||
start_service() {
|
start_service() {
|
||||||
config_load "$NAME"
|
config_load "$NAME"
|
||||||
|
|
||||||
@ -16,12 +208,8 @@ start_service() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local port
|
local server_mode port bind ua log_level ua_regex partial_replace
|
||||||
local bind
|
config_get server_mode "main" "server_mode" "SOCKS5"
|
||||||
local ua
|
|
||||||
local log_level
|
|
||||||
local ua_regex
|
|
||||||
local partial_replace
|
|
||||||
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"
|
||||||
@ -29,13 +217,67 @@ start_service() {
|
|||||||
config_get_bool partial_replace "main" "partial_replace" 0
|
config_get_bool partial_replace "main" "partial_replace" 0
|
||||||
config_get log_level "main" "log_level" "info"
|
config_get log_level "main" "log_level" "info"
|
||||||
|
|
||||||
|
SERVER_MODE="$(echo "$server_mode" | tr '[:lower:]' '[:upper:]')"
|
||||||
|
SERVER_MODE="$server_mode"
|
||||||
|
|
||||||
mkdir -p /var/log/ua3f
|
mkdir -p /var/log/ua3f
|
||||||
chmod o+w /var/log/ua3f
|
chmod o+w /var/log/ua3f
|
||||||
|
|
||||||
|
detect_backend || {
|
||||||
|
log "no firewall backend found"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Always cleanup first (idempotent)
|
||||||
|
if [ "$FW_BACKEND" = "nft" ]; then
|
||||||
|
fw_revert_nft
|
||||||
|
else
|
||||||
|
fw_revert_ipt
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$SERVER_MODE" in
|
||||||
|
SOCKS5)
|
||||||
|
# No firewall interception
|
||||||
|
;;
|
||||||
|
TPROXY)
|
||||||
|
if [ "$FW_BACKEND" = "nft" ]; then
|
||||||
|
try_modprobe nft_tproxy
|
||||||
|
fw_setup_nft_tproxy_tcp || {
|
||||||
|
log "nft TPROXY setup failed"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
try_modprobe xt_TPROXY
|
||||||
|
fw_setup_ipt_tproxy_tcp || {
|
||||||
|
log "iptables TPROXY setup failed"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
REDIRECT)
|
||||||
|
if [ "$FW_BACKEND" = "nft" ]; then
|
||||||
|
fw_setup_nft_redirect_tcp || {
|
||||||
|
log "nft REDIRECT setup failed"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fw_setup_ipt_redirect_tcp || {
|
||||||
|
log "iptables REDIRECT setup failed"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log "unknown server_mode: $SERVER_MODE"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
procd_open_instance "$NAME"
|
procd_open_instance "$NAME"
|
||||||
procd_set_param command "$PROG"
|
procd_set_param command "$PROG"
|
||||||
procd_append_param command -b "$bind"
|
procd_append_param command -m "$server_mode"
|
||||||
procd_append_param command -p $port
|
procd_append_param command -p $port
|
||||||
|
procd_append_param command -b "$bind"
|
||||||
procd_append_param command -f "$ua"
|
procd_append_param command -f "$ua"
|
||||||
procd_append_param command -r "$ua_regex"
|
procd_append_param command -r "$ua_regex"
|
||||||
procd_append_param command -l $log_level
|
procd_append_param command -l $log_level
|
||||||
@ -56,6 +298,15 @@ start_service() {
|
|||||||
procd_close_instance
|
procd_close_instance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stop_service() {
|
||||||
|
detect_backend
|
||||||
|
if [ "$FW_BACKEND" = "nft" ]; then
|
||||||
|
fw_revert_nft
|
||||||
|
else
|
||||||
|
fw_revert_ipt
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
reload_service() {
|
reload_service() {
|
||||||
stop
|
stop
|
||||||
start
|
start
|
||||||
|
|||||||
@ -2,6 +2,7 @@ config 'ua3f' 'enabled'
|
|||||||
option enabled '0'
|
option enabled '0'
|
||||||
|
|
||||||
config 'ua3f' 'main'
|
config 'ua3f' 'main'
|
||||||
|
option server_mode 'SOCKS5'
|
||||||
option port '1080'
|
option port '1080'
|
||||||
option bind '127.0.0.1'
|
option bind '127.0.0.1'
|
||||||
option ua 'FFF'
|
option ua 'FFF'
|
||||||
|
|||||||
@ -26,6 +26,9 @@ msgstr "统计信息"
|
|||||||
msgid "Log"
|
msgid "Log"
|
||||||
msgstr "运行日志"
|
msgstr "运行日志"
|
||||||
|
|
||||||
|
msgid "Server Mode"
|
||||||
|
msgstr "服务模式"
|
||||||
|
|
||||||
msgid "Port"
|
msgid "Port"
|
||||||
msgstr "端口"
|
msgstr "端口"
|
||||||
|
|
||||||
|
|||||||
@ -6,10 +6,8 @@ require (
|
|||||||
github.com/dlclark/regexp2 v1.11.4
|
github.com/dlclark/regexp2 v1.11.4
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
|
golang.org/x/sys v0.26.0
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require github.com/stretchr/testify v1.8.2 // indirect
|
||||||
github.com/stretchr/testify v1.8.2 // indirect
|
|
||||||
golang.org/x/sys v0.26.0 // indirect
|
|
||||||
)
|
|
||||||
|
|||||||
@ -3,11 +3,13 @@ package config
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ServerModeSocks5 = "socks5"
|
ServerModeSocks5 = "SOCKS5"
|
||||||
ServerModeTProxy = "tproxy"
|
ServerModeTProxy = "TPROXY"
|
||||||
|
ServerModeRedirect = "REDIRECT"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@ -33,7 +35,7 @@ func Parse() (*Config, bool) {
|
|||||||
showVer bool
|
showVer bool
|
||||||
)
|
)
|
||||||
|
|
||||||
flag.StringVar(&serverMode, "m", ServerModeSocks5, "server mode: socks5 or tproxy (default: socks5)")
|
flag.StringVar(&serverMode, "m", ServerModeSocks5, "server mode: SOCKS5, TPROXY, REDIRECT (default: SOCKS5)")
|
||||||
flag.StringVar(&bindAddr, "b", "127.0.0.1", "bind address (default: 127.0.0.1)")
|
flag.StringVar(&bindAddr, "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(&payloadUA, "f", "FFF", "User-Agent")
|
flag.StringVar(&payloadUA, "f", "FFF", "User-Agent")
|
||||||
@ -44,7 +46,7 @@ func Parse() (*Config, bool) {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
cfg := &Config{
|
cfg := &Config{
|
||||||
ServerMode: serverMode,
|
ServerMode: strings.ToUpper(serverMode),
|
||||||
BindAddr: bindAddr,
|
BindAddr: bindAddr,
|
||||||
Port: port,
|
Port: port,
|
||||||
ListenAddr: fmt.Sprintf("%s:%d", bindAddr, port),
|
ListenAddr: fmt.Sprintf("%s:%d", bindAddr, port),
|
||||||
|
|||||||
@ -65,6 +65,7 @@ func SetLogConf(level string) {
|
|||||||
|
|
||||||
func LogHeader(version string, cfg *config.Config) {
|
func LogHeader(version string, cfg *config.Config) {
|
||||||
logrus.Info("UA3F v" + version)
|
logrus.Info("UA3F v" + version)
|
||||||
|
logrus.Info("Server Mode: " + cfg.ServerMode)
|
||||||
logrus.Infof("Listen on %s", cfg.ListenAddr)
|
logrus.Infof("Listen on %s", cfg.ListenAddr)
|
||||||
logrus.Infof("User-Agent: %s", cfg.PayloadUA)
|
logrus.Infof("User-Agent: %s", cfg.PayloadUA)
|
||||||
logrus.Infof("User-Agent Regex Pattern: '%s'", cfg.UAPattern)
|
logrus.Infof("User-Agent Regex Pattern: '%s'", cfg.UAPattern)
|
||||||
|
|||||||
92
src/internal/server/redirect/redirect_linux.go
Normal file
92
src/internal/server/redirect/redirect_linux.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
//go:build linux
|
||||||
|
|
||||||
|
package redirect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/sunbk201/ua3f/internal/config"
|
||||||
|
"github.com/sunbk201/ua3f/internal/rewrite"
|
||||||
|
"github.com/sunbk201/ua3f/internal/server/utils"
|
||||||
|
"github.com/sunbk201/ua3f/internal/statistics"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
cfg *config.Config
|
||||||
|
rw *rewrite.Rewriter
|
||||||
|
listener net.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(cfg *config.Config, rw *rewrite.Rewriter) *Server {
|
||||||
|
return &Server{
|
||||||
|
cfg: cfg,
|
||||||
|
rw: rw,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Start() (err error) {
|
||||||
|
if s.listener, err = net.Listen("tcp", s.cfg.ListenAddr); err != nil {
|
||||||
|
return fmt.Errorf("listen failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go statistics.StartRecorder()
|
||||||
|
|
||||||
|
var client net.Conn
|
||||||
|
for {
|
||||||
|
if client, err = s.listener.Accept(); err != nil {
|
||||||
|
logrus.Error("Accept failed: ", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
logrus.Debugf("Accept connection from %s", client.RemoteAddr().String())
|
||||||
|
go s.HandleClient(client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) HandleClient(client net.Conn) {
|
||||||
|
addr, err := getOriginalDstAddr(client)
|
||||||
|
if err != nil {
|
||||||
|
_ = client.Close()
|
||||||
|
logrus.Errorf("Get original dst addr failed: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logrus.Debugf("Original destination address: %s", addr)
|
||||||
|
|
||||||
|
target, err := utils.ConnectWithMark(addr, utils.SO_MARK)
|
||||||
|
if err != nil {
|
||||||
|
_ = client.Close()
|
||||||
|
logrus.Errorf("Dial target %s failed: %v", addr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.ForwardTCP(client, target, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForwardTCP proxies traffic in both directions.
|
||||||
|
// target->client uses raw copy.
|
||||||
|
// client->target is processed by the rewriter (or raw if cached).
|
||||||
|
func (s *Server) ForwardTCP(client, target net.Conn, destAddr string) {
|
||||||
|
// Server -> Client (raw)
|
||||||
|
go utils.CopyHalf(client, target)
|
||||||
|
|
||||||
|
// Client -> Server (rewriter)
|
||||||
|
go utils.ProxyHalf(target, client, s.rw, destAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOriginalDstAddr retrieves the original destination address of the redirected connection.
|
||||||
|
func getOriginalDstAddr(conn net.Conn) (addr string, err error) {
|
||||||
|
fd, err := utils.GetConnFD(conn)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get file descriptor: %v", err)
|
||||||
|
}
|
||||||
|
raw, err := unix.GetsockoptIPv6Mreq(fd, unix.SOL_IP, unix.SO_ORIGINAL_DST)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("getsockopt SO_ORIGINAL_DST failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := net.IPv4(raw.Multiaddr[4], raw.Multiaddr[5], raw.Multiaddr[6], raw.Multiaddr[7])
|
||||||
|
port := uint16(raw.Multiaddr[2])<<8 + uint16(raw.Multiaddr[3])
|
||||||
|
return fmt.Sprintf("%s:%d", ip.String(), port), nil
|
||||||
|
}
|
||||||
37
src/internal/server/redirect/redirect_others.go
Normal file
37
src/internal/server/redirect/redirect_others.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//go:build !linux
|
||||||
|
|
||||||
|
package redirect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/sunbk201/ua3f/internal/config"
|
||||||
|
"github.com/sunbk201/ua3f/internal/rewrite"
|
||||||
|
"github.com/sunbk201/ua3f/internal/server/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
cfg *config.Config
|
||||||
|
rw *rewrite.Rewriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(cfg *config.Config, rw *rewrite.Rewriter) *Server {
|
||||||
|
return &Server{
|
||||||
|
cfg: cfg,
|
||||||
|
rw: rw,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Start() error {
|
||||||
|
return fmt.Errorf("REDIRECT Mode is only supported on Linux")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) HandleClient(client net.Conn) {
|
||||||
|
defer client.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ForwardTCP(client, target net.Conn, _ string) {
|
||||||
|
go utils.CopyHalf(client, target)
|
||||||
|
go utils.CopyHalf(target, client)
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/sunbk201/ua3f/internal/config"
|
"github.com/sunbk201/ua3f/internal/config"
|
||||||
"github.com/sunbk201/ua3f/internal/rewrite"
|
"github.com/sunbk201/ua3f/internal/rewrite"
|
||||||
|
"github.com/sunbk201/ua3f/internal/server/redirect"
|
||||||
"github.com/sunbk201/ua3f/internal/server/socks5"
|
"github.com/sunbk201/ua3f/internal/server/socks5"
|
||||||
"github.com/sunbk201/ua3f/internal/server/tproxy"
|
"github.com/sunbk201/ua3f/internal/server/tproxy"
|
||||||
)
|
)
|
||||||
@ -20,6 +21,10 @@ func NewServer(cfg *config.Config, rw *rewrite.Rewriter) (Server, error) {
|
|||||||
switch cfg.ServerMode {
|
switch cfg.ServerMode {
|
||||||
case config.ServerModeSocks5:
|
case config.ServerModeSocks5:
|
||||||
return socks5.New(cfg, rw), nil
|
return socks5.New(cfg, rw), nil
|
||||||
|
case config.ServerModeTProxy:
|
||||||
|
return tproxy.New(cfg, rw), nil
|
||||||
|
case config.ServerModeRedirect:
|
||||||
|
return redirect.New(cfg, rw), nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown server mode: %s", cfg.ServerMode)
|
return nil, fmt.Errorf("unknown server mode: %s", cfg.ServerMode)
|
||||||
}
|
}
|
||||||
|
|||||||
113
src/internal/server/tproxy/tproxy_linux.go
Normal file
113
src/internal/server/tproxy/tproxy_linux.go
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
//go:build linux
|
||||||
|
|
||||||
|
package tproxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/sunbk201/ua3f/internal/config"
|
||||||
|
"github.com/sunbk201/ua3f/internal/rewrite"
|
||||||
|
"github.com/sunbk201/ua3f/internal/server/utils"
|
||||||
|
"github.com/sunbk201/ua3f/internal/statistics"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
cfg *config.Config
|
||||||
|
rw *rewrite.Rewriter
|
||||||
|
listener net.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(cfg *config.Config, rw *rewrite.Rewriter) *Server {
|
||||||
|
return &Server{
|
||||||
|
cfg: cfg,
|
||||||
|
rw: rw,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Start() error {
|
||||||
|
lc := net.ListenConfig{
|
||||||
|
Control: func(network, address string, c syscall.RawConn) error {
|
||||||
|
var err error
|
||||||
|
c.Control(func(fd uintptr) {
|
||||||
|
if e := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1); e != nil {
|
||||||
|
err = fmt.Errorf("set SO_REUSEADDR: %v", e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e := unix.SetsockoptInt(int(fd), unix.SOL_IP, unix.IP_TRANSPARENT, 1); e != nil {
|
||||||
|
err = fmt.Errorf("set IP_TRANSPARENT: %v", e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if s.listener, err = lc.Listen(context.TODO(), "tcp", s.cfg.ListenAddr); err != nil {
|
||||||
|
return fmt.Errorf("listen failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go statistics.StartRecorder()
|
||||||
|
|
||||||
|
var client net.Conn
|
||||||
|
for {
|
||||||
|
if client, err = s.listener.Accept(); err != nil {
|
||||||
|
logrus.Error("Accept failed: ", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
logrus.Debugf("Accept connection from %s", client.RemoteAddr().String())
|
||||||
|
go s.HandleClient(client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) HandleClient(client net.Conn) {
|
||||||
|
addr, err := getOriginalDstAddr(client)
|
||||||
|
if err != nil {
|
||||||
|
_ = client.Close()
|
||||||
|
logrus.Errorf("Get original dst addr failed: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logrus.Debugf("Original destination address: %s", addr)
|
||||||
|
|
||||||
|
target, err := utils.ConnectWithMark(addr, utils.SO_MARK)
|
||||||
|
if err != nil {
|
||||||
|
_ = client.Close()
|
||||||
|
logrus.Errorf("Dial target %s failed: %v", addr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.ForwardTCP(client, target, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForwardTCP proxies traffic in both directions.
|
||||||
|
// target->client uses raw copy.
|
||||||
|
// client->target is processed by the rewriter (or raw if cached).
|
||||||
|
func (s *Server) ForwardTCP(client, target net.Conn, destAddr string) {
|
||||||
|
// Server -> Client (raw)
|
||||||
|
go utils.CopyHalf(client, target)
|
||||||
|
|
||||||
|
// Client -> Server (rewriter)
|
||||||
|
go utils.ProxyHalf(target, client, s.rw, destAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOriginalDstAddr retrieves the original destination address of a TProxy connection.
|
||||||
|
func getOriginalDstAddr(conn net.Conn) (addr string, err error) {
|
||||||
|
fd, err := utils.GetConnFD(conn)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get file descriptor: %v", err)
|
||||||
|
}
|
||||||
|
raw, err := unix.GetsockoptIPv6Mreq(fd, unix.SOL_IP, unix.SO_ORIGINAL_DST)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("getsockopt SO_ORIGINAL_DST failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := net.IPv4(raw.Multiaddr[4], raw.Multiaddr[5], raw.Multiaddr[6], raw.Multiaddr[7])
|
||||||
|
port := uint16(raw.Multiaddr[2])<<8 + uint16(raw.Multiaddr[3])
|
||||||
|
return fmt.Sprintf("%s:%d", ip.String(), port), nil
|
||||||
|
}
|
||||||
37
src/internal/server/tproxy/tproxy_others.go
Normal file
37
src/internal/server/tproxy/tproxy_others.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//go:build !linux
|
||||||
|
|
||||||
|
package tproxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/sunbk201/ua3f/internal/config"
|
||||||
|
"github.com/sunbk201/ua3f/internal/rewrite"
|
||||||
|
"github.com/sunbk201/ua3f/internal/server/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
cfg *config.Config
|
||||||
|
rw *rewrite.Rewriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(cfg *config.Config, rw *rewrite.Rewriter) *Server {
|
||||||
|
return &Server{
|
||||||
|
cfg: cfg,
|
||||||
|
rw: rw,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Start() error {
|
||||||
|
return fmt.Errorf("TPROXY Mode is only supported on Linux")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) HandleClient(client net.Conn) {
|
||||||
|
defer client.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ForwardTCP(client, target net.Conn, _ string) {
|
||||||
|
go utils.CopyHalf(client, target)
|
||||||
|
go utils.CopyHalf(target, client)
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
@ -9,12 +10,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Connect dials the target address and returns the connection.
|
// Connect dials the target address and returns the connection.
|
||||||
func Connect(Addr string) (target net.Conn, err error) {
|
func Connect(addr string) (target net.Conn, err error) {
|
||||||
logrus.Debugf("Connecting %s", Addr)
|
logrus.Debugf("Connecting %s", addr)
|
||||||
if target, err = net.Dial("tcp", Addr); err != nil {
|
if target, err = net.Dial("tcp", addr); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
logrus.Debugf("Connected %s", Addr)
|
logrus.Debugf("Connected %s", addr)
|
||||||
return target, nil
|
return target, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,3 +53,16 @@ func ProxyHalf(dst, src net.Conn, rw *rewrite.Rewriter, destAddrPort string) {
|
|||||||
}()
|
}()
|
||||||
_ = rw.ProxyHTTPOrRaw(dst, src, destAddrPort)
|
_ = rw.ProxyHTTPOrRaw(dst, src, destAddrPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetConnFD(conn net.Conn) (fd int, err error) {
|
||||||
|
tcpConn, ok := conn.(*net.TCPConn)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("not a TCP connection")
|
||||||
|
}
|
||||||
|
file, err := tcpConn.File()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(file.Fd()), nil
|
||||||
|
}
|
||||||
|
|||||||
34
src/internal/server/utils/tcp_linux.go
Normal file
34
src/internal/server/utils/tcp_linux.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
//go:build linux
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const SO_MARK = 0xc9
|
||||||
|
|
||||||
|
// ConnectWithMark dials the target address with SO_MARK set and returns the connection.
|
||||||
|
func ConnectWithMark(addr string, mark int) (target net.Conn, err error) {
|
||||||
|
logrus.Debugf("Connecting %s with SO_MARK=%d", addr, mark)
|
||||||
|
|
||||||
|
dialer := net.Dialer{
|
||||||
|
Control: func(network, address string, c syscall.RawConn) error {
|
||||||
|
return c.Control(func(fd uintptr) {
|
||||||
|
unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_MARK, mark)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := dialer.Dial("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("Connected %s with SO_MARK=%d", addr, mark)
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user