diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c3d6ae2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +pw +nextdns.conf diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..5c91987 --- /dev/null +++ b/install.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env sh + +export NEXTDNS_PASSWORD=$(cat pw) + +sshpass -p $NEXTDNS_PASSWORD scp nextdns_install.sh root@192.168.1.1:/root/nextdns_install.sh +sshpass -p $NEXTDNS_PASSWORD scp nextdns.conf root@192.168.1.1:/data/nextdns.conf +sshpass -p $NEXTDNS_PASSWORD ssh root@192.168.1.1 "chmod +x /root/nextdns_install.sh && /root/nextdns_install.sh install" diff --git a/nextdns_install.sh b/nextdns_install.sh new file mode 100755 index 0000000..82bc0dd --- /dev/null +++ b/nextdns_install.sh @@ -0,0 +1,1150 @@ +#!/bin/sh +export RUN_COMMAND=$1 + +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