#!/bin/sh WARP_CONF="${WARP_CONF:-/etc/sing-box-warp/warp.conf}" OUTPUT_CONFIG="${OUTPUT_CONFIG:-/opt/sing-box-warp/config.json}" ENABLE_TUN_FILE="${ENABLE_TUN_FILE:-/etc/sing-box-warp/enable-tun}" load_enable_tun() { local value value=$(trim "${ENABLE_TUN:-}") if [ -z "$value" ] && [ -f "$ENABLE_TUN_FILE" ]; then value=$(trim "$(cat "$ENABLE_TUN_FILE")") fi case "$value" in 1|yes|true|y|Y|on|ON) ENABLE_TUN=1 ;; *) ENABLE_TUN=0 ;; esac } urldecode() { echo "$1" | sed 's/%3[dD]/=/g; s/%2[bB]/+/g; s/%2[fF]/\//g; s/%2[cC]/,/g' } trim() { echo "$1" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' } # Default-route NIC (eth0, ens3, enp0s3, …); override with TUN_EXCLUDE_INTERFACE detect_default_interface() { local iface iface=$(trim "${TUN_EXCLUDE_INTERFACE:-}") if [ -n "$iface" ]; then echo "$iface" return 0 fi if command -v ip >/dev/null 2>&1; then iface=$(ip -4 route show default 2>/dev/null \ | awk '/default/ { for (i = 1; i <= NF; i++) if ($i == "dev") { print $(i + 1); exit } }') if [ -n "$iface" ]; then echo "$iface" return 0 fi iface=$(ip -4 route get 1.1.1.1 2>/dev/null \ | awk '{ for (i = 1; i <= NF; i++) if ($i == "dev") { print $(i + 1); exit } }') if [ -n "$iface" ]; then echo "$iface" return 0 fi fi if [ -r /proc/net/route ]; then iface=$(awk '$2 == "00000000" && $1 != "Iface" { print $1; exit }' /proc/net/route) if [ -n "$iface" ]; then echo "$iface" return 0 fi fi return 1 } normalize_cidr() { local addr local suffix addr=$(trim "$1") suffix="$2" if [ -z "$addr" ]; then echo "" return fi case "$addr" in */*) echo "$addr" ;; *) echo "${addr}/${suffix}" ;; esac } # Extract parameter from URL get_param() { local url="$1" local param="$2" echo "$url" | sed -n "s/.*[?&]${param}=\([^&#]*\).*/\1/p" } parse_from_wg_url() { WG_URL=$(grep "^wg://" "$WARP_CONF" | head -1) if [ -z "$WG_URL" ]; then echo "Error: No wg:// URL found in $WARP_CONF" exit 1 fi SERVER=$(echo "$WG_URL" | sed 's|wg://\([^:]*\):.*|\1|') PORT=$(echo "$WG_URL" | sed 's|wg://[^:]*:\([0-9]*\)?.*|\1|') PRIVATE_KEY=$(urldecode "$(get_param "$WG_URL" "private_key")") PUBLIC_KEY=$(urldecode "$(get_param "$WG_URL" "peer_public_key")") MTU=$(get_param "$WG_URL" "mtu") LOCAL_ADDRESS=$(urldecode "$(get_param "$WG_URL" "local_address")") Jc=$(get_param "$WG_URL" "junk_packet_count") Jmin=$(get_param "$WG_URL" "junk_packet_min_size") Jmax=$(get_param "$WG_URL" "junk_packet_max_size") H1=$(get_param "$WG_URL" "init_packet_magic_header") H2=$(get_param "$WG_URL" "response_packet_magic_header") H3=$(get_param "$WG_URL" "underload_packet_magic_header") H4=$(get_param "$WG_URL" "transport_packet_magic_header") IPV4=$(echo "$LOCAL_ADDRESS" | tr ',-' '\n' | sed -n '1p') IPV6=$(echo "$LOCAL_ADDRESS" | tr ',-' '\n' | sed -n '2p') IPV4=$(normalize_cidr "$IPV4" 32) IPV6=$(normalize_cidr "$IPV6" 128) if [ -z "$IPV4" ]; then echo "Error: local_address (IPv4) is empty" exit 1 fi ADDRESS_JSON=$(printf '"%s"' "$IPV4") MTU=${MTU:-1280} Jc=${Jc:-4} Jmin=${Jmin:-40} Jmax=${Jmax:-70} H1=${H1:-1} H2=${H2:-2} H3=${H3:-3} H4=${H4:-4} ALLOWED_IPS="0.0.0.0/0" TAG="wireguard-out" LOG_LEVEL="error" } parse_from_ini() { local section="" local key="" local value="" while IFS= read -r line || [ -n "$line" ]; do line=$(trim "$line") [ -z "$line" ] && continue case "$line" in \#*|\;*) continue ;; esac case "$line" in "[Interface]") section="Interface"; continue ;; "[Peer]") section="Peer"; continue ;; esac key=$(trim "$(echo "$line" | cut -d'=' -f1)") value=$(trim "$(echo "$line" | cut -d'=' -f2-)") [ -z "$key" ] && continue if [ "$section" = "Interface" ]; then case "$key" in PrivateKey) PRIVATE_KEY="$value" ;; Address) IPV4=$(trim "$(echo "$value" | cut -d',' -f1)") IPV6=$(trim "$(echo "$value" | cut -d',' -f2)") ;; MTU) MTU="$value" ;; S1) S1="$value" ;; S2) S2="$value" ;; S3) S3="$value" ;; Jc) Jc="$value" ;; Jmin) Jmin="$value" ;; Jmax) Jmax="$value" ;; H1) H1="$value" ;; H2) H2="$value" ;; H3) H3="$value" ;; H4) H4="$value" ;; I1) I1="$value" ;; I2) I2="$value" ;; esac elif [ "$section" = "Peer" ]; then case "$key" in PublicKey) PUBLIC_KEY="$value" ;; AllowedIPs) if echo "$value" | grep -q "0.0.0.0/0"; then ALLOWED_IPS="0.0.0.0/0" else ALLOWED_IPS=$(trim "$(echo "$value" | cut -d',' -f1)") fi ;; Endpoint) SERVER=$(echo "$value" | cut -d':' -f1) PORT=$(echo "$value" | cut -d':' -f2) ;; esac fi done < "$WARP_CONF" IPV4=$(normalize_cidr "$IPV4" 32) IPV6=$(normalize_cidr "$IPV6" 128) if [ -z "$IPV4" ]; then echo "Error: Address (IPv4) is empty" exit 1 fi ADDRESS_JSON=$(printf '"%s"' "$IPV4") MTU=${MTU:-1280} S1=${S1:-0} S2=${S2:-0} S3=${S3:-0} Jc=${Jc:-4} Jmin=${Jmin:-40} Jmax=${Jmax:-70} H1=${H1:-1} H2=${H2:-2} H3=${H3:-3} H4=${H4:-4} ALLOWED_IPS=${ALLOWED_IPS:-0.0.0.0/0} if [ -z "$SERVER" ] || [ -z "$PORT" ]; then echo "Error: Endpoint is empty" exit 1 fi TAG="wireguard-out" LOG_LEVEL="error" } write_config() { if [ "$ENABLE_TUN" = "1" ]; then EXCLUDE_IFACE=$(detect_default_interface) || { echo "Warning: could not detect default network interface, using eth0" >&2 EXCLUDE_IFACE="eth0" } echo "TUN enabled, exclude_interface: $EXCLUDE_IFACE" >&2 TUN_INBOUND_PART=$(cat <&2 TUN_INBOUND_PART="" TUN_ROUTE_RULE_PART="" RULE_SET_RULE_PART="" ROUTE_RULE_SET_SECTION="" fi if [ -n "$I1" ] || [ -n "$I2" ]; then H4_COMMA="," else H4_COMMA="" fi if [ -n "$I1" ] && [ -n "$I2" ]; then I1_LINE=$(printf ' "i1": "%s",\n' "$I1") I2_LINE=$(printf ' "i2": "%s"\n' "$I2") elif [ -n "$I1" ]; then I1_LINE=$(printf ' "i1": "%s"\n' "$I1") I2_LINE="" elif [ -n "$I2" ]; then I1_LINE="" I2_LINE=$(printf ' "i2": "%s"\n' "$I2") else I1_LINE="" I2_LINE="" fi cat > "$OUTPUT_CONFIG" <