140 lines
4.0 KiB
Bash
Executable File
140 lines
4.0 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
# Download TLS certificates of hosts specified in the `hosts` file.
|
|
|
|
set -o errexit # (-e) exit immediately if any command has a non-zero exit status
|
|
set -o nounset # (-u) don't accept undefined variables
|
|
#set -o xtrace # for debugging
|
|
|
|
# Go where this script is.
|
|
cd "$(dirname "$0")" || exit
|
|
|
|
# Create certs directory, if it does not exist.
|
|
mkdir -p certs
|
|
|
|
|
|
# Define function for fetching a certificate.
|
|
fetch_cert() (
|
|
hp="$1" # host and port
|
|
timeout="${2:-}"
|
|
torsocks="${3:-}"
|
|
|
|
# For .onion domains, connect using Tor.
|
|
if [ -z "${hp##*.onion:*}" ]; then
|
|
torsocks='torsocks'
|
|
fi
|
|
|
|
# If a Tor connection was requested and torsocks is not installed, return.
|
|
if [ "$torsocks" != '' ] && ! command -v torsocks >/dev/null; then
|
|
>&2 echo "torsocks not available ($hp)"
|
|
return
|
|
fi
|
|
|
|
response=$($timeout $torsocks openssl s_client -connect "$hp" </dev/null 2>/dev/null \
|
|
| sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p')
|
|
|
|
# If no response and Tor was not used, try again.
|
|
if [ -z "$response" ] && [ -z "$torsocks" ]; then
|
|
sleep 5
|
|
response=$($timeout openssl s_client -connect "$hp" </dev/null 2>/dev/null \
|
|
| sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p')
|
|
fi
|
|
|
|
echo "$response"
|
|
)
|
|
|
|
|
|
# Loop through all hosts.
|
|
while read -r host; do
|
|
|
|
printf "%s" "$host"
|
|
|
|
port='1965' # default Gemini port
|
|
|
|
# Check if host contains square brackets:
|
|
# [IPv4], [IPv6], [IPv4]:port or [IPv6]:port.
|
|
if expr "$host" : '^\[.*\]' >/dev/null; then
|
|
if expr "$host" : '^\[.*\]\:\([0-9]*\)$' >/dev/null; then
|
|
port=$(expr "$host" : '^\[.*\]\:\([0-9]*\)$')
|
|
fi
|
|
host=$(expr "$host" : '^\[\(.*\)\]')
|
|
|
|
# No square brackets, just host:port.
|
|
# If $host contains only a single ":", then it is not an IPv6 address.
|
|
elif [ "$(echo "$host" | tr -dc ':' | wc -c)" = 1 ]; then
|
|
port=$(echo "$host" | cut -d ':' -f 2)
|
|
host=$(echo "$host" | cut -d ':' -f 1)
|
|
fi
|
|
|
|
# Hostname to punycode.
|
|
host=$(echo "$host" | idn --allow-unassigned)
|
|
|
|
# Hostname to lowercase.
|
|
host=$(echo "$host" | tr '[:upper:]' '[:lower:]')
|
|
|
|
# Generate host_and_port string.
|
|
if [ -z "${host##*:*}" ]; then
|
|
# If host is an IPv6 address, add brackets around it.
|
|
host_and_port="[$host]:$port"
|
|
else
|
|
host_and_port="$host:$port"
|
|
fi
|
|
|
|
# Get cert.
|
|
cert=$(fetch_cert "$host_and_port" 'timeout 10')
|
|
|
|
if [ -z "$cert" ]; then
|
|
>&2 echo # empty line
|
|
>&2 echo "$host_and_port - connection failed"
|
|
fi
|
|
|
|
# If "tor" option is used, then connect again via Tor,
|
|
# to check if we get the same cert from a different network perspective.
|
|
if [ "${1:-}" = 'tor' ] && [ -n "${host##*.onion}" ]; then
|
|
|
|
# If torsocks is not installed, return.
|
|
if ! command -v torsocks >/dev/null; then
|
|
>&2 echo "torsocks not available ($host_and_port)"
|
|
exit 1
|
|
fi
|
|
|
|
cert_via_tor=$(fetch_cert "$host_and_port" 'timeout 25' 'torsocks')
|
|
|
|
if [ -z "$cert_via_tor" ]; then
|
|
[ -n "$cert" ] && >&2 echo # output empty line to stderr if cert was downloaded without Tor
|
|
>&2 echo "$host_and_port - Tor connection failed"
|
|
elif [ -n "$cert" ] && [ "$cert" != "$cert_via_tor" ]; then
|
|
>&2 echo "$host_and_port - Tor VERIFICATION FAILED (certs don't match)!!!"
|
|
# In this case, don't save any certificate to file.
|
|
# Output both certificates to stderr instead.
|
|
>&2 echo "CERT:"
|
|
>&2 echo "$cert"
|
|
>&2 echo "CERT VIA TOR:"
|
|
>&2 echo "$cert_via_tor"
|
|
continue
|
|
else
|
|
# If direct connection failed and Tor connection succeeded,
|
|
# use the cert received via Tor.
|
|
cert="$cert_via_tor"
|
|
fi
|
|
|
|
fi
|
|
|
|
if [ -n "$cert" ]; then
|
|
# If we got a cert back, then the host and port were valid,
|
|
# so they are safe to include in a file name.
|
|
# Convert from punycode to unicode, if needed.
|
|
host_and_port=$(echo "$host_and_port" | idn --allow-unassigned --idna-to-unicode)
|
|
echo "$cert" > "certs/${host_and_port}.pem"
|
|
printf ' - OK'
|
|
else
|
|
printf ' - failed'
|
|
fi
|
|
|
|
echo # newline
|
|
sleep 0.3
|
|
|
|
done < hosts
|
|
|
|
echo OK
|