#!/usr/bin/env bash

REMCHECK_VERSION="1.2"
POOL_SRC=""
MODE="connect"
LANES=64
WAIT_S=10
PROTO="http"
ECHO_URL="https://ipv4.icanhazip.com"
DEST=""
QUIET=0
FORCE_CONNECT=0
PANEL_SUBS="https://panel.remproxy.com/subscriptions"
DNSBL_ZONES="bl.spamcop.net b.barracudacentral.org psbl.surriel.com dnsbl-1.uceprotect.net dnsbl-2.uceprotect.net dnsbl.dronebl.org bl.blocklist.de bl.mailspike.net truncate.gbudb.net dnsbl.justspam.org bl.nordspam.com bl.0spam.org rbl.interserver.net all.s5h.net dnsbl.spfbl.net bl.suomispam.net dnsrbl.swinog.ch"
WIDE_ZONES="dnsbl-2.uceprotect.net"
SOFT_CODES="dnsbl.spfbl.net=127.0.0.3 dnsbl.spfbl.net=127.0.0.4 dnsbl.spfbl.net=127.0.0.5"
DOH_A="https://dns.google/resolve"
DOH_B="https://cloudflare-dns.com/dns-query"
RBL_LANES=128
RBL_LANES_MAX=256
LANES_SET=0
COLON_FORM='^[^:@/]+:[0-9]+:.+'
IPV4_FORM='^([0-9]{1,3}\.){3}[0-9]{1,3}$'

usage() {
  cat <<'TXT'
remcheck - multithreaded proxy checker for Linux, macOS and Windows (Git Bash, WSL)

Usage: remcheck.sh -f LIST [options]

  -f LIST   proxy list: file, http(s) URL or - for stdin
  -m MODE   connect (default) or rbl
  -n NUM    proxies checked at once, default 64; in rbl mode also the
            number of exit IPs looked up at once, default 128, up to 256
  -T SEC    timeout per proxy in seconds, default 10
  -P PROTO  http (default), socks5 or socks4 for lines without a scheme
  -e URL    page opened through each proxy, must answer with the caller IP,
            default https://ipv4.icanhazip.com
  -d DIR    folder for results, default ./remcheck-DATE-TIME
  -C        rbl mode with a RemProxy CSV: connect through every proxy anyway
            instead of taking exit IPs from the real_ip column
  -q        no progress line
  -V        print version
  -h        this help

List lines, one proxy per line:
  host:port
  host:port:user:pass
  user:pass@host:port
  socks5://user:pass@host:port
  ip;port;country;speed;uptime;real_ip    (RemProxy CSV)

Modes:
  connect   opens the page through every proxy, keeps the ones that answer
  rbl       looks up the exit IP of every proxy in 17 DNSBL zones over
            DNS-over-HTTPS (Google and Cloudflare); exit IPs come from the
            real_ip column of a RemProxy CSV, for other lists from a
            connect pass first

Files in DIR:
  alive.txt     working proxies, fastest first (connect pass)
  dead.txt      proxies that did not answer (connect pass)
  results.tsv   one row per proxy with exit IP, time or CSV data, RBL columns;
                no_answer lists zones that neither resolver answered, the IP
                counts as clean for them
  clean.txt     rbl mode: proxies whose exit IP is in no list
  listed.txt    rbl mode: proxies whose exit IP is listed

Exit code: 0 success, 1 no working or clean proxy, 2 usage error,
3 the list server rejected this machine (IP not in the whitelist).
TXT
}

die() {
  printf 'remcheck: %s\n' "$1" >&2
  exit 2
}

say_lang() {
  case "${LC_ALL:-${LC_MESSAGES:-${LANG:-}}}" in
    ru*) printf 'ru' ;;
    uk*) printf 'uk' ;;
    *) printf 'en' ;;
  esac
}

is_num() {
  case "$1" in '' | *[!0-9]*) return 1 ;; esac
  [ "$1" -gt 0 ]
}

lower() {
  printf '%s' "$1" | tr '[:upper:]' '[:lower:]'
}

pct() {
  awk -v a="$1" -v b="$2" 'BEGIN { if (b > 0) printf "%.1f", a * 100 / b; else printf "0.0" }'
}

need_tools() {
  local miss="" t os=""
  for t in curl awk sed grep sort cut tr wc mktemp mkfifo date head; do
    command -v "$t" >/dev/null 2>&1 || miss="$miss $t"
  done
  [ -z "$miss" ] && return 0
  printf 'remcheck: missing tools:%s\n' "$miss" >&2
  [ -r /etc/os-release ] && os=$(. /etc/os-release; printf '%s %s' "${ID:-}" "${ID_LIKE:-}")
  case "$os" in
    *alpine*) printf 'Install them with: apk add bash curl coreutils gawk grep sed\n' >&2 ;;
    *debian* | *ubuntu*) printf 'Install them with: sudo apt-get update && sudo apt-get install -y curl gawk coreutils grep sed\n' >&2 ;;
    *rhel* | *fedora* | *centos* | *rocky* | *alma*) printf 'Install them with: sudo dnf install -y curl gawk coreutils grep sed\n' >&2 ;;
    *arch*) printf 'Install them with: sudo pacman -S --needed curl gawk coreutils grep sed\n' >&2 ;;
    *) printf 'Install them with the package manager of your system.\n' >&2 ;;
  esac
  exit 2
}

whitelist_guard() {
  local text seen allowed
  text=$(head -c 2000 "$1" | tr -d '\r')
  case "$text" in
    *"not authorized"* | *"Not authorized"*) ;;
    *) return 0 ;;
  esac
  seen=$(printf '%s\n' "$text" | awk '{
    i = index(tolower($0), "not authorized"); if (!i) next
    s = substr($0, i + 14)
    if (match(s, /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/) || match(s, /[0-9a-fA-F]*:[0-9a-fA-F:]+/)) { print substr(s, RSTART, RLENGTH); exit }
  }')
  allowed=$(printf '%s\n' "$text" | awk '{
    i = index(tolower($0), "allowed only from:"); if (!i) next
    s = substr($0, i + 18); sub(/^[ \t]+/, "", s); print s; exit
  }')
  [ -n "$seen" ] || seen="?"
  [ -n "$allowed" ] || allowed="-"
  case "$(say_lang)" in
    ru)
      printf '\nremcheck: сервер списка отклонил эту машину, её IP нет в белом списке подписки.\n' >&2
      printf '  IP этой машины:         %s\n  Разрешённые IP подписки: %s\n\n' "$seen" "$allowed" >&2
      printf 'Откройте %s, выберите эту подписку,\nв разделе «Доступ» добавьте %s в «Разрешённые IP» и повторите проверку.\n\n' "$PANEL_SUBS" "$seen" >&2
      ;;
    uk)
      printf '\nremcheck: сервер списку відхилив цю машину, її IP немає в білому списку підписки.\n' >&2
      printf '  IP цієї машини:          %s\n  Дозволені IP підписки:   %s\n\n' "$seen" "$allowed" >&2
      printf 'Відкрийте %s, виберіть цю підписку,\nу розділі «Доступ» додайте %s до «Дозволені IP» і повторіть перевірку.\n\n' "$PANEL_SUBS" "$seen" >&2
      ;;
    *)
      printf '\nremcheck: the list server rejected this machine, its IP is not in the subscription whitelist.\n' >&2
      printf '  IP of this machine:        %s\n  Allowed IPs of the plan:   %s\n\n' "$seen" "$allowed" >&2
      printf 'Open %s, choose this subscription,\nadd %s to Allowed IPs in the Access section and run the check again.\n\n' "$PANEL_SUBS" "$seen" >&2
      ;;
  esac
  exit 3
}

while getopts ":f:m:n:T:P:e:d:CqVh" opt; do
  case "$opt" in
    f) POOL_SRC="$OPTARG" ;;
    m) MODE=$(lower "$OPTARG") ;;
    n) LANES="$OPTARG"; LANES_SET=1 ;;
    T) WAIT_S="$OPTARG" ;;
    P) PROTO=$(lower "$OPTARG") ;;
    e) ECHO_URL="$OPTARG" ;;
    d) DEST="$OPTARG" ;;
    C) FORCE_CONNECT=1 ;;
    q) QUIET=1 ;;
    V) printf 'remcheck %s\n' "$REMCHECK_VERSION"; exit 0 ;;
    h) usage; exit 0 ;;
    :) die "option -$OPTARG needs a value" ;;
    *) die "unknown option -$OPTARG, see -h" ;;
  esac
done

[ -n "$POOL_SRC" ] || { usage >&2; exit 2; }
need_tools
case "$MODE" in connect | rbl) ;; *) die "mode must be connect or rbl" ;; esac
case "$PROTO" in http | https | socks5 | socks4) ;; *) die "proto must be http, https, socks5 or socks4" ;; esac
is_num "$LANES" || die "-n needs a positive number"
is_num "$WAIT_S" || die "-T needs a positive number"
[ "$LANES" -gt 1000 ] && LANES=1000
[ -t 2 ] || QUIET=1

WORK=$(mktemp -d "${TMPDIR:-/tmp}/remcheck.XXXXXX") || die "cannot create a temp folder"
TICKER=""
finish() {
  [ -n "$TICKER" ] && kill "$TICKER" 2>/dev/null
  rm -rf "$WORK"
}
trap finish EXIT
trap 'exit 130' INT TERM

case "$POOL_SRC" in
  http://* | https://*)
    HTTP_CODE=$(curl -sSL --max-time 90 -o "$WORK/src" -w '%{http_code}' "$POOL_SRC") || die "cannot download $POOL_SRC"
    whitelist_guard "$WORK/src"
    case "$HTTP_CODE" in 2??) ;; *) die "the list link answered with HTTP $HTTP_CODE" ;; esac
    ;;
  -) cat > "$WORK/src" ;;
  *) [ -r "$POOL_SRC" ] || die "cannot read $POOL_SRC"; cat "$POOL_SRC" > "$WORK/src" ;;
esac
whitelist_guard "$WORK/src"

: > "$WORK/csvmap"
tr -d '\r' < "$WORK/src" | awk -F';' -v map="$WORK/csvmap" '
  { sub(/^[ \t]+/, ""); sub(/[ \t]+$/, "") }
  $0 == "" || substr($0, 1, 1) == "#" { next }
  NF >= 2 {
    if ($2 !~ /^[0-9]+$/) next
    key = $1 ":" $2
    if (seen[key]++) next
    print key
    if (NF >= 6 && $6 ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/) print key "\t" $6 "\t" $3 "\t" $4 "\t" $5 > map
    next
  }
  { if (!seen[$0]++) print }
' > "$WORK/pool"

TOTAL=$(wc -l < "$WORK/pool" | tr -d ' ')
[ "$TOTAL" -gt 0 ] || die "no proxies found in $POOL_SRC"
MAPPED=$(wc -l < "$WORK/csvmap" | tr -d ' ')
CSV_FAST=0
[ "$MODE" = "rbl" ] && [ "$FORCE_CONNECT" = 0 ] && [ "$MAPPED" = "$TOTAL" ] && CSV_FAST=1
[ -n "$DEST" ] || DEST="remcheck-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$DEST" || die "cannot create $DEST"
: > "$WORK/raw"
: > "$WORK/rblraw"

probe_one() {
  local spec="$1" scheme="$PROTO" rest="$1" cred="" hostport a b c d out tail code secs body ip ms state
  case "$spec" in
    *://*) scheme="${spec%%://*}"; rest="${spec#*://}" ;;
  esac
  if [[ $rest =~ $COLON_FORM ]]; then
    IFS=: read -r a b c d <<< "$rest"
    hostport="$a:$b"
    cred="$c:$d"
  elif [[ $rest == *@* ]]; then
    cred="${rest%@*}"
    hostport="${rest##*@}"
  else
    hostport="$rest"
  fi
  case "$scheme" in
    socks5 | socks5h) scheme="socks5h" ;;
    socks4 | socks4a) scheme="socks4a" ;;
    https) scheme="https" ;;
    *) scheme="http" ;;
  esac
  if [ -n "$cred" ]; then
    out=$(curl -s -o - -w '\n%{http_code} %{time_total}' --max-time "$WAIT_S" --connect-timeout "$WAIT_S" \
      -A "remcheck/$REMCHECK_VERSION" -x "$scheme://$hostport" --proxy-user "$cred" "$ECHO_URL" 2>/dev/null)
  else
    out=$(curl -s -o - -w '\n%{http_code} %{time_total}' --max-time "$WAIT_S" --connect-timeout "$WAIT_S" \
      -A "remcheck/$REMCHECK_VERSION" -x "$scheme://$hostport" "$ECHO_URL" 2>/dev/null)
  fi
  tail="${out##*$'\n'}"
  body="${out%$'\n'*}"
  code="${tail%% *}"
  secs="${tail#* }"
  ip=$(printf '%s' "$body" | tr -d ' \r\n\t' | grep -Eo '^([0-9]{1,3}\.){3}[0-9]{1,3}$|^[0-9A-Fa-f:]{3,39}$' | head -n 1)
  case "$code" in
    2?? | 3??) state="ok"; ms=$(awk -v s="$secs" 'BEGIN { printf "%d", s * 1000 }') ;;
    *) state="dead"; ms="-"; ip="" ;;
  esac
  printf '%s\t%s\t%s\t%s\n' "$state" "$spec" "${ip:--}" "$ms" >> "$WORK/raw"
}

doh_batch() {
  local base="$1" names name
  shift
  names="$*"
  set --
  for name in $names; do
    set -- "$@" "$base?name=$name&type=A"
  done
  curl -s --connect-timeout 5 --max-time 8 -H 'accept: application/dns-json' -w '\n' "$@" 2>/dev/null |
    awk '{
      gsub(/"Status" ?:/, "\001&")
      n = split($0, part, "\001")
      for (i = 2; i <= n; i++) {
        p = part[i]
        if (!match(p, /"Status" ?: ?[0-9]+/)) continue
        st = substr(p, RSTART, RLENGTH); gsub(/[^0-9]/, "", st)
        if (st != "0" && st != "3") continue
        if (!match(p, /"name" ?: ?"[^"]+"/)) continue
        nm = tolower(substr(p, RSTART, RLENGTH)); sub(/^"name" ?: ?"/, "", nm); sub(/"$/, "", nm); sub(/\.$/, "", nm)
        code = ""
        if (st == "0" && match(p, /"data" ?: ?"127\.[0-9.]+"/)) { code = substr(p, RSTART, RLENGTH); sub(/^"data" ?: ?"/, "", code); sub(/"$/, "", code) }
        print nm "\t" code
      }
    }'
}

rbl_group() {
  local ips="$1" ip o1 o2 o3 o4 zone names="" first="$DOH_A" second="$DOH_B" got left
  for ip in $ips; do
    IFS=. read -r o1 o2 o3 o4 <<< "$ip"
    for zone in $DNSBL_ZONES; do
      names="$names $o4.$o3.$o2.$o1.$zone"
    done
  done
  if [ $((10#$o4 % 2)) -eq 1 ]; then
    first="$DOH_B"
    second="$DOH_A"
  fi
  got=$(doh_batch "$first" $names)
  left=$(printf '%s\n' "$got" | awk -F'\t' -v names="$names" '
    { seen[$1] = 1 }
    END { n = split(names, z, " "); for (i = 1; i <= n; i++) if (!(z[i] in seen)) printf "%s ", z[i] }')
  [ -n "$left" ] && got="$got
$(doh_batch "$second" $left)"
  printf '%s\n' "$got" | awk -F'\t' -v ips="$ips" -v zones="$DNSBL_ZONES" -v widez=" $WIDE_ZONES " -v soft=" $SOFT_CODES " '
    $1 != "" { seen[$1] = 1; code[$1] = $2 }
    END {
      m = split(ips, list, " ")
      n = split(zones, z, " ")
      for (j = 1; j <= m; j++) {
        split(list[j], o, ".")
        rev = o[4] "." o[3] "." o[2] "." o[1]
        hits = ""; wide = ""; miss = ""
        for (i = 1; i <= n; i++) {
          k = z[i]
          nm = rev "." k
          if (!(nm in seen)) { miss = miss " " k; continue }
          c = code[nm]
          if (c == "" || c ~ /^127\.255\.255\./) continue
          if (index(widez, " " k " ") || index(soft, " " k "=" c " ")) wide = wide " " k
          else hits = hits " " k
        }
        sub(/^ /, "", hits); sub(/^ /, "", wide); sub(/^ /, "", miss)
        printf "%s\t%s\t%s\t%s\n", list[j], hits, wide, miss
      }
    }' >> "$WORK/rblraw"
}

run_lanes() {
  local list="$1" job="$2" width="$3" gate="$WORK/gate.$2" i item
  mkfifo "$gate" || die "cannot create a fifo in $WORK"
  exec 7<> "$gate"
  rm -f "$gate"
  i=0
  while [ "$i" -lt "$width" ]; do
    printf '.' >&7
    i=$((i + 1))
  done
  while IFS= read -r item || [ -n "$item" ]; do
    read -r -n 1 -u 7 _
    { "$job" "$item"; printf '.' >&7; } &
  done < "$list"
  wait
  exec 7>&-
}

ticker() {
  local file="$1" total="$2" label="$3" done_n alive_n
  while :; do
    done_n=$(wc -l < "$file" | tr -d ' ')
    alive_n=$(grep -c '^ok' "$file" 2>/dev/null)
    if [ "$label" = "rbl" ]; then
      printf '\r  exit IPs looked up %s/%s   ' "$done_n" "$total" >&2
    else
      printf '\r  checked %s/%s, alive %s   ' "$done_n" "$total" "$alive_n" >&2
    fi
    sleep 1
  done
}

start_ticker() {
  [ "$QUIET" = 1 ] && return
  ticker "$@" &
  TICKER=$!
  disown "$TICKER" 2>/dev/null
}

stop_ticker() {
  [ -n "$TICKER" ] || return
  kill "$TICKER" 2>/dev/null
  TICKER=""
  printf '\r%60s\r' '' >&2
}

rbl_pass() {
  local ips="$1" width=$RBL_LANES
  [ "$LANES_SET" = 1 ] && width=$LANES
  [ "$width" -gt "$RBL_LANES_MAX" ] && width=$RBL_LANES_MAX
  IPS=$(wc -l < "$ips" | tr -d ' ')
  [ "$IPS" -gt 0 ] || return 0
  start_ticker "$WORK/rblraw" "$IPS" rbl
  run_lanes "$ips" rbl_group "$width"
  stop_ticker
  IPS_LISTED=$(awk -F'\t' '$2 != ""' "$WORK/rblraw" | wc -l | tr -d ' ')
  IPS_CLEAN=$((IPS - IPS_LISTED))
  IPS_GAPS=$(awk -F'\t' '$4 != ""' "$WORK/rblraw" | wc -l | tr -d ' ')
}

rbl_summary() {
  local base="$1" label="$2"
  printf 'Clean:   %s %s (%s%%), exit IPs %s (%s%%)\n' "$CLEAN" "$label" "$(pct "$CLEAN" "$base")" "$IPS_CLEAN" "$(pct "$IPS_CLEAN" "$IPS")"
  printf 'Listed:  %s %s (%s%%), exit IPs %s (%s%%)\n' "$LISTED" "$label" "$(pct "$LISTED" "$base")" "$IPS_LISTED" "$(pct "$IPS_LISTED" "$IPS")"
  [ "$IPS_GAPS" -gt 0 ] && printf 'Note:    %s exit IPs missed an answer from some lists, see the no_answer column\n' "$IPS_GAPS"
  return 0
}

TAB=$(printf '\t')
IPS=0
IPS_CLEAN=0
IPS_LISTED=0
IPS_GAPS=0

if [ "$CSV_FAST" = 1 ]; then
  cut -f2 "$WORK/csvmap" | sort -u > "$WORK/ips"
  UNIQ=$(wc -l < "$WORK/ips" | tr -d ' ')
  [ "$QUIET" = 1 ] || printf 'remcheck %s: %s proxies from a RemProxy CSV, %s unique exit IPs from the real_ip column, rbl only\n' "$REMCHECK_VERSION" "$TOTAL" "$UNIQ" >&2
  rbl_pass "$WORK/ips"
  {
    printf 'proxy\texit_ip\tcountry\tspeed\tuptime\tlisted_in\tnetwork_flagged\tno_answer\n'
    awk -F'\t' -v OFS='\t' 'NR == FNR { hit[$1] = $2; wide[$1] = $3; gap[$1] = $4; next }
      { print $1, $2, $3, $4, $5, hit[$2], wide[$2], gap[$2] }' "$WORK/rblraw" "$WORK/csvmap"
  } > "$DEST/results.tsv"
  awk -F'\t' 'NR == FNR { if ($2 != "") bad[$1] = 1; next } !($2 in bad) { print $1 }' "$WORK/rblraw" "$WORK/csvmap" > "$DEST/clean.txt"
  awk -F'\t' 'NR == FNR { if ($2 != "") bad[$1] = 1; next } ($2 in bad) { print $1 }' "$WORK/rblraw" "$WORK/csvmap" > "$DEST/listed.txt"
  CLEAN=$(wc -l < "$DEST/clean.txt" | tr -d ' ')
  LISTED=$(wc -l < "$DEST/listed.txt" | tr -d ' ')
  SPENT=$SECONDS
  printf 'Done in %dm %02ds, exit IPs taken from the CSV without connecting\n' $((SPENT / 60)) $((SPENT % 60))
  printf 'Proxies: %s, unique exit IPs %s (%s proxies share an exit IP with another row)\n' "$TOTAL" "$IPS" $((TOTAL - IPS))
  rbl_summary "$TOTAL" proxies
  printf 'Results: %s\n' "$DEST"
  [ "$CLEAN" -gt 0 ] || exit 1
  exit 0
fi

[ "$QUIET" = 1 ] || printf 'remcheck %s: %s proxies, mode %s, %s at once\n' "$REMCHECK_VERSION" "$TOTAL" "$MODE" "$LANES" >&2
start_ticker "$WORK/raw" "$TOTAL" connect
run_lanes "$WORK/pool" probe_one "$LANES"
stop_ticker

awk -F'\t' '$1 == "ok"' "$WORK/raw" | sort -t "$TAB" -k4,4n > "$WORK/ok"
awk -F'\t' '$1 != "ok"' "$WORK/raw" > "$WORK/bad"
cut -f2 "$WORK/ok" > "$DEST/alive.txt"
cut -f2 "$WORK/bad" > "$DEST/dead.txt"
ALIVE=$(wc -l < "$WORK/ok" | tr -d ' ')
DEAD=$(wc -l < "$WORK/bad" | tr -d ' ')

if [ "$MODE" = "rbl" ]; then
  cut -f3 "$WORK/ok" | grep -E "$IPV4_FORM" | sort -u > "$WORK/ips"
  rbl_pass "$WORK/ips"
  {
    printf 'proxy\tstatus\texit_ip\tms\tlisted_in\tnetwork_flagged\tno_answer\n'
    awk -F'\t' -v OFS='\t' 'NR == FNR { hit[$1] = $2; wide[$1] = $3; gap[$1] = $4; next }
      { print $2, $1, $3, $4, hit[$3], wide[$3], gap[$3] }' "$WORK/rblraw" "$WORK/ok" "$WORK/bad"
  } > "$DEST/results.tsv"
  awk -F'\t' 'NR == FNR { if ($2 != "") bad[$1] = 1; next } !($3 in bad) { print $2 }' "$WORK/rblraw" "$WORK/ok" > "$DEST/clean.txt"
  awk -F'\t' 'NR == FNR { if ($2 != "") bad[$1] = 1; next } ($3 in bad) { print $2 }' "$WORK/rblraw" "$WORK/ok" > "$DEST/listed.txt"
  CLEAN=$(wc -l < "$DEST/clean.txt" | tr -d ' ')
  LISTED=$(wc -l < "$DEST/listed.txt" | tr -d ' ')
else
  {
    printf 'proxy\tstatus\texit_ip\tms\n'
    awk -F'\t' -v OFS='\t' '{ print $2, $1, $3, $4 }' "$WORK/ok" "$WORK/bad"
  } > "$DEST/results.tsv"
fi

SPENT=$SECONDS
printf 'Done in %dm %02ds\n' $((SPENT / 60)) $((SPENT % 60))
printf 'Proxies: %s\n' "$TOTAL"
printf 'Alive:   %s (%s%%)\n' "$ALIVE" "$(pct "$ALIVE" "$TOTAL")"
printf 'Dead:    %s (%s%%)\n' "$DEAD" "$(pct "$DEAD" "$TOTAL")"
if [ "$MODE" = "rbl" ]; then
  printf 'Exit IPs of alive proxies: %s\n' "$IPS"
  rbl_summary "$ALIVE" "of alive"
fi
printf 'Results: %s\n' "$DEST"
[ "$ALIVE" -gt 0 ] || exit 1
exit 0
