#!/bin/sh main() { OS=$(detect_os) GOARCH=$(detect_goarch) GOOS=$(detect_goos) NEXTDNS_BIN=$(bin_location) INSTALL_RELEASE=$(get_release) export NEXTDNS_INSTALLER=1 log_info "OS: $OS" log_info "GOARCH: $GOARCH" log_info "GOOS: $GOOS" log_info "NEXTDNS_BIN: $NEXTDNS_BIN" log_info "INSTALL_RELEASE: $INSTALL_RELEASE" if [ -z "$OS" ] || [ -z "$GOARCH" ] || [ -z "$GOOS" ] || [ -z "$NEXTDNS_BIN" ] || [ -z "$INSTALL_RELEASE" ]; then log_error "Cannot detect running environment." exit 1 fi case "$RUN_COMMAND" in install|upgrade|uninstall|configure) "$RUN_COMMAND"; exit ;; esac while true; do CURRENT_RELEASE=$(get_current_release) log_debug "Start install loop with CURRENT_RELEASE=$CURRENT_RELEASE" if [ "$CURRENT_RELEASE" ]; then if ! is_version_current; then log_debug "NextDNS is out of date ($CURRENT_RELEASE != $INSTALL_RELEASE)" menu \ u "Upgrade NextDNS from $CURRENT_RELEASE to $INSTALL_RELEASE" upgrade \ c "Configure NextDNS" configure \ r "Remove NextDNS" uninstall \ e "Exit" exit else log_debug "NextDNS is up to date ($CURRENT_RELEASE)" menu \ c "Configure NextDNS" configure \ r "Remove NextDNS" uninstall \ e "Exit" exit fi else log_debug "NextDNS is not installed" menu \ i "Install NextDNS" install \ e "Exit" exit fi done } install() { if [ "$(get_current_release)" ]; then log_info "Already installed" return fi if type=$(install_type); then log_info "Installing NextDNS..." log_debug "Using $type install type" if "install_$type"; then if [ ! -x "$NEXTDNS_BIN" ]; then log_error "Installation failed: binary not installed in $NEXTDNS_BIN" return 1 fi configure post_install exit 0 fi else return $? fi } upgrade() { if [ "$(get_current_release)" = "$INSTALL_RELEASE" ]; then log_info "Already on the latest version" return fi if type=$(install_type); then log_info "Upgrading NextDNS..." log_debug "Using $type install type" "upgrade_$type" else return $? fi } uninstall() { if type=$(install_type); then log_info "Uninstalling NextDNS..." log_debug "Using $type uninstall type" "uninstall_$type" else return $? fi } precheck() { if [ -e "/data/unifi" ] && [ -f "/run/dnsfilter/dnsfilter" ]; then log_warn "UDM Content Filtering and/or Ad Blocking feature is enabled." log_warn "Please disable it to use NextDNS." log_warn "" log_warn " To disable Content Filtering, go to Settings > Network." log_warn " For each network, set the Content Filtering feature to None." log_warn "" log_warn " To disable Ad Blocking, go to Settings > Application Firewall" log_warn " In the General tab, uncheck the Ad Blocking checkbox." log_warn "" while [ -f "/run/dnsfilter/dnsfilter" ]; do sleep 1 done log_info "Content Filtering feature successfully disabled." fi } configure() { log_debug "Start configure" precheck args="" add_arg() { for value in $2; do log_debug "Add arg -$1=$value" args="$args -$1=$value" done } add_arg_bool_ask() { arg=$1 msg=$2 default=$3 if [ -z "$default" ]; then default=$(get_config_bool "$arg") fi # shellcheck disable=SC2046 add_arg "$arg" $(ask_bool "$msg" "$default") } # Use profile from now on add_arg profile "$(get_profile_id)" doc "Sending your devices name lets you filter analytics and logs by device." add_arg_bool_ask report-client-info 'Report device name?' true case $(guess_host_type) in router) add_arg setup-router true ;; unsure) doc "Accept DNS request from other network hosts." if [ "$(get_config_bool setup-router)" = "true" ]; then router_default=true fi if [ "$(ask_bool 'Setup as a router?' $router_default)" = "true" ]; then add_arg setup-router true fi ;; esac doc "Make NextDNS CLI cache responses. This improves latency and reduces the amount" doc "of queries sent to NextDNS." if [ "$(guess_host_type)" = "router" ]; then doc "Note that enabling this feature will disable dnsmasq for DNS to avoid double" doc "caching." fi if [ "$(get_config cache-size)" != "0" ]; then cache_default=true fi if [ "$(ask_bool 'Enable caching?' $cache_default)" = "true" ]; then add_arg cache-size "10MB" doc "Instant refresh will force low TTL on responses sent to clients so they rely" doc "on CLI DNS cache. This will allow changes on your NextDNS config to be applied" doc "on your LAN hosts without having to wait for their cache to expire." if [ "$(get_config max-ttl)" = "5s" ]; then instant_refresh_default=true fi if [ "$(ask_bool 'Enable instant refresh?' $instant_refresh_default)" = "true" ]; then add_arg max-ttl "5s" fi fi if [ "$(guess_host_type)" != "router" ]; then doc "Changes DNS settings of the host automatically when NextDNS is started." doc "If you say no here, you will have to manually configure DNS to 127.0.0.1." add_arg_bool_ask auto-activate 'Automatically setup local host DNS?' true fi # shellcheck disable=SC2086 asroot "$NEXTDNS_BIN" install $args } post_install() { println println "Congratulations! NextDNS is now installed." println println "To upgrade/uninstall, run this command again and select the appropriate option." println println "You can use the NextDNS command to control the daemon." println "Here are a few important commands to know:" println println "# Start, stop, restart the daemon:" println "nextdns start" println "nextdns stop" println "nextdns restart" println println "# Configure the local host to point to NextDNS or not:" println "nextdns activate" println "nextdns deactivate" println println "# Explore daemon logs:" println "nextdns log" println println "# For more commands, use:" println "nextdns help" println } install_bin() { bin_path=$NEXTDNS_BIN if [ "$1" ]; then bin_path=$1 fi log_debug "Installing $INSTALL_RELEASE binary for $GOOS/$GOARCH to $bin_path" case "$INSTALL_RELEASE" in */*) # Snapshot branch=${INSTALL_RELEASE%/*} hash=${INSTALL_RELEASE#*/} url="https://snapshot.nextdns.io/${branch}/nextdns-${hash}_${GOOS}_${GOARCH}.tar.gz" ;; *) url="https://github.com/nextdns/nextdns/releases/download/v${INSTALL_RELEASE}/nextdns_${INSTALL_RELEASE}_${GOOS}_${GOARCH}.tar.gz" ;; esac log_debug "Downloading $url" asroot mkdir -p "$(dirname "$bin_path")" && curl -sL "$url" | asroot sh -c "tar Ozxf - nextdns > \"$bin_path\"" && asroot chmod 755 "$bin_path" } upgrade_bin() { tmp=$NEXTDNS_BIN.tmp if install_bin "$tmp"; then asroot "$NEXTDNS_BIN" uninstall asroot mv "$tmp" "$NEXTDNS_BIN" asroot "$NEXTDNS_BIN" install fi log_debug "Removing spurious temporary install file" asroot rm -rf "$tmp" } uninstall_bin() { asroot "$NEXTDNS_BIN" uninstall asroot rm -f "$NEXTDNS_BIN" } install_rpm() { asroot curl -Ls https://repo.nextdns.io/nextdns.repo -o /etc/yum.repos.d/nextdns.repo && asroot yum install -y nextdns } upgrade_rpm() { asroot yum update -y nextdns } uninstall_rpm() { asroot yum remove -y nextdns } install_zypper() { if asroot zypper repos | grep -q nextdns >/dev/null; then echo "Repository nextdns already exists. Skipping adding repository..." else asroot zypper ar -f -r https://repo.nextdns.io/nextdns.repo nextdns fi asroot zypper refresh && asroot zypper in -y nextdns } upgrade_zypper() { asroot zypper up nextdns } uninstall_zypper() { asroot zypper remove -y nextdns case $(ask_bool 'Do you want to remove the repository from the repositories list?' true) in true) asroot zypper removerepo nextdns ;; esac } install_deb() { if [ -f /etc/default/ubnt-dpkg-cache ]; then # On UnifiOS 2, make sure the package is persisted over upgrades sed -e '/^DPKG_CACHE_UBNT_PKGS+=" nextdns"/{:a;n;ba;q}' \ -e '$aDPKG_CACHE_UBNT_PKGS+=" nextdns"' \ -i /etc/default/ubnt-dpkg-cache fi install_deb_keyring && asroot sh -c 'echo "deb [signed-by=/etc/apt/keyrings/nextdns.gpg] https://repo.nextdns.io/deb stable main" > /etc/apt/sources.list.d/nextdns.list' && (dpkg --compare-versions $(dpkg-query --showformat='${Version}' --show apt) ge 1.1 || asroot ln -s /etc/apt/keyrings/nextdns.gpg /etc/apt/trusted.gpg.d/.) && (test "$OS" = "debian" && asroot apt-get -y install apt-transport-https || true) && (asroot apt-get update || true) && asroot apt-get install -y nextdns } install_deb_keyring() { # Fallback on curl, some debian based distrib don't have wget while debian # doesn't have curl by default. asroot mkdir -p /etc/apt/keyrings ( asroot wget -qO /etc/apt/keyrings/nextdns.gpg https://repo.nextdns.io/nextdns.gpg || asroot curl -sfL https://repo.nextdns.io/nextdns.gpg -o /etc/apt/keyrings/nextdns.gpg ) && asroot chmod 0644 /etc/apt/keyrings/nextdns.gpg } upgrade_deb() { install_deb_keyring && (asroot apt-get update || true) && asroot apt-get install -y nextdns } uninstall_deb() { asroot apt-get remove -y nextdns } install_apk() { repo=https://repo.nextdns.io/apk asroot wget -O /etc/apk/keys/nextdns.pub https://repo.nextdns.io/nextdns.pub && (grep -v $repo /etc/apk/repositories; echo $repo) | asroot tee /etc/apk/repositories >/dev/null && asroot apk update && asroot apk add nextdns } upgrade_apk() { asroot apk update && asroot apk upgrade nextdns } uninstall_apk() { asroot apk del nextdns } install_arch() { asroot pacman -Sy yay && yay -Sy nextdns } upgrade_arch() { yay -Suy nextdns } uninstall_arch() { asroot pacman -R nextdns } install_merlin_path() { # Add next to Merlin's path mkdir -p /tmp/opt/sbin ln -sf "$NEXTDNS_BIN" /tmp/opt/sbin/nextdns } install_merlin() { if install_bin; then install_merlin_path fi } uninstall_merlin() { uninstall_bin rm -f /tmp/opt/sbin/nextdns } upgrade_merlin() { if upgrade_bin; then install_merlin_path fi } install_openwrt() { opkg update && opkg install nextdns rt=$? if [ $rt -eq 0 ]; then case $(ask_bool 'Install the GUI?' true) in true) opkg install luci-app-nextdns rt=$? ;; esac fi return $rt } upgrade_openwrt() { opkg update && opkg upgrade nextdns } uninstall_openwrt() { opkg remove nextdns } install_ddwrt() { if [ "$(nvram get enable_jffs2)" = "0" ]; then log_error "JFFS support not enabled" log_info "To enabled JFFS:" log_info " 1. On the router web page click on Administration." log_info " 2. Scroll down until you see JFFS2 Support section." log_info " 3. Click Enable JFFS." log_info " 4. Click Save." log_info " 5. Wait couple seconds, then click Apply." log_info " 6. Wait again. Go back to the Enable JFFS section, and enable Clean JFFS." log_info " 7. Do not click Save. Click Apply instead." log_info " 8. Wait till you get the web-GUI back, then disable Clean JFFS again." log_info " 9. Click Save." log_info "10. Relaunch this installer." exit 1 fi mkdir -p /jffs/nextdns && openssl_get https://curl.haxx.se/ca/cacert.pem | http_body > /jffs/nextdns/ca.pem && install_bin } upgrade_ddwrt() { upgrade_bin } uninstall_ddwrt() { uninstall_bin rm -rf /jffs/nextdns } install_brew() { silent_exec brew install nextdns/tap/nextdns } upgrade_brew() { silent_exec brew upgrade nextdns/tap/nextdns asroot "$NEXTDNS_BIN" install } uninstall_brew() { silent_exec brew uninstall nextdns/tap/nextdns } install_freebsd() { # TODO: port install install_bin } upgrade_freebsd() { # TODO: port upgrade upgrade_bin } uninstall_freebsd() { # TODO: port uninstall uninstall_bin } install_pfsense() { # TODO: port install + UI install_bin } upgrade_pfsense() { # TODO: port upgrade upgrade_bin } uninstall_pfsense() { # TODO: port uninstall uninstall_bin } install_opnsense() { # TODO: port install + UI install_bin } upgrade_opnsense() { # TODO: port upgrade upgrade_bin } uninstall_opnsense() { # TODO: port uninstall uninstall_bin } ubios_install_source() { echo "deb [signed-by=/etc/apt/keyrings/nextdns.gpg] https://repo.nextdns.io/deb stable main" > /data/nextdns.list podman exec unifi-os mv /data/nextdns.list /etc/apt/sources.list.d/nextdns.list rm -f /tmp/nextdns.list podman exec unifi-os apt-get install -y gnupg1 curl podman exec unifi-os mkdir -p /etc/apt/keyrings/ podman exec unifi-os curl -sfL https://repo.nextdns.io/nextdns.gpg -o /etc/apt/keyrings/nextdns.gpg podman exec unifi-os apt-get update -o Dir::Etc::sourcelist="sources.list.d/nextdns.list" -o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0" || true } install_ubios() { ubios_install_source podman exec unifi-os apt-get install -y nextdns } upgrade_ubios() { ubios_install_source podman exec unifi-os apt-get install --only-upgrade -y nextdns } uninstall_ubios() { podman exec unifi-os apt-get remove -y nextdns } install_ubios_snapshot() { branch=${INSTALL_RELEASE%/*} hash=${INSTALL_RELEASE#*/} url="https://snapshot.nextdns.io/${branch}/nextdns-${hash}_${GOOS}_${GOARCH}.tar.gz" podman exec unifi-os sh -c "curl -o- $url | tar Ozxf - nextdns > /usr/bin/nextdns; /usr/bin/nextdns install" } upgrade_ubios_snapshot() { /data/nextdns uninstall install_ubios_snapshot } install_type() { if [ "$FORCE_INSTALL_TYPE" ]; then echo "$FORCE_INSTALL_TYPE"; return 0 fi case "$INSTALL_RELEASE" in */*) case $OS in ubios) echo "ubios_snapshot"; return 0 ;; *) # Snapshot mode always use binary install echo "bin"; return 0 ;; esac esac case $OS in centos|fedora|rhel) echo "rpm" ;; opensuse-tumbleweed|opensuse-leap|opensuse) echo "zypper" ;; debian|ubuntu|elementary|raspbian|linuxmint|pop|neon|sparky|vyos|Deepin) echo "deb" ;; alpine) echo "apk" ;; arch|manjaro|steamos) #echo "arch" # TODO: fix AUR install echo "bin" ;; openwrt) # shellcheck disable=SC1091 . /etc/os-release major=$(echo "$VERSION_ID" | cut -d. -f1) case $major in *[!0-9]*) if [ "$VERSION_ID" = "19.07.0-rc1" ]; then # No opkg support before 19.07.0-rc2 echo "bin" else # Likely 'snapshot' build in this case, but still > major version 19 echo "openwrt" fi ;; *) if [ "$major" -lt 19 ]; then # No opkg support before 19.07.0-rc2 echo "bin" else echo "openwrt" fi ;; esac ;; asuswrt-merlin) echo "merlin" ;; edgeos|synology|clear-linux-os|solus|openbsd|netbsd|overthebox) echo "bin" ;; ddwrt) echo "ddwrt" ;; darwin) if [ -x /usr/local/bin/brew ] || [ -x /opt/homebrew/bin/brew ]; then echo "brew" else log_debug "Homebrew not installed, fallback on binary install" echo "bin" fi ;; freebsd) echo "freebsd" ;; pfsense) echo "pfsense" ;; opnsense) echo "opnsense" ;; ubios) echo "ubios" ;; gentoo) echo "bin" ;; void) # TODO: pkg for xbps echo "bin" ;; *) log_error "Unsupported installation for $(detect_os)" return 1 ;; esac } get_config() { "$NEXTDNS_BIN" config | grep -E "^$1 " | cut -d' ' -f 2 } get_config_bool() { val=$(get_config "$1") case $val in true|false) echo "$val" ;; esac echo "$2" } get_profile_id() { log_debug "Get profile ID" if [ "$CONFIG_ID" ]; then # backward compat PROFILE_ID="$CONFIG_ID" fi while [ -z "$PROFILE_ID" ]; do default= prev_id=$(get_config profile) if [ -z "$prev_id" ]; then # backward compat prev_id=$(get_config config) fi if [ "$prev_id" ]; then log_debug "Previous profile ID: $prev_id" default=" (default=$prev_id)" fi print "NextDNS Profile ID%s: " "$default" read -r id if [ -z "$id" ]; then id=$prev_id fi if echo "$id" | grep -qE '^[0-9a-f]{6}$'; then PROFILE_ID=$id break else log_error "Invalid profile ID." println println "ID format is 6 alphanumerical lowercase characters (example: 123abc)." println "Your ID can be found on the Setup tab of https://my.nextdns.io." println fi done echo "$PROFILE_ID" } log_debug() { if [ "$DEBUG" = "1" ]; then printf "\033[30;1mDEBUG: %s\033[0m\n" "$*" >&2 fi } log_info() { printf "INFO: %s\n" "$*" >&2 } log_warn() { printf "\033[33mWARN: %s\033[0m\n" "$*" >&2 } log_error() { printf "\033[31mERROR: %s\033[0m\n" "$*" >&2 } print() { format=$1 if [ $# -gt 0 ]; then shift fi # shellcheck disable=SC2059 printf "$format" "$@" >&2 } println() { format=$1 if [ $# -gt 0 ]; then shift fi # shellcheck disable=SC2059 printf "$format\n" "$@" >&2 } doc() { # shellcheck disable=SC2059 printf "\033[30;1m%s\033[0m\n" "$*" >&2 } menu() { while true; do n=0 default= for item in "$@"; do case $((n%3)) in 0) key=$item if [ -z "$default" ]; then default=$key fi ;; 1) echo "$key) $item" ;; esac n=$((n+1)) done print "Choice (default=%s): " "$default" read -r choice if [ -z "$choice" ]; then choice=$default fi n=0 for item in "$@"; do case $((n%3)) in 0) key=$item ;; 2) if [ "$key" = "$choice" ]; then if ! "$item"; then log_error "$item: exit $?" fi break 2 fi ;; esac n=$((n+1)) done echo "Invalid choice" done } ask_bool() { msg=$1 default=$2 case $default in true) msg="$msg [Y|n]: " ;; false) msg="$msg [y|N]: " ;; *) msg="$msg (y/n): " esac while true; do print "%s" "$msg" read -r answer if [ -z "$answer" ]; then answer=$default fi case $answer in y|Y|yes|YES|true) echo "true" return 0 ;; n|N|no|NO|false) echo "false" return 0 ;; *) echo "Invalid input, use yes or no" ;; esac done } detect_endiannes() { if ! hexdump /dev/null 2>/dev/null; then # Some firmwares do not contain hexdump, for those, try to detect endianness # differently. case $(cat /proc/cpuinfo) in *BCM5300*) # RT-AC66U does not support Merlin version over 380.70 which # lacks hexdump command. echo "le" ;; *) log_error "Cannot determine endianness" return 1 ;; esac return 0 fi case $(hexdump -s 5 -n 1 -e '"%x"' /bin/sh | head -c1) in 1) echo "le" ;; 2) echo "" ;; esac } detect_goarch() { if [ "$FORCE_GOARCH" ]; then echo "$FORCE_GOARCH"; return 0 fi case $(uname -m) in x86_64|amd64) echo "amd64" ;; i386|i686) echo "386" ;; arm) # FreeBSD does not include arm version case "$(sysctl -b hw.model 2>/dev/null)" in *A9*) echo "armv7" ;; *) # Unknown version, fallback to the lowest echo "armv5" ;; esac ;; armv5*) echo "armv5" ;; armv6*|armv7*) if grep -q vfp /proc/cpuinfo 2>/dev/null; then echo "armv$(uname -m | sed -e 's/[[:alpha:]]//g')" else # Soft floating point echo "armv5" fi ;; aarch64) case "$(uname -o 2>/dev/null)" in ASUSWRT-Merlin*) # XXX when using arm64 build on ASUS AC66U and ACG86U, we get Go error: # "out of memory allocating heap arena metadata". echo "armv7" ;; *) echo "arm64" ;; esac ;; armv8*|arm64) echo "arm64" ;; mips*) # TODO: detect hardfloat echo "$(uname -m)$(detect_endiannes)_softfloat" ;; *) log_error "Unsupported GOARCH: $(uname -m)" return 1 ;; esac } detect_goos() { if [ "$FORCE_GOOS" ]; then echo "$FORCE_GOOS"; return 0 fi case $(uname -s) in Linux) echo "linux" ;; Darwin) echo "darwin" ;; FreeBSD) echo "freebsd" ;; NetBSD) echo "netbsd" ;; OpenBSD) echo "openbsd" ;; *) log_error "Unsupported GOOS: $(uname -s)" return 1 esac } detect_os() { if [ "$FORCE_OS" ]; then echo "$FORCE_OS"; return 0 fi case $(uname -s) in Linux) case $(uname -o) in GNU/Linux|Linux) if grep -q -e '^EdgeRouter' -e '^UniFiSecurityGateway' /etc/version 2> /dev/null; then echo "edgeos"; return 0 fi if uname -u 2>/dev/null | grep -q '^synology'; then echo "synology"; return 0 fi # shellcheck disable=SC1091 dist=$(. /etc/os-release; echo "$ID") case $dist in ubios) if [ -z "$(command -v podman)" ]; then log_error "This version of UnifiOS is not supported. Make sure you run version 1.7.0 or above." return 1 fi echo "$dist"; return 0 ;; debian|ubuntu|elementary|raspbian|centos|fedora|rhel|arch|manjaro|openwrt|clear-linux-os|linuxmint|opensuse-tumbleweed|opensuse-leap|opensuse|solus|pop|neon|overthebox|sparky|vyos|void|alpine|Deepin|gentoo|steamos) echo "$dist"; return 0 ;; esac # shellcheck disable=SC1091 for dist in $(. /etc/os-release; echo "$ID_LIKE"); do case $dist in debian|ubuntu|rhel|fedora|openwrt|arch) log_debug "Using ID_LIKE" echo "$dist"; return 0 ;; esac done ;; ASUSWRT-Merlin*) echo "asuswrt-merlin"; return 0 ;; DD-WRT) echo "ddwrt"; return 0 esac ;; Darwin) echo "darwin"; return 0 ;; FreeBSD) if [ -f /etc/platform ]; then case $(cat /etc/platform) in pfSense) echo "pfsense"; return 0 ;; esac fi if [ -x /usr/local/sbin/opnsense-version ]; then case $(/usr/local/sbin/opnsense-version -N) in OPNsense) echo "opnsense"; return 0 ;; esac fi echo "freebsd"; return 0 ;; NetBSD) echo "netbsd"; return 0 ;; OpenBSD) echo "openbsd"; return 0 ;; *) esac log_error "Unsupported OS: $(uname -o) $(grep ID "/etc/os-release" 2>/dev/null | xargs)" return 1 } guess_host_type() { if [ -d /data/unifi ]; then # Special case when installer is run from inside the ubios podman echo "router"; return 0 fi case $OS in pfsense|opnsense|openwrt|asuswrt-merlin|edgeos|ddwrt|synology|overthebox|ubios) echo "router" ;; darwin|steamos) echo "workstation" ;; *) echo "unsure" ;; esac } asroot() { # Some platform (Merlin) do not have the "id" command and $USER report a non root username with uid 0. if [ "$(grep '^Uid:' /proc/$$/status 2>/dev/null|cut -f2)" = "0" ] || [ "$USER" = "root" ] || [ "$(id -u 2>/dev/null)" = "0" ]; then "$@" elif [ "$(command -v sudo 2>/dev/null)" ]; then sudo "$@" else echo "Root required" su -m root -c "$*" fi } silent_exec() { if [ "$DEBUG" = 1 ]; then "$@" else if ! out=$("$@" 2>&1); then rt=$? println "\033[30;1m%s\033[0m" "$out" return $rt fi fi } bin_location() { case $OS in centos|fedora|rhel|debian|ubuntu|elementary|raspbian|arch|manjaro|clear-linux-os|linuxmint|opensuse-tumbleweed|opensuse-leap|opensuse|solus|pop|neon|sparky|vyos|void|alpine|Deepin|gentoo) echo "/usr/bin/nextdns" ;; openwrt|overthebox) echo "/usr/sbin/nextdns" ;; synology) echo "/usr/local/bin/nextdns" ;; darwin) echo "$(brew --prefix 2>/dev/null || echo /usr/local)/bin/nextdns" ;; asuswrt-merlin|ddwrt) echo "/jffs/nextdns/nextdns" ;; freebsd|pfsense|opnsense|netbsd|openbsd) echo "/usr/local/sbin/nextdns" ;; edgeos) echo "/config/nextdns/nextdns" ;; ubios) echo "/data/nextdns" ;; steamos) echo "$HOME/.local/bin/nextdns" ;; *) log_error "Unknown bin location for $OS" ;; esac } is_version_current() { case "$INSTALL_RELEASE" in */*) # Snapshot hash=${INSTALL_RELEASE#*/} test "0.0.0-$hash" = "$CURRENT_RELEASE" ;; *) test "$INSTALL_RELEASE" = "$CURRENT_RELEASE" ;; esac } get_current_release() { if [ -x "$NEXTDNS_BIN" ]; then $NEXTDNS_BIN version|cut -d' ' -f 3 fi } get_release() { if [ "$NEXTDNS_VERSION" ]; then echo "$NEXTDNS_VERSION" else for cmd in curl wget openssl true; do # command is the "right" way but may be compiled out of busybox shell ! command -v $cmd > /dev/null 2>&1 || break ! which $cmd > /dev/null 2>&1 || break done case "$cmd" in curl) cmd="curl -A curl -s" ;; wget) cmd="wget -qO- -U curl" ;; openssl) cmd="openssl_get" ;; *) log_error "Cannot retrieve latest version" return ;; esac v=$($cmd "https://api.github.com/repos/nextdns/nextdns/releases/latest" | \ grep '"tag_name":' | esed 's/.*"([^"]+)".*/\1/' | sed -e 's/^v//') if [ -z "$v" ]; then log_error "Cannot get latest version: $out" fi echo "$v" fi } esed() { if (echo | sed -E '' >/dev/null 2>&1); then sed -E "$@" else sed -r "$@" fi } http_redirect() { while read -r header; do case $header in Location:*) echo "${header#Location: }" return ;; esac if [ "$header" = "" ]; then break fi done cat > /dev/null return 1 } http_body() { sed -n '/^\r/,$p' | sed 1d } openssl_get() { host=${1#https://*} # https://dom.com/path -> dom.com/path path=/${host#*/} # dom.com/path -> /path host=${host%$path} # dom.com/path -> dom.com printf "GET %s HTTP/1.0\nHost: %s\nUser-Agent: curl\n\n" "$path" "$host" | openssl s_client -quiet -connect "$host:443" 2>/dev/null } umask 0022 main