移植 HevLib 到 Android 系统

打算将 HevSocks5Client 移植到 Android 系统,替换原来的 HevTLSTunnel 实现。先要移植 HevLib,原以为只要增加两个文件 Application.mk 和 Android.mk 就可以了。但编译到 hev-event-source-signal.c 的时候就出错了,报 sys/signalfd.h 文件找不到。于是 android-ndk 目录里搜索了一下,还真没有这个文件的。这才意识到有可能 Android 的用户态目前还没开放这些 API。

如果仅仅是用户态没有导出问题应该不太,关键内核要实现了这些功能。还好我的系统中存在 /proc/config.gz 文件,解压查询确认 SIGNALFD、TIMERFD 及 EVENTFD 都是支持。下面的事情就好办了,就是增加几个系统调用的实现问题了。进一步确认了间接系统调用 syscall 函数是实现了的,连汇编都不用写了。

diff --git a/src/hev-event-source-signal.c b/src/hev-event-source-signal.c
index daf3e96..01f0088 100644
--- a/src/hev-event-source-signal.c
+++ b/src/hev-event-source-signal.c
@@ -11,7 +11,12 @@
 #include <unistd.h>
 #include <signal.h>
 #include <sys/epoll.h>
+#if defined(ANDROID)
+#include <fcntl.h>
+#include <asm/unistd.h>
+#else /* GENERIC */
 #include <sys/signalfd.h>
+#endif
 
 #include "hev-event-source-signal.h"
 
@@ -25,6 +30,31 @@ struct _HevEventSourceSignal
 	int signal_fd;
 };
 
+#if defined(ANDROID)
+#define SFD_NONBLOCK	O_NONBLOCK
+
+struct signalfd_siginfo
+{
+  uint32_t ssi_signo;
+  int32_t ssi_errno;
+  int32_t ssi_code;
+  uint32_t ssi_pid;
+  uint32_t ssi_uid;
+  int32_t ssi_fd;
+  uint32_t ssi_tid;
+  uint32_t ssi_band;
+  uint32_t ssi_overrun;
+  uint32_t ssi_trapno;
+  int32_t ssi_status;
+  int32_t ssi_int;
+  uint64_t ssi_ptr;
+  uint64_t ssi_utime;
+  uint64_t ssi_stime;
+  uint64_t ssi_addr;
+  uint8_t __pad[48];
+};
+#endif
+
 static HevEventSourceFuncs hev_event_source_signal_funcs =
 {
 	.prepare = NULL,
@@ -33,6 +63,14 @@ static HevEventSourceFuncs hev_event_source_signal_funcs =
 	.finalize = hev_event_source_signal_finalize,
 };
 
+#if defined(ANDROID)
+static int
+signalfd (int fd, const sigset_t *mask, int flags)
+{
+	return syscall (__NR_signalfd4, fd, mask, _NSIG / 8, flags);
+}
+#endif
+
 HevEventSource *
 hev_event_source_signal_new (int signal)
 {
diff --git a/src/hev-event-source-timeout.c b/src/hev-event-source-timeout.c
index 57a9b00..cacd196 100644
--- a/src/hev-event-source-timeout.c
+++ b/src/hev-event-source-timeout.c
@@ -11,10 +11,20 @@
 #include <errno.h>
 #include <unistd.h>
 #include <sys/epoll.h>
+#if defined(ANDROID)
+#include <fcntl.h>
+#include <asm/unistd.h>
+#else /* GENERIC */
 #include <sys/timerfd.h>
+#endif
 
 #include "hev-event-source-timeout.h"
 
+#if defined(ANDROID)
+#define TFD_NONBLOCK	O_NONBLOCK
+#define TFD_TIMER_ABSTIME 1
+#endif
+
 static bool hev_event_source_timeout_prepare (HevEventSource *source);
 static bool hev_event_source_timeout_check (HevEventSource *source, HevEventSourceFD *fd);
 static void hev_event_source_timeout_finalize (HevEventSource *source);
@@ -35,6 +45,22 @@ static HevEventSourceFuncs hev_event_source_timeout_funcs =
 	.finalize = hev_event_source_timeout_finalize,
 };
 
+#if defined(ANDROID)
+static int
+timerfd_create (int clockid, int flags)
+{
+	return syscall (__NR_timerfd_create, clockid, flags);
+}
+
+static int
+timerfd_settime (int fd, int flags,
+			const struct itimerspec *new_value,
+			struct itimerspec *old_value)
+{
+	return syscall (__NR_timerfd_settime, fd, flags, new_value, old_value);
+}
+#endif
+
 HevEventSource *
 hev_event_source_timeout_new (unsigned int interval)
 {

Over!

重新实现了 Socsk5

可能你会想实现那么多的 Socsk5 能吃吗?其实我只是想研究一下 I/O 模型,拿它来练习一下,如果效果不错也可以拿来用用。本次的实现没有使用 GLib,或许我真的想再也不用 GLib 啦?应该是这样的吧,至少我已经开始在实现我需要的东西。HevLib 就是我的一个初级替代品吧,目前仅仅包含最基本的数据结构、EventSource、EventLoop 等等。更多的数据结构与算法还有类型和对象系统就在以后有需要的时候再实现,一步一步来吧。

重写的 Socks5 Proxy 就是使用了 HevLib 的事件循环机制(Epoll,优先级排序),目前是单线程异步事件驱动的,感觉异步事件驱动实现起来还是有点麻烦的,和内核的进程调度一样,要上下文切换,实际来说 Session 中已经存储了上下文了,要做说也就是流程控制,我是简单的使用了一个状态变量控制的(可能不是太好,有好的方法可以告诉我哦)。

另外,在 DNS 解析上遇到一点麻烦、gethostbyname 虽然工作的没问题,显然这个阻塞的方法并不适合我现在的模型,又看了一下 getaddrinfo_a 和 c-ares 这个异步 DNS 查询库,结果发现想套到当前的事件循环机制中都有一些麻烦。后来就直接基于 UDP 实现了一个简单的异步的标准A记录查询器,简单又好用。

现在服务已经在 VPS 上部署了,RAM 的使用由原来的 300+M 直接下降到 80+M,功能还和原来一样的。;)

代码仓库:https://github.com/heiher/hev-socks5-proxy

Over!