Files
BFR/box/scripts/box.sidecar
2026-08-13 11:56:37 +03:00

263 lines
8.4 KiB
Bash

#!/system/bin/sh
# BFR Sidecar - olcrtc / TrustTunnel local protocol adapters (SOCKS5 upstream for sing-box)
if ! /system/bin/sh -n /data/adb/box/settings.ini 2>/dev/null; then
echo "Err: settings.ini syntax error" >&2
exit 1
fi
scripts_dir="${0%/*}"
. /data/adb/box/settings.ini
sidecar_dir="${box_dir}/sidecar"
sidecar_pid="${box_run}/sidecar.pid"
sidecar_pid_olc="${box_run}/sidecar-olcrtc.pid"
sidecar_pid_tt="${box_run}/sidecar-trusttunnel.pid"
sidecar_log="${box_run}/sidecar.log"
# Defaults for dual mode
: "${sidecar_tt_port:=10800}"
: "${sidecar_olc_port:=10801}"
: "${sidecar_olc_tag:=olcrtc-proxy}"
sidecar_stop() {
for pf in "${sidecar_pid}" "${sidecar_pid_olc}" "${sidecar_pid_tt}"; do
if [ -f "${pf}" ]; then
spid=$(cat "${pf}" 2>/dev/null)
[ -n "${spid}" ] && kill -15 "${spid}" >/dev/null 2>&1
rm -f "${pf}"
fi
done
for bin in olcrtc trusttunnel_client; do
busybox pkill -15 "${bin}" >/dev/null 2>&1
done
}
sidecar_wait_port() {
port="$1"
i=0
while [ "${i}" -lt 45 ]; do
if busybox nc -z 127.0.0.1 "${port}" 2>/dev/null; then
return 0
fi
sleep 1
i=$((i + 1))
done
return 1
}
sidecar_start_one() {
# $1=protocol $2=bin $3=cfg $4=port $5=pidfile $6=extra_args...
_proto="$1"; _bin="$2"; _cfg="$3"; _port="$4"; _pf="$5"
shift 5
if [ ! -x "${_bin}" ]; then
log Error "[Sidecar] Binary not found: ${_bin}"
return 1
fi
if [ ! -f "${_cfg}" ]; then
log Error "[Sidecar] Config not found: ${_cfg}"
return 1
fi
case "${_proto}" in
olcrtc)
nohup busybox setuidgid "${box_user_group}" "${_bin}" "${_cfg}" >> "${sidecar_log}" 2>&1 &
;;
trusttunnel)
nohup busybox setuidgid "${box_user_group}" "${_bin}" -s -c "${_cfg}" >> "${sidecar_log}" 2>&1 &
;;
*)
log Error "[Sidecar] Unknown protocol: ${_proto}"
return 1
;;
esac
echo -n "$!" > "${_pf}"
# Also keep legacy sidecar.pid pointing at last/primary
echo -n "$!" > "${sidecar_pid}"
log Info "[Sidecar] Starting ${_proto} on 127.0.0.1:${_port} (pid $!)"
if sidecar_wait_port "${_port}"; then
log Info "[Sidecar] ${_proto} SOCKS ready on :${_port}"
return 0
fi
log Error "[Sidecar] Timeout waiting for ${_proto} port ${_port}"
tail -n 20 "${sidecar_log}" 2>/dev/null | while read -r line; do log Error "[Sidecar] ${line}"; done
return 1
}
sidecar_inject_socks() {
# $1=tag $2=port
tag="$1"
port="$2"
yq="${box_dir}/bin/yq"
[ -x "${yq}" ] || yq="yq"
if ! "${yq}" ".outbounds[] | select(.tag == \"${tag}\")" "${sing_config}" 2>/dev/null | grep -q .; then
"${yq}" ".outbounds += [{
\"type\": \"socks\",
\"tag\": \"${tag}\",
\"server\": \"127.0.0.1\",
\"server_port\": ${port},
\"version\": \"5\"
}]" -i --output-format=json "${sing_config}" && \
log Info "[Sidecar] Injected socks '${tag}' -> 127.0.0.1:${port}"
else
# Keep port in sync
"${yq}" "(.outbounds[] | select(.tag == \"${tag}\") | .server_port) = ${port}" -i --output-format=json "${sing_config}" 2>/dev/null
log Info "[Sidecar] Outbound '${tag}' already present (port=${port})"
fi
}
sidecar_patch_dns() {
tag="$1"
yq="${box_dir}/bin/yq"
[ -x "${yq}" ] || yq="yq"
"${yq}" '
( [.dns.servers[]? | select(.tag == "dns-direct")] | length) as $n |
if $n == 0 then .dns.servers = [{"type":"local","tag":"dns-direct"}] + (.dns.servers // []) else . end
' -i --output-format=json "${sing_config}" 2>/dev/null || true
"${yq}" '(.dns.servers[] | select(.detour == "proxy" or .tag == "dns-remote") | .detour) = "'"${tag}"'"' -i --output-format=json "${sing_config}" 2>/dev/null && \
log Info "[Sidecar] dns-remote detour -> ${tag}"
"${yq}" '
(.dns.servers[] | select(.tag == "dns-remote")) = {"type":"https","tag":"dns-remote","detour":"'"${tag}"'","server":"1.1.1.1","path":"/dns-query"} |
.dns.final = "dns-remote" |
.dns.strategy = "ipv4_only"
' -i --output-format=json "${sing_config}" 2>/dev/null && \
log Info "[Sidecar] dns-remote -> https/1.1.1.1 via ${tag}, strategy ipv4_only"
}
sidecar_inject_singbox() {
[ "${sidecar_enable}" != "true" ] && return 0
[ "${bin_name}" != "sing-box" ] && return 0
[ ! -f "${sing_config}" ] && return 0
yq="${box_dir}/bin/yq"
if [ ! -x "${yq}" ]; then
if command -v yq >/dev/null 2>&1; then
yq="yq"
else
log Error "[Sidecar] yq not found - cannot inject socks outbound"
return 1
fi
fi
case "${sidecar_protocol}" in
both)
# Fixed tags for BFR Manager profiles (ignore mis-set sidecar_outbound_tag)
sidecar_inject_socks "sidecar-proxy" "${sidecar_tt_port}"
sidecar_inject_socks "${sidecar_olc_tag}" "${sidecar_olc_port}"
# Prefer final already set in the active profile (BFR Manager Manage Profiles)
cur_final=$("${yq}" -r '.route.final // ""' "${sing_config}" 2>/dev/null)
case "${cur_final}" in
"${sidecar_olc_tag}"|sidecar-proxy|"${sidecar_outbound_tag}")
final_tag="${cur_final}"
;;
*)
final_tag="sidecar-proxy"
;;
esac
;;
olcrtc)
sidecar_inject_socks "${sidecar_outbound_tag}" "${sidecar_listen_port}"
final_tag="${sidecar_outbound_tag}"
;;
trusttunnel|*)
sidecar_inject_socks "${sidecar_outbound_tag}" "${sidecar_listen_port}"
final_tag="${sidecar_outbound_tag}"
;;
esac
if [ "${sidecar_set_final}" = "true" ]; then
"${yq}" ".route.final = \"${final_tag}\"" -i --output-format=json "${sing_config}" && \
log Info "[Sidecar] route.final -> ${final_tag}"
sidecar_patch_dns "${final_tag}"
else
log Info "[Sidecar] sidecar_set_final=false - keep profile route.final (BFR Manager switch)"
fi
}
sidecar_start_process() {
if [ "${sidecar_enable}" != "true" ]; then
return 0
fi
if [ -z "${sidecar_protocol}" ]; then
log Error "[Sidecar] sidecar_protocol is empty (olcrtc | trusttunnel | both)"
return 1
fi
sidecar_stop
mkdir -p "${sidecar_dir}" "${box_run}" /data/adb/box/run/olcrtc-data
: > "${sidecar_log}"
case "${sidecar_protocol}" in
olcrtc)
sidecar_start_one olcrtc "${box_dir}/bin/olcrtc" "${sidecar_dir}/olcrtc.yaml" \
"${sidecar_listen_port}" "${sidecar_pid_olc}" || return 1
;;
trusttunnel)
sidecar_start_one trusttunnel "${box_dir}/bin/trusttunnel_client" "${sidecar_dir}/trusttunnel.toml" \
"${sidecar_listen_port}" "${sidecar_pid_tt}" || return 1
;;
both)
# TT + olcrtc for BFR Manager profile flipping; missing one must not block the other
sidecar_start_one trusttunnel "${box_dir}/bin/trusttunnel_client" "${sidecar_dir}/trusttunnel.toml" \
"${sidecar_tt_port}" "${sidecar_pid_tt}" || \
log Warning "[Sidecar] trusttunnel failed/missing - continue without it"
sleep 2
sidecar_start_one olcrtc "${box_dir}/bin/olcrtc" "${sidecar_dir}/olcrtc.yaml" \
"${sidecar_olc_port}" "${sidecar_pid_olc}" || {
log Warning "[Sidecar] olcrtc failed to start - continuing without it"
log Warning "[Sidecar] Tip: telemost room.id = full https://telemost.yandex.ru/j/<id> URL (or numeric id)"
log Warning "[Sidecar] Tip: under root:net_admin set net.dns to LAN resolver (e.g. gateway:53)"
}
# Only hard-fail if neither SOCKS came up
if ! busybox nc -z 127.0.0.1 "${sidecar_tt_port}" 2>/dev/null && \
! busybox nc -z 127.0.0.1 "${sidecar_olc_port}" 2>/dev/null; then
log Error "[Sidecar] neither trusttunnel nor olcrtc is listening"
return 1
fi
;;
*)
log Error "[Sidecar] Unknown protocol: ${sidecar_protocol}"
return 1
;;
esac
return 0
}
case "$1" in
start)
sidecar_start_process && sidecar_inject_singbox
;;
stop)
sidecar_stop
log Info "[Sidecar] Stopped"
;;
inject)
sidecar_inject_singbox
;;
status)
echo "protocol=${sidecar_protocol}"
for pf in "${sidecar_pid_tt}" "${sidecar_pid_olc}" "${sidecar_pid}"; do
[ -f "${pf}" ] || continue
spid=$(cat "${pf}" 2>/dev/null)
if [ -n "${spid}" ] && kill -0 "${spid}" 2>/dev/null; then
log Info "[Sidecar] alive pid=${spid} file=$(basename "${pf}")"
fi
done
busybox nc -z 127.0.0.1 "${sidecar_tt_port}" 2>/dev/null && echo "port ${sidecar_tt_port} open" || true
busybox nc -z 127.0.0.1 "${sidecar_olc_port}" 2>/dev/null && echo "port ${sidecar_olc_port} open" || true
;;
*)
echo "usage: $0 {start|stop|inject|status}"
exit 1
;;
esac