diff --git a/build.sh b/build.sh index d4bc155b..14cfaff6 100755 --- a/build.sh +++ b/build.sh @@ -15,7 +15,7 @@ rm -rf assets/armeabi-v7a rm -rf assets/x86 mkdir -p assets/armeabi-v7a mkdir -p assets/x86 -for app in pdnsd redsocks ss-local ss-tunnel +for app in pdnsd redsocks ss-local ss-tunnel tun2socks do try mv libs/armeabi-v7a/$app assets/armeabi-v7a/ try mv libs/x86/$app assets/x86/ diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 4726f180..23950b2d 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -18,6 +18,7 @@ #ifdef ANDROID +#include #include BAVL connections_tree; typedef struct { @@ -145,6 +146,94 @@ static void tcp_remove(struct tcp_pcb* pcb_list) } } +ssize_t +sock_fd_write(int sock, void *buf, ssize_t buflen, int fd) +{ + ssize_t size; + struct msghdr msg; + struct iovec iov; + union { + struct cmsghdr cmsghdr; + char control[CMSG_SPACE(sizeof (int))]; + } cmsgu; + struct cmsghdr *cmsg; + + iov.iov_base = buf; + iov.iov_len = buflen; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + if (fd != -1) { + msg.msg_control = cmsgu.control; + msg.msg_controllen = sizeof(cmsgu.control); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof (int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + *((int *) CMSG_DATA(cmsg)) = fd; + } else { + msg.msg_control = NULL; + msg.msg_controllen = 0; + } + + size = sendmsg(sock, &msg, 0); + + if (size < 0) + perror ("sendmsg"); + return size; +} + +ssize_t +sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd) +{ + ssize_t size; + + if (fd) { + struct msghdr msg; + struct iovec iov; + union { + struct cmsghdr cmsghdr; + char control[CMSG_SPACE(sizeof (int))]; + } cmsgu; + struct cmsghdr *cmsg; + + iov.iov_base = buf; + iov.iov_len = bufsize; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsgu.control; + msg.msg_controllen = sizeof(cmsgu.control); + size = recvmsg (sock, &msg, 0); + if (size < 0) { + perror ("recvmsg"); + return -1; + } + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { + if (cmsg->cmsg_level != SOL_SOCKET) { + return -1; + } + if (cmsg->cmsg_type != SCM_RIGHTS) { + return -1; + } + + *fd = *((int *) CMSG_DATA(cmsg)); + } else { + *fd = -1; + return -1; + } + } + return size; +} + #endif #define LOGGER_STDOUT 1 @@ -381,6 +470,72 @@ static void daemonize(const char* path) { close(STDOUT_FILENO); close(STDERR_FILENO); } + +int +recv_tun_fd (void) +{ + int s_fd, c_fd, fd_to_read; + struct sockaddr_un addr; + socklen_t addr_len; + char buf[16]; + + s_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (s_fd < 0) { + BLog(BLOG_ERROR, "Error to create local socket"); + return -1; + } + + BLog(BLOG_NOTICE, "Create socket"); + + addr_len = sizeof(struct sockaddr_un); + memset(&addr, 0, addr_len); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path + 1, 14, "tun2socks"); + + if (bind(s_fd, (struct sockaddr *) &addr, addr_len) != 0) + { + BLog(BLOG_ERROR, "Error to bind local socket"); + close(s_fd); + return -1; + } + + BLog(BLOG_NOTICE, "Bind socket"); + + if (listen(s_fd, 5) != 0) + { + BLog(BLOG_ERROR, "Error to listen on local socket"); + close(s_fd); + return -1; + } + + BLog(BLOG_NOTICE, "Listen on socket"); + + if ((c_fd = accept(s_fd, (struct sockaddr *)&addr, &addr_len)) != -1) + { + char buf[1]; + + BLog(BLOG_NOTICE, "Accept a connection"); + + read(c_fd, buf, 1); + sock_fd_read(c_fd, buf, sizeof(buf), &fd_to_read); + + buf[0] = 0; + write(c_fd, buf, 1); + close(c_fd); + } + else + { + BLog(BLOG_ERROR, "Error to accept socket"); + close(s_fd); + return -1; + } + + close(s_fd); + + if (fd_to_read == 0) return -1; + options.tun_fd = fd_to_read; +} + #endif int main (int argc, char **argv) @@ -436,8 +591,25 @@ int main (int argc, char **argv) BLog_SetChannelLoglevel(i, options.loglevel); } } - + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + +#ifdef ANDROID + if (options.pid) { + daemonize(options.pid); + } + + BLog(BLOG_NOTICE, "Receiving the tun fd"); + + // Wait to recevie tun fd + if (recv_tun_fd() == -1) { + BLog(BLOG_ERROR, "Failed to recv tun fd"); + return -1; + } + + BLog(BLOG_NOTICE, "Received the tun fd successfully"); + +#endif // clear password contents pointer password_file_contents = NULL; @@ -563,12 +735,6 @@ int main (int argc, char **argv) // init number of clients num_clients = 0; -#if 0 - if (options.pid) { - daemonize(options.pid); - } -#endif - // enter event loop BLog(BLOG_NOTICE, "entering event loop"); BReactor_Exec(&ss); diff --git a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala index 012cf9ae..aa6d93b6 100644 --- a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala +++ b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala @@ -123,7 +123,7 @@ object Shadowsocks { val FEATRUE_PREFS = Array(Key.route, Key.isGlobalProxy, Key.proxyedApps, Key.isUdpDns, Key.isAutoConnect) - val EXECUTABLES = Array(Executable.PDNSD, Executable.REDSOCKS, Executable.SS_TUNNEL, Executable.SS_LOCAL) + val EXECUTABLES = Array(Executable.PDNSD, Executable.REDSOCKS, Executable.SS_TUNNEL, Executable.SS_LOCAL, Executable.TUN2SOCKS) // Helper functions def updateListPreference(pref: Preference, value: String) { diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala index 12c04a89..96af50c7 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala @@ -45,7 +45,7 @@ import java.net.InetAddress import android.app._ import android.content._ import android.content.pm.{PackageInfo, PackageManager} -import android.net.VpnService +import android.net.{VpnService, LocalSocket, LocalSocketAddress} import android.os._ import android.support.v4.app.NotificationCompat import android.util.Log @@ -322,19 +322,33 @@ class ShadowsocksVpnService extends VpnService with BaseService { if (BuildConfig.DEBUG) Log.d(TAG, cmd) - spawn { - Tun2Socks.start(cmd.split(" ")) - } + Console.runCommand(cmd) + + Thread.sleep(300) + + // try { + val localSocket = new LocalSocket() + localSocket.setFileDescriptorsForSend(Array(conn.getFileDescriptor)) + val localAddr = new LocalSocketAddress("tun2socks") + localSocket.connect(localAddr) + val output = localSocket.getOutputStream + output.write(1) + output.flush() + output.close() + localSocket.close() + // } catch { + // case ex: Exception => // Ignored + // } } /** Called when the activity is first created. */ def handleConnection: Boolean = { - startVpn() startShadowsocksDaemon() if (!config.isUdpDns) { startDnsDaemon() startDnsTunnel() } + startVpn() true } @@ -362,7 +376,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { } def killProcesses() { - for (task <- Array("ss-local", "ss-tunnel", "pdnsd")) { + for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "tun2socks")) { try { val pid = scala.io.Source.fromFile(Path.BASE + task + ".pid").mkString.trim.toInt Process.killProcess(pid) @@ -371,7 +385,6 @@ class ShadowsocksVpnService extends VpnService with BaseService { case e: Throwable => Log.e(TAG, "unable to kill " + task, e) } } - Tun2Socks.stop(); } override def startRunner(c: Config) {