#!/usr/bin/env bash # This project is licensed under the Blue Oak Model License 1.0.0. # See: https://blueoakcouncil.org/license/1.0.0 # A Bash-based Finger client (RFC 1288) using /dev/tcp for socket connections. # Full support for 8-bit characters, long files and ASCII art. # install : chmod +x bfinger && sudo install bfinger /usr/local/bin # uninstall: sudo rm /usr/local/bin/bfinger # bfinger -h --help (for help) # Version 2.0 (May 30, 2025) # Added DoS protection: # - Hard memory limit (~1MB default, configurable via BFINGER_MAX_BYTES) # - Per-byte timeouts (-t 5) # - Clean stream truncation with visual cue # Version 1.0 (May 01, 2025) # A complete Finger client # - Uses Bash's /dev/tcp for socket connections # - Full 8-bit character support (bfinger codepage437@happynetbox.com) # - No artificial stream limits (v2.0 adds 1MB default, configurable) # - Per-byte timeout protection (-t 5) without buffering delays # ------------------------------ Start Script ------------------------------- set -eo pipefail # Removed -u to avoid unbound variable errors [ -n "${DEBUG:-}" ] && set -xu # Only enable -u in debug mode VERSION="2.0 (May 30, 2025)" # enforce consistent locale for byte handling export LC_ALL=C # Cleanup on interrupt cleanup() { exec 3<&- 3>&- 2>/dev/null || true exit 130 } trap cleanup INT # Memory safety - arbitrary stream limit (~1MB) - adjust as needed. # Protects against endless data streams (malicious/poorly behaving servers) MAX_BYTES=${BFINGER_MAX_BYTES:-1000000} # Show usage if no args or help flag if [ $# -eq 0 ] || [[ "$1" =~ ^(/|\-){1,2}(\?|h|help)$ ]]; then echo "" echo "bfinger $VERSION - A Bash-based Finger Client (RFC 1288)" echo "" echo "Uses Bash's /dev/tcp for socket connections to query remote Finger" echo "servers. Full support for 8-bit characters, long files and ASCII art." echo "" echo "Usage:" echo " bfinger @domain.com # Queries host" echo " bfinger user@domain.com # Specific user" echo " bfinger user domain.com # Alternative" echo " bfinger @localhost # fingerd must be installed locally" echo "" echo "Options:" echo " -v, --version Show version info" echo " -h, --help, -?, /? Show this help message" echo "" echo "Examples:" echo " bfinger fingerverse@happynetbox.com | less" echo " bfinger @plan.cat | head -n 20" echo "" echo "install: chmod +x bfinger && sudo install bfinger /usr/local/bin" echo "uninstall: sudo rm /usr/local/bin/bfinger" echo "" exit 0 fi # Show version if [[ "${1:-}" == "--version" || "${1:-}" == "-v" ]]; then echo "bfinger $VERSION - A Bash-based finger client" exit 0 fi # Parse input if [[ "$1" == @* ]]; then USER="" HOST="${1#@}" elif [[ "$1" =~ @ ]]; then USER="${1%%@*}" HOST="${1##*@}" else USER="${1:-}" HOST="${2:-}" fi PORT=79 # validate host. Good enough... if [ -z "$HOST" ]; then echo "Error: No host specified" >&2 exit 1 fi # basic hostname validation. Also good enough:-) if [[ ! "$HOST" =~ ^[a-zA-Z0-9._-]+$ ]]; then echo "Error: Hostname contains invalid characters" >&2 exit 1 fi # check DNS resolution if ! getent hosts "$HOST" >/dev/null; then echo "Error: Host '$HOST' could not be resolved" >&2 exit 1 fi # Connect and stream response if ! timeout 5 bash -c "exec 3<>/dev/tcp/$HOST/$PORT"; then echo "Error: Connection to $HOST:$PORT timed out or failed" >&2 echo "Tip: Try 'telnet $HOST $PORT' to debug" >&2 exit 1 fi exec 3<>/dev/tcp/"$HOST"/"$PORT" # Send request printf "%s\r\n" "$USER" >&3 # Read response with 8-bit and whitespace preservation bytes_read=0 while IFS= read -r -t 5 -N 1 char <&3 && (( bytes_read++ < MAX_BYTES )); do printf "%s" "$char" done (( bytes_read >= MAX_BYTES )) && echo >&2 # Adds a line break when truncated (visual cue) # Cleanup exec 3<&- 3>&- # .-----------------------------. .-------------. .-------------------------. # | https://640kb.neocities.org |--| 30 May 2025 |--| bfinger@happynetbox.com | # '-----------------------------' '-------------' '-------------------------'