diff --git a/src/main/assets/arm/tun2socks b/src/main/assets/arm/tun2socks index 1e371a2e..f675a821 100755 Binary files a/src/main/assets/arm/tun2socks and b/src/main/assets/arm/tun2socks differ diff --git a/src/main/assets/pdnsd.conf b/src/main/assets/pdnsd.conf index 54cb208a..79ca809d 100644 --- a/src/main/assets/pdnsd.conf +++ b/src/main/assets/pdnsd.conf @@ -1,8 +1,8 @@ global { perm_cache = 2048; cache_dir = "/data/data/com.github.shadowsocks"; - server_ip = 127.0.0.1; - server_port = 8153; + server_ip = 0.0.0.0; + server_port = 8153; query_method = tcp_only; run_ipv4 = on; min_ttl = 15m; diff --git a/src/main/assets/x86/tun2socks b/src/main/assets/x86/tun2socks index 1fab6e3e..130cba9d 100755 Binary files a/src/main/assets/x86/tun2socks and b/src/main/assets/x86/tun2socks differ diff --git a/src/main/jni/Android.mk b/src/main/jni/Android.mk index b02bd403..7912f2c2 100755 --- a/src/main/jni/Android.mk +++ b/src/main/jni/Android.mk @@ -184,7 +184,7 @@ LOCAL_CFLAGS := -std=gnu99 LOCAL_CFLAGS += -DBADVPN_THREADWORK_USE_PTHREAD -DBADVPN_LINUX -DBADVPN_BREACTOR_BADVPN -D_GNU_SOURCE LOCAL_CFLAGS += -DBADVPN_USE_SELFPIPE -DBADVPN_USE_EPOLL LOCAL_CFLAGS += -DBADVPN_LITTLE_ENDIAN -DBADVPN_THREAD_SAFE -LOCAL_CFLAGS += -DANDROID +LOCAL_CFLAGS += -DNDEBUG -DANDROID LOCAL_C_INCLUDES:= \ $(LOCAL_PATH)/badvpn/lwip/src/include/ipv4 \ @@ -255,6 +255,8 @@ TUN2SOCKS_SOURCES := \ LOCAL_MODULE := tun2socks +LOCAL_LDLIBS := -ldl -llog + LOCAL_SRC_FILES := $(addprefix badvpn/, $(TUN2SOCKS_SOURCES)) include $(BUILD_EXECUTABLE) diff --git a/src/main/jni/badvpn/base/BLog.c b/src/main/jni/badvpn/base/BLog.c index e3f7406f..e77073aa 100644 --- a/src/main/jni/badvpn/base/BLog.c +++ b/src/main/jni/badvpn/base/BLog.c @@ -32,6 +32,10 @@ #include "BLog.h" +#ifdef ANDROID +#include +#endif + #ifndef BADVPN_PLUGIN struct _BLog_channel blog_channel_list[] = { @@ -51,12 +55,22 @@ static char *level_names[] = { NULL, "ERROR", "WARNING", "NOTICE", "INFO", "DEBU static void stdout_log (int channel, int level, const char *msg) { +#ifndef ANDROID fprintf(stdout, "%s(%s): %s\n", level_names[level], blog_global.channels[channel].name, msg); +#else + __android_log_print(ANDROID_LOG_DEBUG, "tun2socks", + "%s(%s): %s\n", level_names[level], blog_global.channels[channel].name, msg); +#endif } static void stderr_log (int channel, int level, const char *msg) { +#ifndef ANDROID fprintf(stderr, "%s(%s): %s\n", level_names[level], blog_global.channels[channel].name, msg); +#else + __android_log_print(ANDROID_LOG_ERROR, "tun2socks", + "%s(%s): %s\n", level_names[level], blog_global.channels[channel].name, msg); +#endif } static void stdout_stderr_free (void) diff --git a/src/main/jni/badvpn/base/BLog.h b/src/main/jni/badvpn/base/BLog.h index d3eaf745..d7234cac 100644 --- a/src/main/jni/badvpn/base/BLog.h +++ b/src/main/jni/badvpn/base/BLog.h @@ -40,14 +40,6 @@ #include #include -#ifdef ANDROID -#include - -#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "tun2socks", __VA_ARGS__)) -#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "tun2socks", __VA_ARGS__)) - -#endif - // auto-generated channel numbers and number of channels #include diff --git a/src/main/jni/badvpn/tun2socks/tun2socks.c b/src/main/jni/badvpn/tun2socks/tun2socks.c index 0df7250d..8aeaf5b5 100644 --- a/src/main/jni/badvpn/tun2socks/tun2socks.c +++ b/src/main/jni/badvpn/tun2socks/tun2socks.c @@ -114,6 +114,7 @@ struct { int tun_fd; int tun_mtu; char *pid; + char *dnsgw; #else char *tundev; #endif @@ -207,6 +208,11 @@ LinkedList1 tcp_clients; // number of clients int num_clients; +#ifdef ANDROID +// Address of dnsgw +BAddr dnsgw; +#endif + static void terminate (void); static void print_help (const char *name); static void print_version (void); @@ -218,6 +224,9 @@ static void lwip_init_job_hadler (void *unused); static void tcp_timer_handler (void *unused); static void device_error_handler (void *unused); static void device_read_handler_send (void *unused, uint8_t *data, int data_len); +#ifdef ANDROID +static int process_device_dns_packet (uint8_t *data, int data_len); +#endif static int process_device_udp_packet (uint8_t *data, int data_len); static err_t netif_init_func (struct netif *netif); static err_t netif_output_func (struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr); @@ -563,6 +572,7 @@ void print_help (const char *name) #ifdef ANDROID " [--tunfd ]\n" " [--tunmtu ]\n" + " [--dnsgw ]\n" " [--pid ]\n" #else " [--tundev ]\n" @@ -725,6 +735,14 @@ int parse_arguments (int argc, char *argv[]) } i++; } + else if (!strcmp(arg, "--dnsgw")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.dnsgw = argv[i + 1]; + i++; + } else if (!strcmp(arg, "--pid")) { if (1 >= argc - i) { fprintf(stderr, "%s: requires an argument\n", arg); @@ -878,7 +896,7 @@ int parse_arguments (int argc, char *argv[]) int process_arguments (void) { ASSERT(!password_file_contents) - + // resolve netif ipaddr if (!BIPAddr_Resolve(&netif_ipaddr, options.netif_ipaddr, 0)) { BLog(BLOG_ERROR, "netif ipaddr: BIPAddr_Resolve failed"); @@ -945,6 +963,20 @@ int process_arguments (void) return 0; } } + +#ifdef ANDROID + // resolve dnsgw addr + if (options.dnsgw) { + if (!BAddr_Parse2(&dnsgw, options.dnsgw, NULL, 0, 0)) { + BLog(BLOG_ERROR, "dnsgw addr: BAddr_Parse2 failed"); + return 0; + } + if (dnsgw.type != BADDR_TYPE_IPV4) { + BLog(BLOG_ERROR, "dnsgw addr: must be an IPv4 address"); + return 0; + } + } +#endif return 1; } @@ -1103,6 +1135,13 @@ void device_read_handler_send (void *unused, uint8_t *data, int data_len) // accept packet PacketPassInterface_Done(&device_read_interface); + +#ifdef ANDROID + // process DNS directly + if (process_device_dns_packet(data, data_len)) { + return; + } +#endif // process UDP directly if (process_device_udp_packet(data, data_len)) { @@ -1130,6 +1169,140 @@ void device_read_handler_send (void *unused, uint8_t *data, int data_len) } } +#ifdef ANDROID +int process_device_dns_packet (uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + + // do nothing if we don't have dnsgw + if (!options.dnsgw) { + goto fail; + } + + static BAddr local_addr; + static BAddr remote_addr; + static int init = 0; + + int to_dns; + int from_dns; + int packet_length = 0; + + uint8_t ip_version = 0; + if (data_len > 0) { + ip_version = (data[0] >> 4); + } + + switch (ip_version) { + case 4: { + // ignore non-UDP packets + if (data_len < sizeof(struct ipv4_header) || data[offsetof(struct ipv4_header, protocol)] != IPV4_PROTOCOL_UDP) { + goto fail; + } + + // parse IPv4 header + struct ipv4_header ipv4_header; + if (!ipv4_check(data, data_len, &ipv4_header, &data, &data_len)) { + goto fail; + } + + // parse UDP + struct udp_header udp_header; + if (!udp_check(data, data_len, &udp_header, &data, &data_len)) { + goto fail; + } + + // verify UDP checksum + uint16_t checksum_in_packet = udp_header.checksum; + udp_header.checksum = 0; + uint16_t checksum_computed = udp_checksum(&udp_header, data, data_len, ipv4_header.source_address, ipv4_header.destination_address); + if (checksum_in_packet != checksum_computed) { + goto fail; + } + + // to port 53 is considered a DNS packet + to_dns = udp_header.dest_port == hton16(53); + + // from port 8153 is considered a DNS packet + from_dns = udp_header.source_port == dnsgw.ipv4.port; + + // if not DNS packet, just bypass it. + if (!to_dns && !from_dns) { + goto fail; + } + + // modify DNS packet + if (to_dns) { + BLog(BLOG_INFO, "UDP: to DNS %d bytes", data_len); + + // construct addresses + if (!init) { + init = 1; + BAddr_InitIPv4(&local_addr, ipv4_header.source_address, udp_header.source_port); + BAddr_InitIPv4(&remote_addr, ipv4_header.destination_address, udp_header.dest_port); + } + + // build IP header + ipv4_header.destination_address = dnsgw.ipv4.ip; + ipv4_header.source_address = netif_ipaddr.ipv4; + + // build UDP header + udp_header.dest_port = dnsgw.ipv4.port; + + } else if (from_dns) { + + // if not initialized + if (!init) { + goto fail; + } + + BLog(BLOG_INFO, "UDP: from DNS %d bytes", data_len); + + // build IP header + ipv4_header.source_address = remote_addr.ipv4.ip; + ipv4_header.destination_address = local_addr.ipv4.ip; + + // build UDP header + udp_header.source_port = remote_addr.ipv4.port; + + } + + // update IPv4 header's checksum + ipv4_header.checksum = hton16(0); + ipv4_header.checksum = ipv4_checksum(&ipv4_header, NULL, 0); + + // update UDP header's checksum + udp_header.checksum = hton16(0); + udp_header.checksum = udp_checksum(&udp_header, data, data_len, + ipv4_header.source_address, ipv4_header.destination_address); + + // write packet + memcpy(device_write_buf, &ipv4_header, sizeof(ipv4_header)); + memcpy(device_write_buf + sizeof(ipv4_header), &udp_header, sizeof(udp_header)); + memcpy(device_write_buf + sizeof(ipv4_header) + sizeof(udp_header), data, data_len); + packet_length = sizeof(ipv4_header) + sizeof(udp_header) + data_len; + + } break; + + case 6: { + // TODO: support IPv6 DNS Gateway + goto fail; + } break; + + default: { + goto fail; + } break; + } + + // submit packet + BTap_Send(&device, device_write_buf, packet_length); + + return 1; + +fail: + return 0; +} +#endif + int process_device_udp_packet (uint8_t *data, int data_len) { ASSERT(data_len >= 0) diff --git a/src/main/scala/com/github/shadowsocks/ShadowVpnService.scala b/src/main/scala/com/github/shadowsocks/ShadowVpnService.scala index f8fd30b5..b2b2a016 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowVpnService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowVpnService.scala @@ -50,6 +50,7 @@ import android.net.VpnService import org.apache.http.conn.util.InetAddressUtils import android.os.Message import scala.Some +import scala.concurrent.ops._ object ShadowVpnService { def isServiceStarted(context: Context): Boolean = { @@ -74,7 +75,6 @@ class ShadowVpnService extends VpnService { val PRIVATE_VLAN_172 = "172.30.254.%d" var conn: ParcelFileDescriptor = null - var udpgw: String = null var notificationManager: NotificationManager = null var receiver: BroadcastReceiver = null var apps: Array[ProxiedApp] = null @@ -136,18 +136,20 @@ class ShadowVpnService extends VpnService { } def startShadowsocksDaemon() { - new Thread { - override def run() { - val cmd: String = (BASE + - "shadowsocks -s \"%s\" -p \"%d\" -l \"%d\" -k \"%s\" -m \"%s\" -f " + - BASE + - "shadowsocks.pid") - .format(config.proxy, config.remotePort, config.localPort, config.sitekey, - config.encMethod) - Log.d(TAG, cmd) - System.exec(cmd) - } - }.start() + spawn { + val cmd: String = (BASE + + "shadowsocks -s \"%s\" -p \"%d\" -l \"%d\" -k \"%s\" -m \"%s\" -f " + + BASE + + "shadowsocks.pid") + .format(config.proxy, config.remotePort, config.localPort, config.sitekey, config.encMethod) + Log.d(TAG, cmd) + System.exec(cmd) + } + } + + def startDnsDaemon() { + val cmd: String = BASE + "pdnsd -c " + BASE + "pdnsd.conf" + Utils.runCommand(cmd) } def getVersionName: String = { @@ -182,43 +184,33 @@ class ShadowVpnService extends VpnService { config = Extra.get(intent) - new Thread(new Runnable { - def run() { - killProcesses() + spawn { + killProcesses() - // Resolve server address - var resolved: Boolean = false - if (!InetAddressUtils.isIPv4Address(config.proxy) && - !InetAddressUtils.isIPv6Address(config.proxy)) { - Utils.resolve(config.proxy, enableIPv6 = true) match { - case Some(addr) => - config.proxy = addr - resolved = true - case None => resolved = false - } - } else { - resolved = true + // Resolve server address + var resolved: Boolean = false + if (!InetAddressUtils.isIPv4Address(config.proxy) && + !InetAddressUtils.isIPv6Address(config.proxy)) { + Utils.resolve(config.proxy, enableIPv6 = true) match { + case Some(addr) => + config.proxy = addr + resolved = true + case None => resolved = false } - - // Resolve UDP gateway - if (resolved) { - Utils.resolve("u.maxcdn.info", enableIPv6 = false) match { - case Some(host) => udpgw = host - case None => resolved = false - } - } - - if (resolved && handleConnection) { - handler.sendEmptyMessageDelayed(MSG_CONNECT_SUCCESS, 300) - } else { - notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed)) - handler.sendEmptyMessageDelayed(MSG_CONNECT_FAIL, 300) - handler.sendEmptyMessageDelayed(MSG_STOP_SELF, 500) - } - handler.sendEmptyMessageDelayed(MSG_CONNECT_FINISH, 300) + } else { + resolved = true } - }).start() + + if (resolved && handleConnection) { + handler.sendEmptyMessageDelayed(MSG_CONNECT_SUCCESS, 300) + } else { + notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed)) + handler.sendEmptyMessageDelayed(MSG_CONNECT_FAIL, 300) + handler.sendEmptyMessageDelayed(MSG_STOP_SELF, 500) + } + handler.sendEmptyMessageDelayed(MSG_CONNECT_FINISH, 300) + } } def waitForProcess(name: String): Boolean = { @@ -258,7 +250,6 @@ class ShadowVpnService extends VpnService { .setMtu(VPN_MTU) .addAddress(localAddress.format(1), 24) .addDnsServer("8.8.8.8") - .addDnsServer("8.8.4.4") if (InetAddressUtils.isIPv6Address(config.proxy)) { builder.addRoute("0.0.0.0", 0) @@ -310,19 +301,21 @@ class ShadowVpnService extends VpnService { val cmd = (BASE + "tun2socks --netif-ipaddr %s " - + "--udpgw-remote-server-addr %s:7300 " + // + "--udpgw-remote-server-addr %s:7300 " + + "--dnsgw %s:8153 " + "--netif-netmask 255.255.255.0 " + "--socks-server-addr 127.0.0.1:%d " + "--tunfd %d " + "--tunmtu %d " + "--pid %stun2socks.pid") - .format(localAddress.format(2), udpgw, config.localPort, fd, VPN_MTU, BASE) + .format(localAddress.format(2), localAddress.format(1), config.localPort, fd, VPN_MTU, BASE) Log.d(TAG, cmd) System.exec(cmd) } /** Called when the activity is first created. */ def handleConnection: Boolean = { + startDnsDaemon() startShadowsocksDaemon() startVpn() true @@ -384,11 +377,9 @@ class ShadowVpnService extends VpnService { unregisterReceiver(receiver) receiver = null } - new Thread { - override def run() { - killProcesses() - } - }.start() + spawn { + killProcesses() + } if (conn != null) { conn.close() conn = null