diff --git a/common/service.sh b/common/service.sh index 3084414..8526f5c 100644 --- a/common/service.sh +++ b/common/service.sh @@ -12,6 +12,7 @@ if [ ! -f /data/v2ray/manual ] ; then $MODDIR/scripts/v2ray.service start &> /data/v2ray/run/service.log && \ if [ -f /data/v2ray/appid.list ] || [ -f /data/v2ray/softap.list ] ; then $MODDIR/scripts/v2ray.tproxy enable &>> /data/v2ray/run/service.log + [ -f "$MODDIR/scripts/v2ray-dns.keeper" ] && $MODDIR/scripts/v2ray-dns.service start &>> /data/v2ray/run/service.log & fi fi diff --git a/install.sh b/install.sh index ccb49e9..f922ca2 100644 --- a/install.sh +++ b/install.sh @@ -142,6 +142,7 @@ on_install() { mkdir -p $MODPATH/system/etc unzip -j -o "$ZIPFILE" 'v2ray/scripts/*' -d $MODPATH/scripts >&2 unzip -j -o "$ZIPFILE" "v2ray/bin/$ARCH/*" -d $MODPATH/system/bin >&2 + [ -f $MODPATH/system/bin/v2ray-dns.keeper ] && mv $MODPATH/system/bin/v2ray-dns.keeper $MODPATH/scripts >&2 # copy v2ray data and config ui_print "- Copy V2Ray config and data files" @@ -149,12 +150,14 @@ on_install() { mkdir -p /data/v2ray/run [ -f /data/v2ray/softap.list ] || \ echo "softap0" > /data/v2ray/softap.list - [ -f /data/v2ray/config.json ] || \ - unzip -j -o "$ZIPFILE" "v2ray/etc/config.json" -d /data/v2ray >&2 [ -f /data/v2ray/resolv.conf ] || \ unzip -j -o "$ZIPFILE" "v2ray/etc/resolv.conf" -d /data/v2ray >&2 unzip -j -o "$ZIPFILE" "v2ray/etc/geosite.dat" -d /data/v2ray >&2 unzip -j -o "$ZIPFILE" "v2ray/etc/geoip.dat" -d /data/v2ray >&2 + unzip -j -o "$ZIPFILE" "v2ray/etc/config.json" -d /data/v2ray/run >&2 + mv /data/v2ray/run/config.json /data/v2ray/config.json.template + [ -f /data/v2ray/config.json ] || \ + cp /data/v2ray/config.json.template /data/v2ray/config.json ln -s /data/v2ray/resolv.conf $MODPATH/system/etc/resolv.conf } @@ -169,6 +172,9 @@ set_permissions() { set_perm $MODPATH/scripts/v2ray.inotify 0 0 0755 set_perm $MODPATH/scripts/v2ray.service 0 0 0755 set_perm $MODPATH/scripts/v2ray.tproxy 0 0 0755 + set_perm $MODPATH/scripts/v2ray-dns.handle 0 0 0755 + set_perm $MODPATH/scripts/v2ray-dns.keeper 0 0 0755 + set_perm $MODPATH/scripts/v2ray-dns.service 0 0 0755 set_perm $MODPATH/system/bin/v2ray ${inet_uid} ${inet_uid} 0755 set_perm $MODPATH/system/bin/v2ctl ${inet_uid} ${inet_uid} 0755 set_perm /data/v2ray ${inet_uid} ${inet_uid} 0755 diff --git a/module.prop b/module.prop index ebecfd0..ac908e9 100644 --- a/module.prop +++ b/module.prop @@ -1,6 +1,6 @@ id=v2ray name=V2ray for Android version=v4.19.1 -versionCode=20190622 +versionCode=20190707 author=chendefine description=V2ray core with service scripts for Android diff --git a/v2ray/etc/config.json b/v2ray/etc/config.json index 6f81555..0bf8721 100644 --- a/v2ray/etc/config.json +++ b/v2ray/etc/config.json @@ -15,6 +15,22 @@ }, // List of inbound proxy configurations. "inbounds": [{ + // Just listen for DNS proxy. + "port": 65534, + + // Tag of the inbound for DNS proxy routing. + "tag": "dns-in", + + // DNS proxy protocol must be dokodemo-door. + "protocol": "dokodemo-door", + + // Setting of DNS proxy. + "settings": { + "port": 53, + "address": "1.1.1.1", + "network": "tcp,udp" + } + },{ // Port to listen on. You may need root access if the value is less than 1024. "port": 65535, @@ -61,6 +77,12 @@ // Tag of the outbound. May be used for routing. "tag": "direct" + },{ + // DNS Proxy Outbond + "protocol": "dns", + + // Tag of the outbound for DNS proxy routing. + "tag": "dns-out" },{ "protocol": "blackhole", "settings": {}, @@ -75,6 +97,12 @@ "routing": { "domainStrategy": "IPOnDemand", "rules":[ + { + // Proxy DNS request + "type": "field", + "inboundTag": ["dns-in"], + "outboundTag": "dns-out" + }, { // Bypass private IPs. "type": "field", @@ -117,6 +145,8 @@ }, "servers": [ "1.1.1.1", + "8.8.8.8", + "9.9.9.9", { "address": "114.114.114.114", "port": 53, @@ -125,7 +155,6 @@ "geosite:cn" ] }, - "8.8.8.8", "localhost" ] }, diff --git a/v2ray/scripts/v2ray-dns.handle b/v2ray/scripts/v2ray-dns.handle new file mode 100644 index 0000000..28f47ce --- /dev/null +++ b/v2ray/scripts/v2ray-dns.handle @@ -0,0 +1,196 @@ +#!/system/bin/sh + +host_ip="" +inet_uid="3003" +proxy_port="65534" +iptables_wait="iptables" +proxy_for_app=false +appid_file="/data/v2ray/appid.list" +work_path="`dirname $0`" +find_outbound="${work_path}/v2ray-dns.keeper -o" + +suit_iptables_version() { + iptables_version=`iptables -V | grep -o "v1\.[0-9]"` + if [ "${iptables_version}" = "v1.4" ] ; then + ## fix options for lower version iptables + export ANDROID_DATA=/data + export ANDROID_ROOT=/system + iptables_wait="iptables -w" + elif [ "${iptables_version}" = "v1.6" ] || [ "${iptables_version}" = "v1.8" ] ; then + iptables_wait="iptables -w 100" + else + iptables_wait="echo iptables" + fi +} + +find_netstat_path() { + [ -f /system/bin/netstat ] && alias netstat="/system/bin/netstat" && return 0 + [ -f /system/xbin/netstat ] && alias netstat="/system/xbin/netstat" && return 0 + return 1 +} + +iptables_chain_exist() { + local chain_list="$1" + local target_chain="$2" + if `echo "${chain_list}" | grep -q ":${target_chain} "` ; then + return 0 + fi + return 1 +} + +probe_v2ray_listen() { + find_netstat_path || return + v2ray_listen=`netstat -unlp | grep v2ray` + if eval "echo \"${v2ray_listen}\" | grep -q :::${proxy_port}" || eval "echo \"${v2ray_listen}\" | grep -q 0.0.0.0:${proxy_port}" ; then + return + else + echo "[Error]: V2Ray service is not listening on port ${proxy_port} ." + exit 1 + fi +} + +probe_v2ray_target() { + ## add eof to appid and softap file + echo "" >> "${appid_file}" + ## trim empty line in appid and softap file + sed -i '/^$/d' "${appid_file}" + ## probe proxy app + if [ -f ${appid_file} ] ; then + ## check appid_file is white-list or black-list + if head -1 "${appid_file}" | grep -q 'bypass' ; then + app_proxy_mode="skip" + else + app_proxy_mode="pick" + fi + ## filter appid number + while read appid_line ; do + appid_text=(`echo ${appid_line}`) + for appid_word in ${appid_text[*]} ; do + if echo "${appid_word}" | grep -q '#' ; then + break + elif [ "${appid_word}" -ge 0 ] 2>/dev/null ; then + appid_list=(${appid_list[*]} ${appid_word}) + fi + done + done < ${appid_file} + fi + ## check proxy app or not + if ( [ "${app_proxy_mode}" = "skip" ] || ( [ "${app_proxy_mode}" = "pick" ] && [ ${#appid_list[@]} -gt 0 ] ) ) ; then + proxy_for_app=true + fi + ## check enable proxy iptables or not + if ! ${proxy_for_app} ; then + echo "[Error]: V2Ray service is not proxy for APP." + exit 1 + fi +} + +probe_uid_app_name() { + app_handle="$2" + if [ "$1" == "0" ] ; then + app_name="root" + else + app_name=`grep " $1 " /data/system/packages.list | cut -d ' ' -f 1` + app_name=`echo ${app_name} | sed 's/ / \& /g'` + fi + if [ "${app_name}" != "" ] ; then + echo "[Info]: ${app_handle} ${app_name} APP's DNS request." + else + echo "[Warning]: APP with uid=$1 is not found." + return 1 + fi +} + +proxy_app_dns_iptables() { + ## create iptables proxy chains for app tcp + ${iptables_wait} -t nat -N APP_DNS_PROXY + ## bypass v2ray program + ${iptables_wait} -t nat -A APP_DNS_PROXY -m owner --uid-owner ${inet_uid} -j RETURN + ## white-list mode + if [ "${app_proxy_mode}" = "pick" ] ; then + ## proxy all apps network + if [ "${appid_list[*]}" = "0" ] ; then + echo "[Info]: Proxy all APP's DNS request." + ${iptables_wait} -t nat -A APP_DNS_PROXY -m owner ! --uid-owner ${inet_uid} -j V2RAY_APP_DNS + ## proxy assign app + else + for appid in ${appid_list[@]}; do + probe_uid_app_name ${appid} "Proxy" && \ + ${iptables_wait} -t nat -A APP_DNS_PROXY -m owner --uid-owner ${appid} -j V2RAY_APP_DNS + done + fi + ## black-list mode + elif [ "${app_proxy_mode}" = "skip" ] ; then + for appid in ${appid_list[@]}; do + probe_uid_app_name ${appid} "Ignore" && \ + ${iptables_wait} -t nat -A APP_DNS_PROXY -m owner --uid-owner ${appid} -j RETURN + done + echo "[Info]: Proxy all remaining APP's DNS request." + ${iptables_wait} -t nat -A APP_DNS_PROXY -m owner ! --uid-owner ${inet_uid} -j V2RAY_APP_DNS + fi + ## apply proxy rules to iptables + ${iptables_wait} -t nat -A OUTPUT -p udp --dport 53 -j APP_DNS_PROXY +} + +create_proxy_iptables() { + while [ "${host_ip}" == "" ] || [ "${host_ip}" == "0.0.0.0" ] || [ "${host_ip}" == "127.0.0.1" ] ; do + host_ip=`${find_outbound}` + sleep 2 + done + local iptables_chains=`iptables-save -t nat | cut -d ' ' -f 1 | tr "\n" " " | grep -o ":[0-9A-Z_]* "` + if ! iptables_chain_exist "${iptables_chains}" "V2RAY_APP_DNS" ; then + ## create basic iptables proxy chains + echo "[Info]: Create DNS proxy chains to ${host_ip}:${proxy_port}" + ${iptables_wait} -t nat -N V2RAY_APP_DNS + else + ## flush basic iptables proxy chains + echo "[Info]: Rebuild DNS proxy chains to ${host_ip}:${proxy_port}" + ${iptables_wait} -t nat -F V2RAY_APP_DNS + fi + ## build basic iptables proxy chains + ${iptables_wait} -t nat -A V2RAY_APP_DNS -p udp -j DNAT --to-destination ${host_ip}:${proxy_port} + + if ! iptables_chain_exist "${iptables_chains}" "APP_DNS_PROXY" && ${proxy_for_app} ; then + ## proxy app network + proxy_app_dns_iptables + fi +} + +flush_endpoint_iptables() { + ${iptables_wait} -t nat -F V2RAY_APP_DNS 2>/dev/null +} + +flush_nat_iptables() { + echo "[Info]: Clean nat proxy iptables rules." + local iptables_chains=`iptables-save -t nat | cut -d ' ' -f 1 | tr "\n" " " | grep -o ":[0-9A-Z_]* "` + ${iptables_wait} -t nat -D OUTPUT -p udp --dport 53 -j APP_DNS_PROXY 2>/dev/null + if iptables_chain_exist "${iptables_chains}" "APP_DNS_PROXY" ; then + ${iptables_wait} -t nat -F APP_DNS_PROXY + ${iptables_wait} -t nat -X APP_DNS_PROXY + fi + if iptables_chain_exist "${iptables_chains}" "V2RAY_APP_DNS" ; then + ${iptables_wait} -t nat -F V2RAY_APP_DNS + ${iptables_wait} -t nat -X V2RAY_APP_DNS + fi + unset iptables_chains +} + +disable_proxy() { + flush_nat_iptables +} + +case "$1" in + enable) + flush_endpoint_iptables + probe_v2ray_listen + probe_v2ray_target + sleep 2 + create_proxy_iptables + ;; + disable) + disable_proxy + ;; + *) + echo "usage: $0 {enable|disable}" + ;; +esac diff --git a/v2ray/scripts/v2ray-dns.service b/v2ray/scripts/v2ray-dns.service new file mode 100644 index 0000000..1326e64 --- /dev/null +++ b/v2ray/scripts/v2ray-dns.service @@ -0,0 +1,91 @@ +#!/system/bin/sh + +proxy_port="65534" +work_path="`dirname $0`" +bin_name="v2ray-dns.keeper" +bin_file="${work_path}/${bin_name}" +run_path="/data/v2ray/run" +pid_file="${run_path}/dns-keeper.pid" +log_file="${run_path}/dns-keeper.log" +handle_script="${work_path}/v2ray-dns.handle" + +find_netstat_path() { + [ -f /system/bin/netstat ] && alias netstat="/system/bin/netstat" && return 0 + [ -f /system/xbin/netstat ] && alias netstat="/system/xbin/netstat" && return 0 + return 1 +} + +probe_keeper_alive() { + [ -f ${pid_file} ] && cmd_file="/proc/`cat ${pid_file}`/cmdline" || return 1 + [ -f ${cmd_file} ] && grep -q "v2ray-dns.keeper" ${cmd_file} && return 0 || return 1 +} + +probe_v2ray_listen() { + find_netstat_path || return + v2ray_listen=`netstat -unlp | grep v2ray` + if eval "echo \"${v2ray_listen}\" | grep -q :::${proxy_port}" || eval "echo \"${v2ray_listen}\" | grep -q 0.0.0.0:${proxy_port}" ; then + return 0 + else + return 1 + fi +} + +display_keeper_pid() { + if probe_keeper_alive ; then + echo "[Info]: ${bin_name} service is running. ( PID: `cat ${pid_file}` )" + return 0 + else + echo "[Info]: ${bin_name} service is stopped." + return 1 + fi +} + +start_service() { + if probe_keeper_alive ; then + echo "[Info]: ${bin_name} service is running. ( PID: `cat ${pid_file}` )" + return 0 + elif probe_v2ray_listen ; then + echo "[Info]: Starting ${bin_name} service." + mkdir -p ${run_path} + nohup ${bin_file} -d "${handle_script} enable" &>${log_file} & + echo -n $! > ${pid_file} + if probe_keeper_alive ; then + echo "[Info]: ${bin_name} service is running. ( PID: `cat ${pid_file}` )" + return 0 + else + echo "[Error]: Start ${bin_name} service Failed." + rm -f ${pid_file} + return 1 + fi + else + echo "[Error]: V2Ray service is not listening on port ${proxy_port} for DNS proxy." + exit 1 + return 2 + fi +} + +stop_service() { + if display_keeper_pid ; then + echo "[Info]: Stopping ${bin_name} service." + kill `cat ${pid_file}` + sleep 1 + display_keeper_pid + fi + ${handle_script} disable + rm -f ${pid_file} +} + +case "$1" in + start) + start_service + ;; + stop) + stop_service + ;; + status) + display_keeper_pid + ;; + *) + echo "$0: usage: $0 {start|stop|status}" + ;; +esac diff --git a/v2ray/scripts/v2ray.inotify b/v2ray/scripts/v2ray.inotify index 6cc99a2..844ffef 100644 --- a/v2ray/scripts/v2ray.inotify +++ b/v2ray/scripts/v2ray.inotify @@ -4,6 +4,8 @@ inotify=`realpath $0` scripts_dir=`dirname ${inotify}` service="${scripts_dir}/v2ray.service" tproxy="${scripts_dir}/v2ray.tproxy" +dns_proxy_keeper="${scripts_dir}/v2ray-dns.keeper" +dns_proxy_service="${scripts_dir}/v2ray-dns.service" events=$1 monitor_dir=$2 @@ -13,11 +15,13 @@ start_v2ray() { ${service} start && \ if [ -f /data/v2ray/appid.list ] || [ -f /data/v2ray/softap.list ] ; then ${tproxy} enable + [ -f "${dns_proxy_keeper}" ] && ${dns_proxy_service} start fi } stop_v2ray() { ${tproxy} disable + [ -f "${dns_proxy_keeper}" ] && ${dns_proxy_service} stop ${service} stop } diff --git a/v2ray/scripts/v2ray.tproxy b/v2ray/scripts/v2ray.tproxy index 46221bb..7013c8a 100644 --- a/v2ray/scripts/v2ray.tproxy +++ b/v2ray/scripts/v2ray.tproxy @@ -101,8 +101,12 @@ probe_v2ray_target() { probe_uid_app_name() { app_handle="$2" - app_name=`grep " $1 " /data/system/packages.list | cut -d ' ' -f 1` - app_name=`echo ${app_name} | sed 's/ / \& /g'` + if [ "$1" == "0" ] ; then + app_name="root" + else + app_name=`grep " $1 " /data/system/packages.list | cut -d ' ' -f 1` + app_name=`echo ${app_name} | sed 's/ / \& /g'` + fi if [ "${app_name}" != "" ] ; then echo "[Info]: ${app_handle} ${app_name} APP's network." else