#!/bin/sh -e

loosely_ipv4_and_port() {
	# called thousands of times per consensus update
	case "$1" in           *[!0-9.:]*) return 1; esac
	case "$1" in *[0-9].*[0-9]:*[0-9]) return 0; esac
	return 1
}

bridges() {
	while read -r LINE; do
		# convert "Bridge [transport] IP:ORPort [fingerprint]"
		# to "IP,ORPort"

		set -- $LINE

		if test "$1" = Bridge; then
			for s in "$2" "$3"; do
				if loosely_ipv4_and_port "$s"; then
					echo "${s%:*},${s#*:}"
					break
				fi
			done
		fi
	done <<-END | corridor-load-ipset-relays
		$BRIDGES
	END
}

receive_consensus() {
	if test -n "$TOR_CONTROL_SOCKET"; then
		CTRL=UNIX-CONNECT:$TOR_CONTROL_SOCKET
	else
		CTRL=TCP:${TOR_CONTROL_HOST-localhost}:${TOR_CONTROL_PORT-9051}
	fi

	if test -n "$TOR_CONTROL_COOKIE_AUTH_FILE"; then
		hex()  { perl -ne "print unpack '(H2)*'"; }
		auth() { hex <"$TOR_CONTROL_COOKIE_AUTH_FILE"; }
	else
		auth() { echo "\"$TOR_CONTROL_PASSWD\""; }
	fi

	while :; do
		# Pass a large 'total inactivity timeout' of 2^30 seconds
		# (34 years) to work around a socat v1.8.0.x bug fixed in
		# v1.8.0.3 where there is an immediate timeout in case of
		# ignoreeof. https://repo.or.cz/socat.git/commit/fa67d7d
		socat -T 1073741824 "$CTRL",crnl -,ignoreeof <<-END || :
			AUTHENTICATE `auth`
			SETCONF DormantTimeoutEnabled=0
			GETINFO config/defaults
			GETINFO ns/all
			SETEVENTS NEWCONSENSUS
		END
		sleep 1
	done
}

process_consensus() {
	HARDCODED_DIRS=

	while read -r LINE; do
		set -- $LINE

		case "$1" in
		DirAuthority|FallbackDir)
			IP=
			ORPORT=

			for s; do
				case "$s" in
				orport=*)
					ORPORT=${s#*=}
				;;
				*)
					s=${s#\"}
					if loosely_ipv4_and_port "$s"; then
						IP=${s%:*}
					fi
				;;
				esac

				if test -n "$IP" -a -n "$ORPORT"; then
					HARDCODED_DIRS="$HARDCODED_DIRS
					                $IP,$ORPORT"
					break
				fi
			done
		;;

		250+ns/all=|650+*)
			echo Processing router list...
			RELAYS=
		;;

		r)
			IP=$7
			ORPORT=$8
		;;

		s)
			case         "$LINE " in *" Valid "*)
				case "$LINE " in *" Guard "*)
					RELAYS="$RELAYS
					        $IP,$ORPORT"
				esac
			esac
		;;

		.)
			case "$RELAYS" in ?*)
				{
					sed -e 's/^[[:space:]]*//' -e '/^$/d' |
					sort -u >"$RELAYS_STATE".tmp
				} <<-END
					$HARDCODED_DIRS
					$RELAYS
				END

				corridor-load-ipset-relays <"$RELAYS_STATE".tmp
				mv -- "$RELAYS_STATE".tmp "$RELAYS_STATE"
			esac
		;;

		5*)
			echo "$LINE"
			exit 1
		;;
		esac
	done >&2
}


. corridor-load-config
set -f

if test -n "$BRIDGES"; then
	bridges
else
	if test -e "$RELAYS_STATE"; then
		corridor-load-ipset-relays <"$RELAYS_STATE"
	fi

	receive_consensus | {
		trap "kill -s USR1 -- -$$" EXIT
		process_consensus
	}
fi
