dnscrypt-proxy on Android with Universal-init.d

Device root access is required.

This method is tried and tested with Android 6 - 9 (LineageOS 13 - 16); mostly on a Samsung Galaxy S5 (klte).

Summary

This guide describes how to configure an Android device so that all DNS requests (on both cellular and WiFi) are encrypted between the device and the DNS server using the DNSCrypt protocol and dnscrypt-proxy client. Universal-init.d is used so it will run automatically when the device boots. dnscrypt-proxy is not installed as an Adroid zip package; it is copied directly to the device.

Requirements

To use this guide, you must have a working Android system with root (su) access. A custom ROM is not required; however, root is.

Prerequisites

To use this guide, you need a computer with adb installed and developer mode enabled on the device. USB debugging permission must be granted to the computer.

Why do this?

Installation

Install Universal-init.d

Universal-init.d is an Android application that emulates the behavior of the init.d kernel mechanism. It allows us to run custom scripts at boot, which will be used to start dnscrypt-proxy automatically and add iptables rules to redirect DNS requests through dnscrypt-proxy.

If you can't or don't want to use Universal-init.d, that is okay as long as you can get the dnscrypt-proxy script to run using some other method (either manually or with some other software to run it on every boot).

Install Universal-init.d from the Play store or other source (apk).

Turn Init.d support on, test and reboot.

Re-open Universal-init.d and make sure that it says "Your Kernet has init.d Support".

Install dnscrypt-proxy binary

Download the dnscrypt-proxy-android release for your architecture and extract the files to a temporary location.

The dnscrypt-proxy binary will be placed in /system/xbin.

Connect your device to the computer and enable USB file transfer.

Copy the dnscrypt files to a temporary location on the device's external memory card or internal shared stored.

Unmount storage, turn off file transfer and set back to charging only.

Connect to a shell on your device using adb. Once you have a shell, run su to switch to root.

# from your computer
adb shell

# once on the device
su

Now re-mount the system partition to be writable:

mount -o remount,rw /system

Copy dnscrypt-proxy to /system/xbin (replace /sdcard/dnscrypt-proxy with the location where the files were copied in the above step):

cp /sdcard/dnscrypt-proxy /system/xbin/.

Install dnscrypt-proxy configuration files

The config files will be kept in /data/local/dnscrypt-proxy

From the root, shell, create the directory:

mkdir /data/local/dnscrypt-proxy

Copy dnscrypt-proxy.toml to config path:

cp /sdcard/dnscrypt-proxy.toml /data/local/dnscrypt-proxy/.
chmod +x /data/local/dnscrypt-proxy

Enable dnscrypt-proxy at boot

The following init script runs dnscrypt-proxy and redirects outbound DNS requests to go through dnscrypt-proxy.

Copy this init script to /etc/init.d/99dnscrypt, or run vim /etc/init.d/99dnscrypt from the adb root shell, paste the contents, and save.

#!/system/bin/sh

DNSCRYPT_PROXY="/system/xbin/dnscrypt-proxy"
CONFFILE="/data/local/dnscrypt-proxy/dnscrypt-proxy.toml"
PIDFILE="/data/local/tmp/dnscrypt-proxy.pid"
APPEND="-A"
WAITFORDAEMON=10
STOP=0
nohup="nohup"

_log() {
  log -p i -t dnscrypt-init -- $1
  echo "$1" 1>&2
}

_wait_for_pid() {
  cnt=0

  while true ; do
    sleep 1

    if test -s "$PIDFILE" ; then
      pid=$(cat "$PIDFILE")

      if kill -0 $pid 2>/dev/null ; then
        return 0
      fi
    fi

    [ "`expr $cnt % 3`" != 2 ] || _log "."

    cnt=`expr $cnt + 1`
    if [ $cnt -gt $WAITFORDAEMON ] ; then
      break
    fi
  done

  return 1
}

## Begin

if [ "$1" = "--debug" ] ; then
  nohup=""
  _log "Attempting to run dnscrypt-proxy in foreground"
fi

if [ "$1" = "--stop" ] ; then
  STOP=1
  APPEND="-D"
  _log "Stopping dnscrypt-proxy"
else
  _log "Starting dnscrypt-proxy"
fi

if [ -s "$PIDFILE" ] ; then
  # if dnscrypt-proxy is running, kill
  kill $(cat "$PIDFILE")
  rm -f "$PIDFILE"
fi

if [ $STOP -eq 0 ] ; then
  cmd="""$DNSCRYPT_PROXY"" -config ""$CONFFILE"" -pidfile=""$PIDFILE"""

  if [ "$nohup" != "" ] ; then
    $nohup $cmd >/dev/null 2>&1 &
    _wait_for_pid
    if [ $? -ne 0 ] ; then
      # delete iptable rule
      APPEND="-D"
      _log "failed to start dnscrypt-proxy"
      _log "Try running '$0 --debug' or enable logging and check the logs for errors"
      exit 1
    fi
  else
    $cmd
  fi
fi

iptables -t nat $APPEND OUTPUT -p tcp --dport 53 -j DNAT --to-destination 127.0.0.1:533
iptables -t nat $APPEND OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:533

Make the script executable:

chmod +x /etc/init.d/99dnscrypt

With Universal-init.d enabled, this script will execute at boot and start dnscrypt-proxy and redirect DNS requests through it.

From a shell, running this file directly will also start or restart the process.

Testing & Troubleshooting

Starting & Restarting

To start or restart dnscrypt-proxy, run the following command from a root shell:

/etc/init.d/99dnscrypt

Note: This can be run from adb or from a terminal emulator on the device (as long as you can run su from the terminal).

From a shell on your device, you can see if it is running at any time:

# ps -A | grep dnscrypt
root          8849     1  810176   8644 futex_wait_queue_me aa97632c S dnscrypt-proxy
Testing DNS resolution
/system/xbin/dnscrypt-proxy -resolve example.com
Debugging

If dnscrypt-proxy fails to run or cannot resolve anything, run it in debug mode (which runs dnscrypt-proxy in the foreground):

/etc/init.d/99dnscrypt --debug

Output from dnscrypt-proxy will be written to the console so you can identify why it may be failing to work.

Stopping

If there are problems with dnscrypt or you need to fall back to regular system DNS, use:

/etc/init.d/99dnscrypt --stop

Document History

Version 1.0 (2019/04/05): Initial version