Skip to content

Instantly share code, notes, and snippets.

@smhdhsn
Last active July 23, 2024 12:56
Show Gist options
  • Save smhdhsn/fe2c9f334e3d0db1f6102991ddc694a0 to your computer and use it in GitHub Desktop.
Save smhdhsn/fe2c9f334e3d0db1f6102991ddc694a0 to your computer and use it in GitHub Desktop.
shecan.ir configurator.
#! /bin/bash -e
#####################################################
# DNS configurator #
#####################################################
# Responds to a list of syscall.
trap "error 'Exiting now...'" SIGINT SIGQUIT
# CLI options (IRREPLACEABLE).
readonly bold=`tput bold`
readonly reset=`tput sgr0`
readonly red=`tput setaf 1`
readonly blue=`tput setaf 4`
readonly cyan=`tput setaf 6`
readonly grey=`tput setaf 8`
# Prints out a given message.
# Arguments:
# $1 - Message to print
# $2 - Optional flag to determine if the message should be suffixed with \r
function out {
if [[ $2 ]]; then
echo -ne "${1}\r"
else
echo -ne "${1}\n"
fi
}
# Modifies a success message.
# Arguments:
# $1 - Success message
# $2 - Optional flag to determine if the message should be prefixed
# $3 - Optional flag for line ending
function success {
local prefix='[+] '
if [[ $2 ]]; then
msg="${cyan}${bold}${prefix}${reset}${blue}${1}${reset}"
else
msg="${blue}${1}${reset}"
fi
out "$msg" $3 >> /dev/stdout
}
# Modifies an info message.
# Arguments:
# $1 - Info message
# $2 - Optional flag to determine if the message should be prefixed
# $3 - Optional flag for line ending
function info {
local prefix='[*] '
if [[ $2 ]]; then
msg="${cyan}${bold}${prefix}${reset}${grey}${1}${reset}"
else
msg="${grey}${1}${reset}"
fi
out "$msg" $3 >> /dev/stdout
}
# Modifies an error message.
# Arguments:
# $1 - Error message
# $2 - Optional flag to determine if the message should be prefixed
# $3 - Optional flag for line ending
function error {
local prefix='[-] '
if [[ $2 ]]; then
msg="${cyan}${bold}${prefix}${reset}${red}${1}${reset}"
else
msg="${red}${1}${reset}"
fi
out "$msg" $3 >> /dev/stderr \
&& exit 1
}
# Prints out the usage guide of this script.
function usage {
echo -e "Usage: $0 OPTION ARGUMENT [OPTION ARGUMENT]...
-a\tRead configurations from specified address.
-f\tChoose file to extract configurations from.
-i\tSpecify hook to extract configurations with.
-m\tChange application flow. [${FLOW_ADD}, ${FLOW_REMOVE}, ${FLOW_UPDATE}, ${FLOW_STATUS}]" >> /dev/stdout
}
# Makes print functions irreplaceable.
readonly -f usage success error info out
# RegEx pattern for validating IPv4. (IRREPLACEABLE)
readonly ipRegEx="((1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])"
# Default path to DNS configurations.
readonly configFilePath="/etc/resolv.conf"
# DNS configurator's identifier. (IRREPLACEABLE)
readonly appFlag='#DNS_CONFIGURATOR'
# Key for configuring a DNS.
readonly configKey="nameserver"
# This section holds keys of application's flow.
# status: Checks if any configurations are already applied by this script.
# update: Removes old configurations and adds new configurations.
# remove: Removes old configurations.
# add: Adds new configurations.
readonly FLOW_STATUS="status"
readonly FLOW_UPDATE="update"
readonly FLOW_REMOVE="remove"
readonly FLOW_ADD="add"
# This section holds keys of application's source of configurations.
# file: Reads configurations from file.
# url: Extracts configurations from url.
readonly SOURCE_FILE="file"
readonly SOURCE_URL="url"
# Default url to get DNS configurations from.
url='https://shecan.ir'
# Default hook for catching DNS configurations.
hook='shecan-dns-ips'
# Default application's source form extracting configurations.
configSource=$SOURCE_URL
# Default application flow.
flow=$FLOW_ADD
# Gets the program's source of configurations based on input.
# Arguments:
# $1 - Source value (URL or file path)
# $2 - Source type (file or URL)
function getSource {
configSource=$2
if [[ $configSource == $SOURCE_FILE ]]; then
configFile=$1
elif [[ $configSource == $SOURCE_URL ]]; then
url=$1
fi
}
# Gets the program's flow based on input.
# Arguments:
# $1 - Flow value (add, remove, update, status)
function getFlow {
if grep -qE "\b(${FLOW_ADD}|${FLOW_REMOVE}|${FLOW_UPDATE}|${FLOW_STATUS})\b" <<< $1; then
flow=$1
else
usage; exit 1
fi
}
# Writes given addresses to the local DNS configuration file.
# Arguments:
# $@ - List of DNS addresses
function write {
for address in $@; do
if ! grep -Exq "${configKey}\s+${address}\s+${appFlag}$" $configFilePath; then
sudo sed -i "$(grep -n "${configKey}" ${configFilePath} | cut -f 1 -d ':' | head -n 1)i\\${configKey} ${address} \\t${appFlag}" $configFilePath
fi
done
}
# Removes old DNS configurations from local DNS configuration file. (Identifies old configurations with app flag)
function remove {
sudo sed -i "/${appFlag}$/d" $configFilePath
}
# Makes managing functions irreplaceable.
readonly -f getSource getFlow write remove
# Gets arguments from input.
while getopts "a:f:i:m:h" arg; do
case $arg in
a )
getSource $OPTARG $SOURCE_URL
;;
f )
getSource $OPTARG $SOURCE_FILE
;;
i )
hook=$OPTARG
;;
m )
getFlow $OPTARG
;;
h )
usage; exit
;;
* )
usage; exit 1
;;
esac
done
# Checks if the chosen flow requires removing old configurations.
if [[ $flow == $FLOW_UPDATE || $flow == $FLOW_REMOVE ]]; then
{ # This section removes old configurations from local DNS configuration file.
remove \
&& success "Old configurations have been removed." true
} || {
error "Error on removing old configuration files." true
}
# If the flow is 'off' the application needs to be terminated.
if [[ $flow == $FLOW_REMOVE ]]; then
info "All configurations has been removed."; exit
fi
elif [[ $flow == $FLOW_STATUS ]]; then
if grep -Fq "${appFlag}" "${configFilePath}"; then
info "Configurations are ${blue}active${reset}."; exit
else
info "Configurations are ${red}inactive${reset}."; exit
fi
fi
# This section is responsible for chosing the source of configurations [URL, FILE].
if [[ $configSource == $SOURCE_FILE ]]; then
{ # This section fetches payload from FILE.
info "Reading file..." true true \
&& payload=$(cat $configFile) \
&& success "File has been read." true
} || {
error "Error on reading file." true
}
elif [[ $configSource == $SOURCE_URL ]]; then
{ # This section fetches payload from URL.
info "Fetching payload..." true true \
&& payload=$(curl -sX GET ${url} | grep ${hook}) \
&& success "Payload has been fetched." true
} || {
error "Error on fetching payload." true
}
fi
{ # This section extracts DNS configurations from payload.
info "Extracting configurations..." true true \
&& addresses=$(grep -oE ${ipRegEx} <<< ${payload}) \
&& success "Configurations have been extracted." true
} || {
error "Error on extracting configurations." true
}
{ # This section writes new configurations to the local DNS configuration file.
write $addresses \
&& success "Local DNS configuration file has been updated." true
} || {
error "Error on editing local DNS configuration file." true
}
info "Enjoy your freedom :)"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment