Not a member of GistPad yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env bash
- # portcheck.sh - is a TCP port reachable from the internet?
- #
- # Usage: ./portcheck.sh [port]
- # Exit : 0 = OPEN (externally reachable), 1 = CLOSED/FILTERED, 2 = inconclusive
- #
- # One external endpoint (check-host.net), TCP only, short timeouts.
- set -uo pipefail
- PORT="${1:-8899}"
- TIMEOUT=3 # local connect timeout, seconds
- MAX_WAIT=45 # max wait for external nodes, seconds
- # ---- dependencies ----------------------------------------------------------
- for c in curl jq; do
- command -v "$c" >/dev/null 2>&1 || { echo "Missing dependency: $c"; exit 2; }
- done
- # ---- public IP -------------------------------------------------------------
- IP=$(curl -s --max-time 6 'https://api.ipify.org' \
- || curl -s --max-time 6 'https://ifconfig.me/ip' \
- || curl -s --max-time 6 'https://icanhazip.com')
- [[ -z "$IP" ]] && { echo "Cannot determine public IP"; exit 2; }
- echo "IP : $IP"
- echo "Port : $PORT/tcp"
- # ---- local probe (fast, tells us if hairpin works) -------------------------
- if command -v nc >/dev/null 2>&1; then
- R=$(timeout $((TIMEOUT+2)) nc -zv -w "$TIMEOUT" "$IP" "$PORT" 2>&1)
- if grep -qi succeeded <<<"$R"; then echo "Local : reachable (hairpin OK)"
- elif grep -qi refused <<<"$R"; then echo "Local : refused"
- else echo "Local : timeout/filtered"; fi
- fi
- # ---- external probe (the real test) ----------------------------------------
- RID=$(curl -sS --max-time 10 -H 'Accept: application/json' \
- | jq -r '.request_id // empty')
- [[ -z "$RID" ]] && { echo "RESULT: INCONCLUSIVE (probe service unavailable)"; exit 2; }
- WAIT=0
- while (( WAIT < MAX_WAIT )); do
- sleep 3; WAIT=$((WAIT+3))
- OUT=$(curl -sS --max-time 10 -H 'Accept: application/json' \
- [[ -z "$OUT" ]] && continue
- OPEN=$(jq -r '[to_entries[] | select(.value[0].time != null)] | length' <<<"$OUT" 2>/dev/null || echo 0)
- FAIL=$(jq -r '[to_entries[] | select(.value[0].error != null or .value[0].timeout != null)] | length' <<<"$OUT" 2>/dev/null || echo 0)
- PEND=$(jq -r '[to_entries[] | select(.value[0] == null)] | length' <<<"$OUT" 2>/dev/null || echo 0)
- # A single node connecting proves the port is open - stop early.
- (( OPEN >= 1 )) && break
- # All nodes reported a failure - stop early.
- (( PEND == 0 )) && break
- done
- echo "Nodes: open=$OPEN failed=$FAIL pending=$PEND"
- if (( OPEN >= 2 )); then echo "RESULT: OPEN - port $PORT IS externally reachable"
- elif (( OPEN == 1 )); then echo "RESULT: OPEN (single node) - port $PORT IS externally reachable"
- elif (( FAIL >= 1 )); then echo "RESULT: CLOSED/FILTERED - port $PORT is NOT externally reachable"
- else echo "RESULT: INCONCLUSIVE"; exit 2
- fi
- (( OPEN >= 1 )) && exit 0 || exit 1
RAW Paste Data
Copied
