|
#!/usr/bin/env bash |
|
set -euo pipefail |
|
|
|
INSTALLER_VERSION="0.2.8" |
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
|
|
|
CONFIG_FILE="" |
|
ACTION="install" |
|
MODE="existing" |
|
INTERFACE="auto" |
|
PROXY_HOST="" |
|
PROXY_LOGIN="proxy" |
|
PROXY_PASSWORD="" |
|
PROTOCOLS="http,socks5" |
|
HTTP_PORT_START="30000" |
|
SOCKS_PORT_START="40000" |
|
PROXY_COUNT="10" |
|
IPV6_CIDR="" |
|
IPV6_LIST_FILE="" |
|
IPV6_START_OFFSET="1" |
|
ASSIGNED_PREFIXLEN="64" |
|
THREEPROXY_VERSION="0.9.6" |
|
CONFIG_DIR="/etc/ywb-ipv6-proxy" |
|
STATE_DIR="/var/lib/ywb-ipv6-proxy" |
|
OUTPUT_DIR="/root/ywb-ipv6-proxy" |
|
LOG_DIR="/var/log/ywb-ipv6-proxy" |
|
MANAGE_FIREWALL="0" |
|
SELF_TEST_COUNT="2" |
|
SHOW_FILE="combined" |
|
INTERACTIVE="0" |
|
PROMPT_INPUT="" |
|
PROMPT_OUTPUT="" |
|
|
|
usage() { |
|
cat <<'EOF' |
|
Usage: |
|
sudo ./install.sh [--config example.env] [options] |
|
sudo ./install.sh --interactive |
|
|
|
One-line interactive start: |
|
tmp=$(mktemp) && curl -fsSL https://gist.githubusercontent.com/dvygolov/807b6e23c35b4387d4f547bccbf6615a/raw/install.sh -o "$tmp" && bash "$tmp" --interactive; status=$?; rm -f "$tmp"; test $status -eq 0 |
|
|
|
Action: |
|
--action install Install or re-apply the proxy setup. |
|
--action uninstall Remove the YWB-managed proxy setup. |
|
--action show Print the current proxy list without reinstalling. |
|
--interactive Start the interactive setup wizard. |
|
|
|
Required inputs for install: |
|
--login USER |
|
--password PASS |
|
|
|
Address source for install: |
|
--mode existing Use already configured global IPv6 addresses on the interface. |
|
--mode cidr Generate sequential IPv6 addresses from --ipv6-cidr. |
|
--mode list Read IPv6 addresses from --ipv6-list-file. |
|
|
|
Common options: |
|
--config FILE |
|
--interface IFACE |
|
--proxy-host HOST |
|
--protocols http,socks5 |
|
--http-port-start 30000 |
|
--socks-port-start 40000 |
|
--self-test-count 2 |
|
--manage-firewall 1 |
|
--show-file combined |
|
|
|
CIDR mode: |
|
--ipv6-cidr 2a01:4f8:abcd::/64 |
|
--proxy-count 100 |
|
--ipv6-start-offset 1 |
|
--assigned-prefixlen 64 |
|
|
|
List mode: |
|
--ipv6-list-file ./addresses.txt |
|
--assigned-prefixlen 64 |
|
|
|
Example: |
|
sudo ./install.sh \ |
|
--action install \ |
|
--mode cidr \ |
|
--interface eth0 \ |
|
--login yellow \ |
|
--password 'strong-pass' \ |
|
--ipv6-cidr '2a01:4f8:abcd::/64' \ |
|
--proxy-count 1000 \ |
|
--http-port-start 30000 \ |
|
--socks-port-start 40000 |
|
|
|
Show current proxy list: |
|
sudo ./install.sh --action show --show-file combined |
|
|
|
Interactive wizard: |
|
sudo ./install.sh --interactive |
|
tmp=$(mktemp) && curl -fsSL https://gist.githubusercontent.com/dvygolov/807b6e23c35b4387d4f547bccbf6615a/raw/install.sh -o "$tmp" && bash "$tmp" --interactive; status=$?; rm -f "$tmp"; test $status -eq 0 |
|
|
|
Uninstall: |
|
sudo ./install.sh --action uninstall |
|
EOF |
|
} |
|
|
|
log() { |
|
printf '[*] %s\n' "$*" |
|
} |
|
|
|
die() { |
|
printf '[!] %s\n' "$*" >&2 |
|
exit 1 |
|
} |
|
|
|
require_root() { |
|
[[ "${EUID}" -eq 0 ]] || die "Run this installer as root." |
|
} |
|
|
|
require_supported_os() { |
|
[[ -r /etc/os-release ]] || die "/etc/os-release is missing." |
|
# shellcheck disable=SC1091 |
|
source /etc/os-release |
|
case "${ID:-}" in |
|
debian|ubuntu) ;; |
|
*) die "Supported only on Debian/Ubuntu. Detected: ${ID:-unknown}" ;; |
|
esac |
|
} |
|
|
|
random_password() { |
|
local password="" |
|
password="$( |
|
set +o pipefail |
|
LC_ALL=C tr -dc 'A-Za-z0-9._-' </dev/urandom | head -c 20 |
|
)" |
|
printf '%s' "${password}" |
|
} |
|
|
|
normalize_yes_no() { |
|
local value="${1,,}" |
|
case "${value}" in |
|
y|yes|1|true) echo "1" ;; |
|
n|no|0|false) echo "0" ;; |
|
*) die "Expected yes/no value, got: ${1}" ;; |
|
esac |
|
} |
|
|
|
init_prompt_io() { |
|
if [[ -t 0 ]]; then |
|
PROMPT_INPUT="/dev/stdin" |
|
PROMPT_OUTPUT="/dev/stderr" |
|
return |
|
fi |
|
|
|
if [[ -r /dev/tty && -w /dev/tty ]]; then |
|
PROMPT_INPUT="/dev/tty" |
|
PROMPT_OUTPUT="/dev/tty" |
|
return |
|
fi |
|
|
|
die "Interactive mode requires a TTY." |
|
} |
|
|
|
prompt_with_default() { |
|
local __var_name="$1" |
|
local prompt_text="$2" |
|
local default_value="${3-}" |
|
local answer="" |
|
|
|
while true; do |
|
if [[ -n "${default_value}" ]]; then |
|
printf '%s [%s]: ' "${prompt_text}" "${default_value}" > "${PROMPT_OUTPUT}" |
|
else |
|
printf '%s: ' "${prompt_text}" > "${PROMPT_OUTPUT}" |
|
fi |
|
|
|
IFS= read -r answer < "${PROMPT_INPUT}" |
|
if [[ -z "${answer}" ]]; then |
|
answer="${default_value}" |
|
fi |
|
[[ -n "${answer}" ]] && break |
|
done |
|
|
|
printf -v "${__var_name}" '%s' "${answer}" |
|
} |
|
|
|
prompt_secret() { |
|
local __var_name="$1" |
|
local prompt_text="$2" |
|
local answer="" |
|
|
|
while true; do |
|
printf '%s: ' "${prompt_text}" > "${PROMPT_OUTPUT}" |
|
IFS= read -r -s answer < "${PROMPT_INPUT}" |
|
printf '\n' > "${PROMPT_OUTPUT}" |
|
[[ -n "${answer}" ]] && break |
|
done |
|
|
|
printf -v "${__var_name}" '%s' "${answer}" |
|
} |
|
|
|
prompt_yes_no() { |
|
local __var_name="$1" |
|
local prompt_text="$2" |
|
local default_value="${3:-1}" |
|
local hint="Y/n" |
|
local answer="" |
|
|
|
if [[ "${default_value}" == "0" ]]; then |
|
hint="y/N" |
|
fi |
|
|
|
while true; do |
|
printf '%s [%s]: ' "${prompt_text}" "${hint}" > "${PROMPT_OUTPUT}" |
|
IFS= read -r answer < "${PROMPT_INPUT}" |
|
answer="${answer,,}" |
|
|
|
if [[ -z "${answer}" ]]; then |
|
answer="${default_value}" |
|
fi |
|
|
|
case "${answer}" in |
|
y|yes|1|true) |
|
printf -v "${__var_name}" '%s' "1" |
|
return |
|
;; |
|
n|no|0|false) |
|
printf -v "${__var_name}" '%s' "0" |
|
return |
|
;; |
|
esac |
|
done |
|
} |
|
|
|
prompt_choice() { |
|
local __var_name="$1" |
|
local prompt_text="$2" |
|
local default_value="$3" |
|
shift 3 |
|
local options=("$@") |
|
local answer="" |
|
local matched="" |
|
local option="" |
|
|
|
while true; do |
|
printf '%s [%s]: ' "${prompt_text}" "${default_value}" > "${PROMPT_OUTPUT}" |
|
IFS= read -r answer < "${PROMPT_INPUT}" |
|
if [[ -z "${answer}" ]]; then |
|
answer="${default_value}" |
|
fi |
|
|
|
matched="" |
|
for option in "${options[@]}"; do |
|
if [[ "${answer}" == "${option}" ]]; then |
|
matched="${option}" |
|
break |
|
fi |
|
done |
|
|
|
if [[ -n "${matched}" ]]; then |
|
printf -v "${__var_name}" '%s' "${matched}" |
|
return |
|
fi |
|
done |
|
} |
|
|
|
configure_protocols_interactively() { |
|
local enable_http="1" |
|
local enable_socks5="1" |
|
|
|
if [[ ",${PROTOCOLS}," != *",http,"* ]]; then |
|
enable_http="0" |
|
fi |
|
if [[ ",${PROTOCOLS}," != *",socks5,"* ]]; then |
|
enable_socks5="0" |
|
fi |
|
|
|
while true; do |
|
prompt_yes_no enable_http "Enable HTTP proxies?" "${enable_http}" |
|
prompt_yes_no enable_socks5 "Enable SOCKS5 proxies?" "${enable_socks5}" |
|
|
|
if [[ "${enable_http}" == "1" || "${enable_socks5}" == "1" ]]; then |
|
break |
|
fi |
|
|
|
printf 'Select at least one protocol.\n' > "${PROMPT_OUTPUT}" |
|
done |
|
|
|
PROTOCOLS="" |
|
if [[ "${enable_http}" == "1" ]]; then |
|
PROTOCOLS="http" |
|
fi |
|
if [[ "${enable_socks5}" == "1" ]]; then |
|
if [[ -n "${PROTOCOLS}" ]]; then |
|
PROTOCOLS="${PROTOCOLS},socks5" |
|
else |
|
PROTOCOLS="socks5" |
|
fi |
|
fi |
|
} |
|
|
|
interactive_configure() { |
|
local password_mode="generate" |
|
local confirm_install="1" |
|
local confirm_uninstall="0" |
|
local detected_interface="" |
|
local count_default="" |
|
local count_iface="" |
|
local detected_count="" |
|
|
|
init_prompt_io |
|
|
|
printf '\nYWB IPv6 proxy installer %s\n\n' "${INSTALLER_VERSION}" > "${PROMPT_OUTPUT}" |
|
|
|
prompt_choice ACTION "Action (install/show/uninstall)" "${ACTION}" install show uninstall |
|
|
|
case "${ACTION}" in |
|
show) |
|
prompt_choice SHOW_FILE "Which list to show? (combined/http/http-urls/socks5/socks5-urls)" "${SHOW_FILE}" combined http http-urls socks5 socks5-urls |
|
return |
|
;; |
|
uninstall) |
|
prompt_yes_no confirm_uninstall "Remove the current YWB-managed setup?" "0" |
|
[[ "${confirm_uninstall}" == "1" ]] || die "Uninstall cancelled." |
|
return |
|
;; |
|
esac |
|
|
|
prompt_choice MODE "Address source mode (existing/cidr/list)" "${MODE}" existing cidr list |
|
|
|
if [[ "${INTERFACE}" == "auto" ]]; then |
|
detected_interface="$(detect_interface_value 2>/dev/null || true)" |
|
fi |
|
if [[ -n "${detected_interface}" ]]; then |
|
printf 'Auto-detected interface: %s\n' "${detected_interface}" > "${PROMPT_OUTPUT}" |
|
fi |
|
|
|
prompt_with_default INTERFACE "Network interface (Enter for auto-detect)" "${INTERFACE}" |
|
prompt_with_default PROXY_HOST "Proxy host for clients (Enter for auto-detect public IPv4)" "${PROXY_HOST:-auto}" |
|
[[ "${PROXY_HOST}" == "auto" ]] && PROXY_HOST="" |
|
prompt_with_default PROXY_LOGIN "Proxy login" "${PROXY_LOGIN}" |
|
|
|
if [[ -n "${PROXY_PASSWORD}" ]]; then |
|
password_mode="manual" |
|
fi |
|
|
|
prompt_choice password_mode "Password mode (generate/manual)" "${password_mode}" generate manual |
|
if [[ "${password_mode}" == "generate" ]]; then |
|
PROXY_PASSWORD="" |
|
else |
|
prompt_secret PROXY_PASSWORD "Proxy password" |
|
fi |
|
|
|
configure_protocols_interactively |
|
|
|
if [[ ",${PROTOCOLS}," == *",http,"* ]]; then |
|
prompt_with_default HTTP_PORT_START "HTTP start port" "${HTTP_PORT_START}" |
|
fi |
|
if [[ ",${PROTOCOLS}," == *",socks5,"* ]]; then |
|
prompt_with_default SOCKS_PORT_START "SOCKS5 start port" "${SOCKS_PORT_START}" |
|
fi |
|
|
|
case "${MODE}" in |
|
existing) |
|
count_default="${PROXY_COUNT}" |
|
count_iface="${INTERFACE}" |
|
if [[ "${count_iface}" == "auto" ]]; then |
|
count_iface="${detected_interface}" |
|
fi |
|
if [[ -n "${count_iface}" ]]; then |
|
detected_count="$(count_existing_ipv6_on_interface "${count_iface}" 2>/dev/null || true)" |
|
if [[ "${detected_count}" =~ ^[0-9]+$ ]] && [[ "${detected_count}" -gt 0 ]]; then |
|
count_default="${detected_count}" |
|
printf 'Found %s existing global IPv6 addresses on %s\n' "${detected_count}" "${count_iface}" > "${PROMPT_OUTPUT}" |
|
fi |
|
fi |
|
prompt_with_default PROXY_COUNT "How many existing IPv6 addresses to use" "${count_default}" |
|
;; |
|
cidr) |
|
prompt_with_default IPV6_CIDR "IPv6 CIDR to generate from" "${IPV6_CIDR}" |
|
prompt_with_default PROXY_COUNT "How many IPv6 addresses to generate" "${PROXY_COUNT}" |
|
prompt_with_default IPV6_START_OFFSET "Start offset inside CIDR" "${IPV6_START_OFFSET}" |
|
prompt_with_default ASSIGNED_PREFIXLEN "Prefix length for addresses added on the interface" "${ASSIGNED_PREFIXLEN}" |
|
;; |
|
list) |
|
prompt_with_default IPV6_LIST_FILE "Path to the IPv6 list file" "${IPV6_LIST_FILE}" |
|
prompt_with_default ASSIGNED_PREFIXLEN "Prefix length for addresses added on the interface" "${ASSIGNED_PREFIXLEN}" |
|
;; |
|
esac |
|
|
|
prompt_with_default SELF_TEST_COUNT "How many first addresses to self-test" "${SELF_TEST_COUNT}" |
|
prompt_yes_no MANAGE_FIREWALL "Manage UFW rules automatically?" "$(normalize_yes_no "${MANAGE_FIREWALL}")" |
|
|
|
printf '\nSummary:\n' > "${PROMPT_OUTPUT}" |
|
printf ' Action: %s\n' "${ACTION}" > "${PROMPT_OUTPUT}" |
|
printf ' Mode: %s\n' "${MODE}" > "${PROMPT_OUTPUT}" |
|
printf ' Interface: %s\n' "${INTERFACE}" > "${PROMPT_OUTPUT}" |
|
printf ' Proxy host: %s\n' "${PROXY_HOST:-auto}" > "${PROMPT_OUTPUT}" |
|
printf ' Login: %s\n' "${PROXY_LOGIN}" > "${PROMPT_OUTPUT}" |
|
printf ' Protocols: %s\n' "${PROTOCOLS}" > "${PROMPT_OUTPUT}" |
|
if [[ "${MODE}" == "cidr" ]]; then |
|
printf ' IPv6 CIDR: %s\n' "${IPV6_CIDR}" > "${PROMPT_OUTPUT}" |
|
fi |
|
if [[ "${MODE}" == "list" ]]; then |
|
printf ' IPv6 list file: %s\n' "${IPV6_LIST_FILE}" > "${PROMPT_OUTPUT}" |
|
fi |
|
printf ' Firewall via UFW: %s\n' "$( [[ "${MANAGE_FIREWALL}" == "1" ]] && echo yes || echo no )" > "${PROMPT_OUTPUT}" |
|
printf '\n' > "${PROMPT_OUTPUT}" |
|
|
|
prompt_yes_no confirm_install "Start installation with these settings?" "1" |
|
[[ "${confirm_install}" == "1" ]] || die "Installation cancelled." |
|
} |
|
|
|
parse_args() { |
|
while [[ $# -gt 0 ]]; do |
|
case "$1" in |
|
--config) |
|
CONFIG_FILE="${2:?missing value for --config}" |
|
shift 2 |
|
;; |
|
--action) |
|
ACTION="${2:?missing value for --action}" |
|
shift 2 |
|
;; |
|
--mode) |
|
MODE="${2:?missing value for --mode}" |
|
shift 2 |
|
;; |
|
--interface) |
|
INTERFACE="${2:?missing value for --interface}" |
|
shift 2 |
|
;; |
|
--proxy-host) |
|
PROXY_HOST="${2:?missing value for --proxy-host}" |
|
shift 2 |
|
;; |
|
--login) |
|
PROXY_LOGIN="${2:?missing value for --login}" |
|
shift 2 |
|
;; |
|
--password) |
|
PROXY_PASSWORD="${2:?missing value for --password}" |
|
shift 2 |
|
;; |
|
--protocols) |
|
PROTOCOLS="${2:?missing value for --protocols}" |
|
shift 2 |
|
;; |
|
--http-port-start) |
|
HTTP_PORT_START="${2:?missing value for --http-port-start}" |
|
shift 2 |
|
;; |
|
--socks-port-start) |
|
SOCKS_PORT_START="${2:?missing value for --socks-port-start}" |
|
shift 2 |
|
;; |
|
--proxy-count) |
|
PROXY_COUNT="${2:?missing value for --proxy-count}" |
|
shift 2 |
|
;; |
|
--ipv6-cidr) |
|
IPV6_CIDR="${2:?missing value for --ipv6-cidr}" |
|
shift 2 |
|
;; |
|
--ipv6-list-file) |
|
IPV6_LIST_FILE="${2:?missing value for --ipv6-list-file}" |
|
shift 2 |
|
;; |
|
--ipv6-start-offset) |
|
IPV6_START_OFFSET="${2:?missing value for --ipv6-start-offset}" |
|
shift 2 |
|
;; |
|
--assigned-prefixlen) |
|
ASSIGNED_PREFIXLEN="${2:?missing value for --assigned-prefixlen}" |
|
shift 2 |
|
;; |
|
--3proxy-version) |
|
THREEPROXY_VERSION="${2:?missing value for --3proxy-version}" |
|
shift 2 |
|
;; |
|
--config-dir) |
|
CONFIG_DIR="${2:?missing value for --config-dir}" |
|
shift 2 |
|
;; |
|
--state-dir) |
|
STATE_DIR="${2:?missing value for --state-dir}" |
|
shift 2 |
|
;; |
|
--output-dir) |
|
OUTPUT_DIR="${2:?missing value for --output-dir}" |
|
shift 2 |
|
;; |
|
--log-dir) |
|
LOG_DIR="${2:?missing value for --log-dir}" |
|
shift 2 |
|
;; |
|
--manage-firewall) |
|
MANAGE_FIREWALL="${2:?missing value for --manage-firewall}" |
|
shift 2 |
|
;; |
|
--self-test-count) |
|
SELF_TEST_COUNT="${2:?missing value for --self-test-count}" |
|
shift 2 |
|
;; |
|
--show-file) |
|
SHOW_FILE="${2:?missing value for --show-file}" |
|
shift 2 |
|
;; |
|
--interactive) |
|
INTERACTIVE="1" |
|
shift |
|
;; |
|
-h|--help) |
|
usage |
|
exit 0 |
|
;; |
|
*) |
|
die "Unknown argument: $1" |
|
;; |
|
esac |
|
done |
|
} |
|
|
|
load_config_if_needed() { |
|
if [[ -n "${CONFIG_FILE}" ]]; then |
|
[[ -f "${CONFIG_FILE}" ]] || die "Config file not found: ${CONFIG_FILE}" |
|
# shellcheck disable=SC1090 |
|
source "${CONFIG_FILE}" |
|
fi |
|
} |
|
|
|
validate_inputs() { |
|
case "${ACTION}" in |
|
install|uninstall|show) ;; |
|
*) die "--action must be one of: install, uninstall, show" ;; |
|
esac |
|
|
|
case "${SHOW_FILE}" in |
|
combined|http|http-urls|socks5|socks5-urls) ;; |
|
*) die "--show-file must be one of: combined, http, http-urls, socks5, socks5-urls" ;; |
|
esac |
|
|
|
if [[ "${ACTION}" != "install" ]]; then |
|
return |
|
fi |
|
|
|
[[ -n "${PROXY_LOGIN}" ]] || die "--login is required for install." |
|
if [[ -z "${PROXY_PASSWORD}" ]]; then |
|
PROXY_PASSWORD="$(random_password)" |
|
log "Generated proxy password because --password was not provided." |
|
fi |
|
[[ "${PROXY_LOGIN}" =~ ^[A-Za-z0-9._-]+$ ]] || die "--login contains unsupported characters. Allowed: A-Z a-z 0-9 . _ -" |
|
[[ "${PROXY_PASSWORD}" =~ ^[A-Za-z0-9._-]+$ ]] || die "--password contains unsupported characters. Allowed: A-Z a-z 0-9 . _ -" |
|
|
|
case "${MODE}" in |
|
existing|cidr|list) ;; |
|
*) die "--mode must be one of: existing, cidr, list" ;; |
|
esac |
|
|
|
[[ "${HTTP_PORT_START}" =~ ^[0-9]+$ ]] || die "--http-port-start must be numeric." |
|
[[ "${SOCKS_PORT_START}" =~ ^[0-9]+$ ]] || die "--socks-port-start must be numeric." |
|
[[ "${PROXY_COUNT}" =~ ^[0-9]+$ ]] || die "--proxy-count must be numeric." |
|
[[ "${IPV6_START_OFFSET}" =~ ^[0-9]+$ ]] || die "--ipv6-start-offset must be numeric." |
|
[[ "${ASSIGNED_PREFIXLEN}" =~ ^[0-9]+$ ]] || die "--assigned-prefixlen must be numeric." |
|
[[ "${SELF_TEST_COUNT}" =~ ^[0-9]+$ ]] || die "--self-test-count must be numeric." |
|
|
|
case "${MODE}" in |
|
cidr) |
|
[[ -n "${IPV6_CIDR}" ]] || die "--ipv6-cidr is required in cidr mode." |
|
;; |
|
list) |
|
[[ -n "${IPV6_LIST_FILE}" ]] || die "--ipv6-list-file is required in list mode." |
|
[[ -f "${IPV6_LIST_FILE}" ]] || die "IPv6 list file not found: ${IPV6_LIST_FILE}" |
|
;; |
|
esac |
|
} |
|
|
|
detect_interface() { |
|
if [[ "${INTERFACE}" != "auto" ]]; then |
|
return |
|
fi |
|
|
|
INTERFACE="$(detect_interface_value)" |
|
[[ -n "${INTERFACE}" ]] || die "Unable to auto-detect network interface." |
|
} |
|
|
|
detect_interface_value() { |
|
local detected="" |
|
detected="$(ip -6 route show default 2>/dev/null | awk '/default/ {print $5; exit}')" |
|
if [[ -z "${detected}" ]]; then |
|
detected="$(ip route show default 2>/dev/null | awk '/default/ {print $5; exit}')" |
|
fi |
|
[[ -n "${detected}" ]] || return 1 |
|
printf '%s\n' "${detected}" |
|
} |
|
|
|
detect_proxy_host_value() { |
|
local iface="$1" |
|
local detected="" |
|
|
|
detected="$(ip -4 -o addr show dev "${iface}" scope global | awk '{print $4}' | cut -d/ -f1 | head -n1)" |
|
if [[ -z "${detected}" ]]; then |
|
detected="$(hostname -f 2>/dev/null || true)" |
|
fi |
|
[[ -n "${detected}" ]] || return 1 |
|
printf '%s\n' "${detected}" |
|
} |
|
|
|
detect_proxy_host() { |
|
if [[ -n "${PROXY_HOST}" ]]; then |
|
return |
|
fi |
|
|
|
PROXY_HOST="$(detect_proxy_host_value "${INTERFACE}")" |
|
[[ -n "${PROXY_HOST}" ]] || die "Unable to auto-detect proxy host." |
|
} |
|
|
|
count_existing_ipv6_on_interface() { |
|
local iface="$1" |
|
ip -6 -o addr show dev "${iface}" scope global 2>/dev/null \ |
|
| awk '{print $4}' \ |
|
| cut -d/ -f1 \ |
|
| sort -u \ |
|
| grep -v '^$' \ |
|
| wc -l \ |
|
| tr -d '[:space:]' |
|
} |
|
|
|
install_base_packages() { |
|
export DEBIAN_FRONTEND=noninteractive |
|
log "Installing base packages..." |
|
apt-get update -y |
|
apt-get install -y --no-install-recommends ca-certificates curl iproute2 gawk python3 |
|
} |
|
|
|
disable_packaged_3proxy_service() { |
|
if systemctl list-unit-files 2>/dev/null | grep -q '^3proxy\.service'; then |
|
systemctl disable --now 3proxy.service >/dev/null 2>&1 || true |
|
fi |
|
} |
|
|
|
build_3proxy_from_source() { |
|
( |
|
set -euo pipefail |
|
local tmp_dir |
|
local src_dir |
|
|
|
tmp_dir="$(mktemp -d)" |
|
trap "rm -rf -- '${tmp_dir}'" EXIT |
|
|
|
log "Building 3proxy ${THREEPROXY_VERSION} from source..." |
|
apt-get install -y build-essential gcc make libc6-dev libpcre2-dev |
|
curl -fsSL "https://codeload.github.com/3proxy/3proxy/tar.gz/refs/tags/${THREEPROXY_VERSION}" -o "${tmp_dir}/3proxy.tar.gz" |
|
tar -xzf "${tmp_dir}/3proxy.tar.gz" -C "${tmp_dir}" |
|
src_dir="$(find "${tmp_dir}" -maxdepth 1 -type d -name '3proxy-*' | head -n1)" |
|
[[ -n "${src_dir}" ]] || die "Failed to unpack 3proxy sources." |
|
make -C "${src_dir}" -f Makefile.Linux |
|
install -m 0755 "${src_dir}/bin/3proxy" /usr/local/bin/3proxy |
|
) |
|
} |
|
|
|
install_3proxy() { |
|
if command -v 3proxy >/dev/null 2>&1; then |
|
log "3proxy already installed: $(command -v 3proxy)" |
|
disable_packaged_3proxy_service |
|
return |
|
fi |
|
|
|
local arch |
|
local asset |
|
local tmp_dir |
|
local url |
|
|
|
arch="$(dpkg --print-architecture)" |
|
case "${arch}" in |
|
amd64) asset="3proxy-${THREEPROXY_VERSION}.x86_64.deb" ;; |
|
arm64) asset="3proxy-${THREEPROXY_VERSION}.arm64.deb" ;; |
|
armhf|arm) asset="3proxy-${THREEPROXY_VERSION}.arm.deb" ;; |
|
*) |
|
asset="" |
|
;; |
|
esac |
|
|
|
tmp_dir="$(mktemp -d)" |
|
trap "rm -rf -- '${tmp_dir}'" RETURN |
|
|
|
if [[ -n "${asset}" ]]; then |
|
url="https://github.com/3proxy/3proxy/releases/download/${THREEPROXY_VERSION}/${asset}" |
|
log "Installing 3proxy ${THREEPROXY_VERSION} from official release package..." |
|
curl -fsSL "${url}" -o "${tmp_dir}/${asset}" |
|
if dpkg -i "${tmp_dir}/${asset}"; then |
|
disable_packaged_3proxy_service |
|
return |
|
fi |
|
log "Official 3proxy package could not be installed cleanly on this host. Falling back to source build..." |
|
apt-get install -f -y >/dev/null 2>&1 || true |
|
apt-get purge -y 3proxy >/dev/null 2>&1 || true |
|
else |
|
log "No official DEB asset for architecture ${arch}. Falling back to source build..." |
|
fi |
|
|
|
build_3proxy_from_source |
|
disable_packaged_3proxy_service |
|
} |
|
|
|
collect_existing_ipv6() { |
|
python3 - "${INTERFACE}" <<'PY' |
|
import ipaddress |
|
import subprocess |
|
import sys |
|
|
|
addrs = [] |
|
seen = set() |
|
|
|
iface = sys.argv[1] |
|
output = subprocess.check_output( |
|
["ip", "-6", "-o", "addr", "show", "dev", iface, "scope", "global"], |
|
text=True, |
|
) |
|
|
|
for line in output.splitlines(): |
|
parts = line.split() |
|
if len(parts) < 4: |
|
continue |
|
addr = parts[3].split("/", 1)[0].strip() |
|
if not addr or addr in seen: |
|
continue |
|
seen.add(addr) |
|
addrs.append(ipaddress.IPv6Address(addr)) |
|
|
|
for addr in sorted(addrs): |
|
print(addr) |
|
PY |
|
} |
|
|
|
generate_ipv6_from_cidr() { |
|
python3 - "${IPV6_CIDR}" "${PROXY_COUNT}" "${IPV6_START_OFFSET}" <<'PY' |
|
import ipaddress |
|
import sys |
|
|
|
network = ipaddress.IPv6Network(sys.argv[1], strict=False) |
|
count = int(sys.argv[2]) |
|
start = int(sys.argv[3]) |
|
|
|
if count <= 0: |
|
sys.exit("proxy count must be > 0") |
|
|
|
available = network.num_addresses - start |
|
if available < count: |
|
sys.exit(f"requested {count} addresses but only {available} remain in {network}") |
|
|
|
for offset in range(start, start + count): |
|
print(network.network_address + offset) |
|
PY |
|
} |
|
|
|
collect_list_ipv6() { |
|
awk ' |
|
/^[[:space:]]*#/ { next } |
|
/^[[:space:]]*$/ { next } |
|
{ |
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", $0) |
|
split($0, parts, "/") |
|
print parts[1] |
|
} |
|
' "${IPV6_LIST_FILE}" |
|
} |
|
|
|
write_addresses_file() { |
|
local source="${STATE_DIR}/ipv6-addresses.txt" |
|
mkdir -p "${STATE_DIR}" |
|
printf '%s\n' "${ADDRESSES[@]}" > "${source}" |
|
} |
|
|
|
write_metadata_file() { |
|
mkdir -p "${STATE_DIR}" |
|
cat > "${STATE_DIR}/installer.env" <<EOF |
|
ACTION=install |
|
MODE=${MODE} |
|
INTERFACE=${INTERFACE} |
|
PROXY_HOST=${PROXY_HOST} |
|
PROXY_LOGIN=${PROXY_LOGIN} |
|
PROTOCOLS=${PROTOCOLS} |
|
HTTP_PORT_START=${HTTP_PORT_START} |
|
SOCKS_PORT_START=${SOCKS_PORT_START} |
|
ASSIGNED_PREFIXLEN=${ASSIGNED_PREFIXLEN} |
|
CONFIG_DIR=${CONFIG_DIR} |
|
STATE_DIR=${STATE_DIR} |
|
OUTPUT_DIR=${OUTPUT_DIR} |
|
LOG_DIR=${LOG_DIR} |
|
EOF |
|
} |
|
|
|
load_metadata_file() { |
|
local allow_missing="${1:-0}" |
|
local metadata_file="${STATE_DIR}/installer.env" |
|
if [[ ! -f "${metadata_file}" ]]; then |
|
if [[ "${allow_missing}" == "1" ]]; then |
|
return 1 |
|
fi |
|
die "State file not found: ${metadata_file}. Run install first." |
|
fi |
|
# shellcheck disable=SC1090 |
|
source "${metadata_file}" |
|
} |
|
|
|
apply_addresses_now() { |
|
local addr |
|
if [[ "${MODE}" == "existing" ]]; then |
|
return |
|
fi |
|
|
|
for addr in "${ADDRESSES[@]}"; do |
|
if ip -6 addr show dev "${INTERFACE}" | grep -Fq "${addr}/${ASSIGNED_PREFIXLEN}"; then |
|
continue |
|
fi |
|
ip -6 addr add "${addr}/${ASSIGNED_PREFIXLEN}" dev "${INTERFACE}" |
|
done |
|
} |
|
|
|
write_address_helper() { |
|
cat > /usr/local/sbin/ywb-ipv6-addresses <<EOF |
|
#!/usr/bin/env bash |
|
set -euo pipefail |
|
|
|
INTERFACE="${INTERFACE}" |
|
PREFIXLEN="${ASSIGNED_PREFIXLEN}" |
|
ADDR_FILE="${STATE_DIR}/ipv6-addresses.txt" |
|
|
|
cmd="\${1:-apply}" |
|
|
|
[[ -f "\${ADDR_FILE}" ]] || exit 0 |
|
|
|
while IFS= read -r addr; do |
|
[[ -n "\${addr}" ]] || continue |
|
case "\${cmd}" in |
|
apply) |
|
if ! ip -6 addr show dev "\${INTERFACE}" | grep -Fq "\${addr}/\${PREFIXLEN}"; then |
|
ip -6 addr add "\${addr}/\${PREFIXLEN}" dev "\${INTERFACE}" |
|
fi |
|
;; |
|
remove) |
|
if ip -6 addr show dev "\${INTERFACE}" | grep -Fq "\${addr}/\${PREFIXLEN}"; then |
|
ip -6 addr del "\${addr}/\${PREFIXLEN}" dev "\${INTERFACE}" |
|
fi |
|
;; |
|
*) |
|
echo "Unknown command: \${cmd}" >&2 |
|
exit 1 |
|
;; |
|
esac |
|
done < "\${ADDR_FILE}" |
|
EOF |
|
chmod 0755 /usr/local/sbin/ywb-ipv6-addresses |
|
} |
|
|
|
write_address_service() { |
|
if [[ "${MODE}" == "existing" ]]; then |
|
return |
|
fi |
|
|
|
cat > /etc/systemd/system/ywb-ipv6-addresses.service <<EOF |
|
[Unit] |
|
Description=Apply IPv6 addresses for YWB proxy |
|
After=network-online.target |
|
Wants=network-online.target |
|
|
|
[Service] |
|
Type=oneshot |
|
RemainAfterExit=yes |
|
ExecStart=/usr/local/sbin/ywb-ipv6-addresses apply |
|
|
|
[Install] |
|
WantedBy=multi-user.target |
|
EOF |
|
} |
|
|
|
write_3proxy_config() { |
|
mkdir -p "${CONFIG_DIR}" "${OUTPUT_DIR}" "${LOG_DIR}" |
|
|
|
local cfg="${CONFIG_DIR}/3proxy.cfg" |
|
{ |
|
echo "pidfile ${STATE_DIR}/3proxy.pid" |
|
echo "log ${LOG_DIR}/3proxy.log D" |
|
echo "rotate 30" |
|
echo "maxconn 4096" |
|
echo "nscache 65536" |
|
echo "nserver 1.1.1.1" |
|
echo "nserver 1.0.0.1" |
|
echo "nserver 2606:4700:4700::1111" |
|
echo "nserver 2606:4700:4700::1001" |
|
echo "timeouts 1 5 30 60 180 1800 15 60" |
|
echo "auth strong" |
|
echo "users ${PROXY_LOGIN}:CL:${PROXY_PASSWORD}" |
|
echo |
|
|
|
local idx=0 |
|
local addr="" |
|
local http_port=0 |
|
local socks_port=0 |
|
for addr in "${ADDRESSES[@]}"; do |
|
http_port=$((HTTP_PORT_START + idx)) |
|
socks_port=$((SOCKS_PORT_START + idx)) |
|
|
|
if [[ ",${PROTOCOLS}," == *",http,"* ]]; then |
|
echo "allow ${PROXY_LOGIN}" |
|
echo "proxy -6 -n -a -p${http_port} -i0.0.0.0 -e${addr}" |
|
echo "flush" |
|
echo |
|
fi |
|
|
|
if [[ ",${PROTOCOLS}," == *",socks5,"* ]]; then |
|
echo "allow ${PROXY_LOGIN}" |
|
echo "socks -6 -n -a -p${socks_port} -i0.0.0.0 -e${addr}" |
|
echo "flush" |
|
echo |
|
fi |
|
|
|
idx=$((idx + 1)) |
|
done |
|
} > "${cfg}" |
|
} |
|
|
|
resolve_3proxy_binary() { |
|
if command -v 3proxy >/dev/null 2>&1; then |
|
command -v 3proxy |
|
return |
|
fi |
|
[[ -x /usr/local/bin/3proxy ]] && { echo /usr/local/bin/3proxy; return; } |
|
die "3proxy binary not found after installation." |
|
} |
|
|
|
write_3proxy_service() { |
|
local bin_path |
|
bin_path="$(resolve_3proxy_binary)" |
|
|
|
cat > /etc/systemd/system/ywb-ipv6-proxy.service <<EOF |
|
[Unit] |
|
Description=YWB IPv6 3proxy service |
|
After=network-online.target |
|
Wants=network-online.target |
|
$( [[ "${MODE}" != "existing" ]] && printf 'After=ywb-ipv6-addresses.service\nWants=ywb-ipv6-addresses.service\n' ) |
|
|
|
[Service] |
|
Type=simple |
|
ExecStart=${bin_path} ${CONFIG_DIR}/3proxy.cfg |
|
Restart=always |
|
RestartSec=3 |
|
LimitNOFILE=65536 |
|
|
|
[Install] |
|
WantedBy=multi-user.target |
|
EOF |
|
} |
|
|
|
write_proxy_lists() { |
|
mkdir -p "${OUTPUT_DIR}" |
|
: > "${OUTPUT_DIR}/proxies-http.txt" |
|
: > "${OUTPUT_DIR}/proxies-http-urls.txt" |
|
: > "${OUTPUT_DIR}/proxies-socks5.txt" |
|
: > "${OUTPUT_DIR}/proxies-socks5-urls.txt" |
|
: > "${OUTPUT_DIR}/proxies-combined.csv" |
|
|
|
local idx=0 |
|
local addr="" |
|
local http_port=0 |
|
local socks_port=0 |
|
printf 'protocol,host,port,username,password,ipv6\n' > "${OUTPUT_DIR}/proxies-combined.csv" |
|
|
|
for addr in "${ADDRESSES[@]}"; do |
|
http_port=$((HTTP_PORT_START + idx)) |
|
socks_port=$((SOCKS_PORT_START + idx)) |
|
|
|
if [[ ",${PROTOCOLS}," == *",http,"* ]]; then |
|
printf '%s:%s:%s:%s\n' "${PROXY_HOST}" "${http_port}" "${PROXY_LOGIN}" "${PROXY_PASSWORD}" >> "${OUTPUT_DIR}/proxies-http.txt" |
|
printf 'http://%s:%s@%s:%s\n' "${PROXY_LOGIN}" "${PROXY_PASSWORD}" "${PROXY_HOST}" "${http_port}" >> "${OUTPUT_DIR}/proxies-http-urls.txt" |
|
printf 'http,%s,%s,%s,%s,%s\n' "${PROXY_HOST}" "${http_port}" "${PROXY_LOGIN}" "${PROXY_PASSWORD}" "${addr}" >> "${OUTPUT_DIR}/proxies-combined.csv" |
|
fi |
|
|
|
if [[ ",${PROTOCOLS}," == *",socks5,"* ]]; then |
|
printf '%s:%s:%s:%s\n' "${PROXY_HOST}" "${socks_port}" "${PROXY_LOGIN}" "${PROXY_PASSWORD}" >> "${OUTPUT_DIR}/proxies-socks5.txt" |
|
printf 'socks5://%s:%s@%s:%s\n' "${PROXY_LOGIN}" "${PROXY_PASSWORD}" "${PROXY_HOST}" "${socks_port}" >> "${OUTPUT_DIR}/proxies-socks5-urls.txt" |
|
printf 'socks5,%s,%s,%s,%s,%s\n' "${PROXY_HOST}" "${socks_port}" "${PROXY_LOGIN}" "${PROXY_PASSWORD}" "${addr}" >> "${OUTPUT_DIR}/proxies-combined.csv" |
|
fi |
|
|
|
idx=$((idx + 1)) |
|
done |
|
} |
|
|
|
manage_firewall_if_needed() { |
|
if [[ "${MANAGE_FIREWALL}" != "1" ]]; then |
|
return |
|
fi |
|
|
|
if command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q "Status: active"; then |
|
log "Adding UFW rules for proxy ports..." |
|
local port |
|
if [[ ",${PROTOCOLS}," == *",http,"* ]]; then |
|
for ((port = HTTP_PORT_START; port < HTTP_PORT_START + ${#ADDRESSES[@]}; port++)); do |
|
ufw allow "${port}/tcp" >/dev/null |
|
done |
|
fi |
|
if [[ ",${PROTOCOLS}," == *",socks5,"* ]]; then |
|
for ((port = SOCKS_PORT_START; port < SOCKS_PORT_START + ${#ADDRESSES[@]}; port++)); do |
|
ufw allow "${port}/tcp" >/dev/null |
|
done |
|
fi |
|
else |
|
log "Firewall management requested, but no active UFW was found. Skipping firewall changes." |
|
fi |
|
} |
|
|
|
enable_and_start_services() { |
|
systemctl daemon-reload |
|
if [[ "${MODE}" != "existing" ]]; then |
|
systemctl enable ywb-ipv6-addresses.service >/dev/null 2>&1 || true |
|
systemctl restart ywb-ipv6-addresses.service |
|
fi |
|
|
|
systemctl enable ywb-ipv6-proxy.service >/dev/null 2>&1 || true |
|
if systemctl is-active --quiet ywb-ipv6-proxy.service; then |
|
systemctl restart ywb-ipv6-proxy.service |
|
else |
|
systemctl start ywb-ipv6-proxy.service |
|
fi |
|
} |
|
|
|
wait_for_local_listener() { |
|
local port="$1" |
|
local attempts="${2:-40}" |
|
local sleep_seconds="${3:-0.25}" |
|
local attempt=0 |
|
|
|
while (( attempt < attempts )); do |
|
if ss -ltn "( sport = :${port} )" 2>/dev/null | grep -Fq ":${port}"; then |
|
return 0 |
|
fi |
|
sleep "${sleep_seconds}" |
|
attempt=$((attempt + 1)) |
|
done |
|
|
|
return 1 |
|
} |
|
|
|
run_self_test() { |
|
local max="${SELF_TEST_COUNT}" |
|
local tested=0 |
|
local idx=0 |
|
local addr="" |
|
local port=0 |
|
local result="" |
|
|
|
[[ "${max}" -gt 0 ]] || return |
|
|
|
log "Running self-test against the first ${max} address(es)..." |
|
|
|
for addr in "${ADDRESSES[@]}"; do |
|
if [[ "${tested}" -ge "${max}" ]]; then |
|
break |
|
fi |
|
|
|
if [[ ",${PROTOCOLS}," == *",http,"* ]]; then |
|
port=$((HTTP_PORT_START + idx)) |
|
wait_for_local_listener "${port}" || die "HTTP proxy listener did not start on port ${port} before self-test." |
|
result="$(curl -4 -fsS --noproxy '' -x "http://${PROXY_LOGIN}:${PROXY_PASSWORD}@127.0.0.1:${port}" --max-time 20 https://ifconfig.co/ip | tr -d '\r\n')" |
|
[[ "${result}" == "${addr}" ]] || die "HTTP proxy self-test failed on port ${port}. Expected ${addr}, got ${result}." |
|
fi |
|
|
|
if [[ ",${PROTOCOLS}," == *",socks5,"* ]]; then |
|
port=$((SOCKS_PORT_START + idx)) |
|
wait_for_local_listener "${port}" || die "SOCKS5 proxy listener did not start on port ${port} before self-test." |
|
result="$(curl -4 -fsS --noproxy '' --socks5-hostname "127.0.0.1:${port}" --proxy-user "${PROXY_LOGIN}:${PROXY_PASSWORD}" --max-time 20 https://ifconfig.co/ip | tr -d '\r\n')" |
|
[[ "${result}" == "${addr}" ]] || die "SOCKS5 proxy self-test failed on port ${port}. Expected ${addr}, got ${result}." |
|
fi |
|
|
|
tested=$((tested + 1)) |
|
idx=$((idx + 1)) |
|
done |
|
} |
|
|
|
print_summary() { |
|
cat <<EOF |
|
|
|
YWB IPv6 proxy installer ${INSTALLER_VERSION} completed successfully. |
|
|
|
Interface: ${INTERFACE} |
|
Proxy host: ${PROXY_HOST} |
|
Mode: ${MODE} |
|
Address count: ${#ADDRESSES[@]} |
|
Protocols: ${PROTOCOLS} |
|
HTTP start port: ${HTTP_PORT_START} |
|
SOCKS start port: ${SOCKS_PORT_START} |
|
|
|
Config directory: ${CONFIG_DIR} |
|
State directory: ${STATE_DIR} |
|
Output directory: ${OUTPUT_DIR} |
|
Log file: ${LOG_DIR}/3proxy.log |
|
|
|
Example proxy: |
|
EOF |
|
|
|
if [[ ",${PROTOCOLS}," == *",http,"* ]]; then |
|
printf ' http://%s:%s@%s:%s\n' "${PROXY_LOGIN}" "${PROXY_PASSWORD}" "${PROXY_HOST}" "${HTTP_PORT_START}" |
|
fi |
|
if [[ ",${PROTOCOLS}," == *",socks5,"* ]]; then |
|
printf ' socks5://%s:%s@%s:%s\n' "${PROXY_LOGIN}" "${PROXY_PASSWORD}" "${PROXY_HOST}" "${SOCKS_PORT_START}" |
|
fi |
|
|
|
cat <<EOF |
|
|
|
Generated files: |
|
${OUTPUT_DIR}/proxies-http.txt |
|
${OUTPUT_DIR}/proxies-http-urls.txt |
|
${OUTPUT_DIR}/proxies-socks5.txt |
|
${OUTPUT_DIR}/proxies-socks5-urls.txt |
|
${OUTPUT_DIR}/proxies-combined.csv |
|
EOF |
|
} |
|
|
|
show_current_list() { |
|
local target_file="" |
|
|
|
load_metadata_file |
|
|
|
case "${SHOW_FILE}" in |
|
combined) target_file="${OUTPUT_DIR}/proxies-combined.csv" ;; |
|
http) target_file="${OUTPUT_DIR}/proxies-http.txt" ;; |
|
http-urls) target_file="${OUTPUT_DIR}/proxies-http-urls.txt" ;; |
|
socks5) target_file="${OUTPUT_DIR}/proxies-socks5.txt" ;; |
|
socks5-urls) target_file="${OUTPUT_DIR}/proxies-socks5-urls.txt" ;; |
|
esac |
|
|
|
[[ -f "${target_file}" ]] || die "Requested proxy list file not found: ${target_file}" |
|
|
|
cat <<EOF |
|
YWB IPv6 proxy list |
|
|
|
Mode: ${MODE} |
|
Interface: ${INTERFACE} |
|
Proxy host: ${PROXY_HOST} |
|
Protocols: ${PROTOCOLS} |
|
Source file: ${target_file} |
|
|
|
EOF |
|
|
|
cat "${target_file}" |
|
} |
|
|
|
uninstall_setup() { |
|
load_metadata_file 1 || true |
|
|
|
if [[ -f /usr/local/sbin/ywb-ipv6-addresses && "${MODE}" != "existing" ]]; then |
|
/usr/local/sbin/ywb-ipv6-addresses remove || true |
|
fi |
|
|
|
systemctl disable --now ywb-ipv6-proxy.service >/dev/null 2>&1 || true |
|
systemctl disable --now ywb-ipv6-addresses.service >/dev/null 2>&1 || true |
|
systemctl daemon-reload |
|
|
|
rm -f /etc/systemd/system/ywb-ipv6-proxy.service |
|
rm -f /etc/systemd/system/ywb-ipv6-addresses.service |
|
rm -f /usr/local/sbin/ywb-ipv6-addresses |
|
rm -rf "${CONFIG_DIR}" |
|
rm -rf "${STATE_DIR}" |
|
rm -rf "${OUTPUT_DIR}" |
|
rm -rf "${LOG_DIR}" |
|
|
|
systemctl daemon-reload |
|
log "YWB-managed proxy setup was removed. Installed 3proxy package was left in place." |
|
} |
|
|
|
install_setup() { |
|
detect_interface |
|
detect_proxy_host |
|
install_base_packages |
|
install_3proxy |
|
|
|
mapfile -t ADDRESSES < <( |
|
case "${MODE}" in |
|
existing) collect_existing_ipv6 ;; |
|
cidr) generate_ipv6_from_cidr ;; |
|
list) collect_list_ipv6 ;; |
|
esac |
|
) |
|
|
|
[[ "${#ADDRESSES[@]}" -gt 0 ]] || die "No IPv6 addresses were collected." |
|
|
|
if [[ "${MODE}" == "existing" ]] && [[ "${PROXY_COUNT}" -gt 0 ]] && [[ "${PROXY_COUNT}" -lt "${#ADDRESSES[@]}" ]]; then |
|
ADDRESSES=("${ADDRESSES[@]:0:${PROXY_COUNT}}") |
|
fi |
|
|
|
write_addresses_file |
|
write_metadata_file |
|
write_address_helper |
|
apply_addresses_now |
|
write_address_service |
|
write_3proxy_config |
|
write_3proxy_service |
|
write_proxy_lists |
|
manage_firewall_if_needed |
|
enable_and_start_services |
|
run_self_test |
|
print_summary |
|
} |
|
|
|
main() { |
|
local original_argc="$#" |
|
|
|
parse_args "$@" |
|
|
|
if [[ "${original_argc}" -eq 0 && -t 0 ]]; then |
|
INTERACTIVE="1" |
|
fi |
|
|
|
load_config_if_needed |
|
if [[ "${INTERACTIVE}" == "1" ]]; then |
|
interactive_configure |
|
fi |
|
validate_inputs |
|
require_root |
|
require_supported_os |
|
|
|
case "${ACTION}" in |
|
install) install_setup ;; |
|
uninstall) uninstall_setup ;; |
|
show) show_current_list ;; |
|
esac |
|
} |
|
|
|
main "$@" |