又是好久没来这儿了啊。
最近因为工作需要(其实也没需要那么多),一直在断断续续地看 OpenVPN 的代码,终于大概搞清楚了它的握手是怎么个流程了。简单来说的话其实非常的简单,首先在 reliable 模块中实现了一个可靠的 UDP 报文协议,就是加上超时重传和确认报文的功能;然后用该协议交换一个 Hard Reset 命令,开始握手;最后建立 SSL 对象,并且通过内存 BIO 在可靠 UDP 协议的基础上转发 OpenSSL 的握手协议报文,通过这个 SSL 连接交换 OpenVPN 自己的密钥。接下来就是用这些密钥,该干嘛干嘛了。
本来还写了好多,不过太乱,写不下去了,还是简单点吧。具体实现就是,首先在 key_state_init() 函数中,初始化了 ssl 、 ssl_bio 、 ct_in 、 ct_out 这几个相关的成员, ssl 就是普通 SSL 程序里面的 SSL 对象, ssl_bio 是以 BIO 接口来读写这个 ssl 成员而准备的,tls_process() 中通过 ssl_bio 这个成员完成 OpenVPN 自己的密钥交换,完成从 S_START 到 S_GOT_KEY 这几个状态的转换。而 ct_in 和 ct_out 则是这个 ssl 对象的后端,不是通常用的 socket BIO 对象,而是两个 memory BIO 对象,为的就是得到 ssl 对象要发给对端的密文,然后通过可靠 UDP 协议来转发。这样, tls_process() 的主要工作就是操作这几个对象,外加可靠 UDP 模块的那几个队列了。
这里的关键就是通过 OpenSSL 实现提供的 memory BIO 对象抓取密文包的功能。如果是在其他没有这个功能接口的语言实现或封装中,则可以使用 socketpair 这种方法来抓,我今天就试着改了一下 OpenVPN ,这种方法是可行的。具体就是把其中一个 socket 给 ssl 当后端,另一个 socket 就可以用来读写密文了。注意 socketpair() 调用中的 type 参数要加上 SOCK_NONBLOCK 。
不过在 Erlang 这个语言中,甚至就连 socketpair 这个调用都没封。搜了一下它的源码,才发现自己实现一个也非常的简单,怪不得。下面摘自 otp_src_R14B/lib/kernel/test/inet_sockopt_SUITE.erl 文件:
| |
至于前面的 ?line 是啥,咱就不需要关心了。