mirror of
https://github.com/pi-hole/pi-hole
synced 2024-12-22 14:58:08 +00:00
b304562c8e
Signed-off-by: DL6ER <dl6er@dl6er.de>
291 lines
8.9 KiB
Bash
Executable File
291 lines
8.9 KiB
Bash
Executable File
#!/usr/bin/env sh
|
|
# shellcheck disable=SC3043 #https://github.com/koalaman/shellcheck/wiki/SC3043#exceptions
|
|
|
|
# Pi-hole: A black hole for Internet advertisements
|
|
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
|
# Network-wide ad blocking via your own hardware.
|
|
#
|
|
# Script to hold api functions for use in other scripts
|
|
#
|
|
# This file is copyright under the latest version of the EUPL.
|
|
# Please see LICENSE file for your rights under this license.
|
|
|
|
|
|
# The basic usage steps are
|
|
# 1) Test Availability of the API
|
|
# 2) Try to authenticate (read password if needed)
|
|
# 3) Get the data from the API endpoint
|
|
# 4) Delete the session
|
|
|
|
|
|
TestAPIAvailability() {
|
|
|
|
# as we are running locally, we can get the port value from FTL directly
|
|
local chaos_api_list availabilityResponse
|
|
|
|
# Query the API URLs from FTL using CHAOS TXT local.api.ftl
|
|
# The result is a space-separated enumeration of full URLs
|
|
# e.g., "http://localhost:80/api/" "https://localhost:443/api/"
|
|
chaos_api_list="$(dig +short chaos txt local.api.ftl @127.0.0.1)"
|
|
|
|
# If the query was not successful, the variable is empty
|
|
if [ -z "${chaos_api_list}" ]; then
|
|
echo "API not available. Please check connectivity"
|
|
exit 1
|
|
fi
|
|
|
|
# Iterate over space-separated list of URLs
|
|
while [ -n "${chaos_api_list}" ]; do
|
|
# Get the first URL
|
|
API_URL="${chaos_api_list%% *}"
|
|
# Strip leading and trailing quotes
|
|
API_URL="${API_URL%\"}"
|
|
API_URL="${API_URL#\"}"
|
|
|
|
# Test if the API is available at this URL
|
|
availabilityResponse=$(curl -skS -o /dev/null -w "%{http_code}" "${API_URL}auth")
|
|
|
|
# Test if http status code was 200 (OK) or 401 (authentication required)
|
|
if [ ! "${availabilityResponse}" = 200 ] && [ ! "${availabilityResponse}" = 401 ]; then
|
|
# API is not available at this port/protocol combination
|
|
API_PORT=""
|
|
else
|
|
# API is available at this URL combination
|
|
|
|
if [ "${availabilityResponse}" = 200 ]; then
|
|
# API is available without authentication
|
|
needAuth=false
|
|
fi
|
|
|
|
break
|
|
fi
|
|
|
|
# Remove the first URL from the list
|
|
local last_api_list
|
|
last_api_list="${chaos_api_list}"
|
|
chaos_api_list="${chaos_api_list#* }"
|
|
|
|
# If the list did not change, we are at the last element
|
|
if [ "${last_api_list}" = "${chaos_api_list}" ]; then
|
|
# Remove the last element
|
|
chaos_api_list=""
|
|
fi
|
|
done
|
|
|
|
# if API_PORT is empty, no working API port was found
|
|
if [ -n "${API_PORT}" ]; then
|
|
echo "API not available at: ${API_URL}"
|
|
echo "Exiting."
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
LoginAPI() {
|
|
# If the API URL is not set, test the availability
|
|
if [ -z "${API_URL}" ]; then
|
|
TestAPIAvailability
|
|
fi
|
|
|
|
# Exit early if authentication is not needed
|
|
if [ "${needAuth}" = false ]; then
|
|
if [ "${1}" = "verbose" ]; then
|
|
echo "API Authentication: Not needed"
|
|
fi
|
|
return
|
|
fi
|
|
|
|
# Try to read the CLI password (if enabled and readable by the current user)
|
|
if [ -r /etc/pihole/cli_pw ]; then
|
|
password=$(cat /etc/pihole/cli_pw)
|
|
|
|
if [ "${1}" = "verbose" ]; then
|
|
echo "API Authentication: Trying to use CLI password"
|
|
fi
|
|
|
|
# Try to authenticate using the CLI password
|
|
Authentication "${1}"
|
|
|
|
elif [ "${1}" = "verbose" ]; then
|
|
echo "API Authentication: CLI password not available"
|
|
fi
|
|
|
|
|
|
|
|
# If this did not work, ask the user for the password
|
|
while [ "${validSession}" = false ] || [ -z "${validSession}" ] ; do
|
|
echo "Authentication failed. Please enter your Pi-hole password"
|
|
|
|
# secretly read the password
|
|
secretRead; printf '\n'
|
|
|
|
# Try to authenticate again
|
|
Authentication "${1}"
|
|
done
|
|
|
|
}
|
|
|
|
Authentication() {
|
|
sessionResponse="$(curl -skS -X POST "${API_URL}auth" --user-agent "Pi-hole cli " --data "{\"password\":\"${password}\"}" )"
|
|
|
|
if [ -z "${sessionResponse}" ]; then
|
|
echo "No response from FTL server. Please check connectivity"
|
|
exit 1
|
|
fi
|
|
# obtain validity and session ID from session response
|
|
validSession=$(echo "${sessionResponse}"| jq .session.valid 2>/dev/null)
|
|
SID=$(echo "${sessionResponse}"| jq --raw-output .session.sid 2>/dev/null)
|
|
|
|
if [ "${1}" = "verbose" ]; then
|
|
if [ "${validSession}" = true ]; then
|
|
echo "API Authentication: ${COL_GREEN}Success${COL_NC}"
|
|
else
|
|
echo "API Authentication: ${COL_RED}Failed${COL_NC}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
LogoutAPI() {
|
|
# if a valid Session exists (no password required or successful Authentication) and
|
|
# SID is not null (successful Authentication only), delete the session
|
|
if [ "${validSession}" = true ] && [ ! "${SID}" = null ]; then
|
|
# Try to delete the session. Omit the output, but get the http status code
|
|
deleteResponse=$(curl -skS -o /dev/null -w "%{http_code}" -X DELETE "${API_URL}auth" -H "Accept: application/json" -H "sid: ${SID}")
|
|
|
|
case "${deleteResponse}" in
|
|
"401") echo "Logout attempt without a valid session. Unauthorized!";;
|
|
"204") if [ "${1}" = "verbose" ]; then echo "API Logout: ${COL_GREEN}Success${COL_NC} (session deleted)"; fi;;
|
|
esac;
|
|
elif [ "${1}" = "verbose" ]; then
|
|
echo "API Logout: ${COL_GREEN}Success${COL_NC} (no valid session)"
|
|
fi
|
|
}
|
|
|
|
GetFTLData() {
|
|
local data response status
|
|
# get the data from querying the API as well as the http status code
|
|
response=$(curl -skS -w "%{http_code}" -X GET "${API_URL}$1" -H "Accept: application/json" -H "sid: ${SID}" )
|
|
|
|
# status are the last 3 characters
|
|
status="${response#"${response%???}"}"
|
|
# data is everything from response without the last 3 characters
|
|
data="${response%???}"
|
|
|
|
if [ "${2}" = "raw" ]; then
|
|
# return the raw response
|
|
echo "${response}"
|
|
else
|
|
# return only the data
|
|
if [ "${status}" = 200 ]; then
|
|
# response OK
|
|
echo "${data}"
|
|
else
|
|
# connection lost
|
|
echo "${status}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
PostFTLData() {
|
|
local data response status
|
|
# send the data to the API
|
|
response=$(curl -skS -w "%{http_code}" -X POST "${API_URL}$1" --data-raw "$2" -H "Accept: application/json" -H "sid: ${SID}" )
|
|
# data is everything from response without the last 3 characters
|
|
if [ "${3}" = "status" ]; then
|
|
# Keep the status code appended if requested
|
|
printf %s "${response}"
|
|
else
|
|
# Strip the status code
|
|
printf %s "${response%???}"
|
|
fi
|
|
}
|
|
|
|
secretRead() {
|
|
|
|
# POSIX compliant function to read user-input and
|
|
# mask every character entered by (*)
|
|
#
|
|
# This is challenging, because in POSIX, `read` does not support
|
|
# `-s` option (suppressing the input) or
|
|
# `-n` option (reading n chars)
|
|
|
|
|
|
# This workaround changes the terminal characteristics to not echo input and later resets this option
|
|
# credits https://stackoverflow.com/a/4316765
|
|
# showing asterisk instead of password
|
|
# https://stackoverflow.com/a/24600839
|
|
# https://unix.stackexchange.com/a/464963
|
|
|
|
|
|
# Save current terminal settings (needed for later restore after password prompt)
|
|
stty_orig=$(stty -g)
|
|
|
|
stty -echo # do not echo user input
|
|
stty -icanon min 1 time 0 # disable canonical mode https://man7.org/linux/man-pages/man3/termios.3.html
|
|
|
|
unset password
|
|
unset key
|
|
unset charcount
|
|
charcount=0
|
|
while key=$(dd ibs=1 count=1 2>/dev/null); do #read one byte of input
|
|
if [ "${key}" = "$(printf '\0' | tr -d '\0')" ] ; then
|
|
# Enter - accept password
|
|
break
|
|
fi
|
|
if [ "${key}" = "$(printf '\177')" ] ; then
|
|
# Backspace
|
|
if [ $charcount -gt 0 ] ; then
|
|
charcount=$((charcount-1))
|
|
printf '\b \b'
|
|
password="${password%?}"
|
|
fi
|
|
else
|
|
# any other character
|
|
charcount=$((charcount+1))
|
|
printf '*'
|
|
password="$password$key"
|
|
fi
|
|
done
|
|
|
|
# restore original terminal settings
|
|
stty "${stty_orig}"
|
|
}
|
|
|
|
apiFunc() {
|
|
local data response status status_col
|
|
|
|
# Authenticate with the API
|
|
LoginAPI verbose
|
|
echo ""
|
|
|
|
echo "Requesting: ${COL_PURPLE}GET ${COL_CYAN}${API_URL}${COL_YELLOW}$1${COL_NC}"
|
|
echo ""
|
|
|
|
# Get the data from the API
|
|
response=$(GetFTLData "$1" raw)
|
|
|
|
# status are the last 3 characters
|
|
status="${response#"${response%???}"}"
|
|
# data is everything from response without the last 3 characters
|
|
data="${response%???}"
|
|
|
|
# Output the status (200 -> green, else red)
|
|
if [ "${status}" = 200 ]; then
|
|
status_col="${COL_GREEN}"
|
|
else
|
|
status_col="${COL_RED}"
|
|
fi
|
|
echo "Status: ${status_col}${status}${COL_NC}"
|
|
|
|
# Output the data. Format it with jq if available and data is actually JSON.
|
|
# Otherwise just print it
|
|
echo "Data:"
|
|
if command -v jq >/dev/null && echo "${data}" | jq . >/dev/null 2>&1; then
|
|
echo "${data}" | jq .
|
|
else
|
|
echo "${data}"
|
|
fi
|
|
|
|
# Delete the session
|
|
LogoutAPI verbose
|
|
}
|