redsocks源码阅读 socks5源码

redsocks源码阅读 socks5源码

redsocks源码阅读

最近有机会了解和使用了开源软件redsocks,这是一个运行在设备端的代理服务器(proxy server)前端,在linux下能很好地支持iptables,实现系统级的透明代理。

redsocks是单线程的,包括所依赖的库libevent也是单线程的,因此适合运行在资源有限的嵌入式设备上,效率很高。在Android上也有一些应用程序(app)是基于redsocks开发的,比如ProxyDroid是一个Android上的透明代理软件,底层直接使用了redsocks,但是去掉了对DNS、UDP的支持。

redsocks处理网络通信、消息循环、缓冲区管理等都是基于开源库libevent的,因此在阅读redsocks 源码前应先对 libevent 的 bufferevent、evbuffer 等有所了解,好在虽然libevent代码量远比redsocks大,但有详细的官方文档可读,不需要理解它的工作原理只需要知道怎么用就行了,libevent的参考手册在http://www.wangafu.net/~nickm/libevent-book/,据说Google的Chrome浏览器中也用了libevent。

redsocks用C语言写成,全部源码仅有7K余行,可从https://github.com/darkk/redsocks下载,虽然该开源项目已有两三年没有被维护过,但代码质量很高,可读性也很好。

本文仅针对 http-relay 的情形(也就是一般的HTTP代理)阅读了相关代码,主要对app、redsocks、proxy server三者之间的数据传输流程进行了分析。下面主要是自己的阅读笔记,供日后备忘,可能写得有些凌乱(原文放在Evernote上,此处内容有所删改)。

redsocks 创建两个socket,一个是client 端的,在 redsocks.c 中的 redsocks_accept_client中,accept 应用程序后得到 client_fd,并基于client_fd 调用 bufferevent_new 创建了 client->client:

client->client =bufferevent_new(client_fd, NULL, NULL, redsocks_event_error,client);

然后调用 bufferevent_enable设置 read 回调函数:

if(bufferevent_enable(client->client, EV_READ) != 0) {

redsocks_log_errno(client, LOG_ERR,"bufferevent_enable");

goto fail;

}

然后

if(self->relay_ss->connect_relay)

self->relay_ss->connect_relay(client);

else

redsocks_connect_relay(client);

会调用 httpr_connect_relay,挂上读回调函数httpr_client_read_cb:

client->client->readcb =httpr_client_read_cb;

所以 client 那边第一次有数据过来时,也就是 HTTP Request 头过来时,应该是 httpr_client_read_cb会被调用,在 httpr_client_read_cb里面从 buffev->input 中逐行读入 HTTP Request 头并分析、复制到 httpr->client_buffer中,读取完后,connect_relay 被设为 1,先调用httpr_client_read_content将紧跟在 HTTP Request 头之后的一些数据也复制到 httpr->client_buffer中,然后去调用 redsocks_connect_relay。

redsocks_connect_relay调用 red_connect_relay,其中创建了另一个socket,即relay 端的 relay_fd,基于relay_fd 调用 bufferevent_new 创建了 client->relay,回调函数redsocks_relay_connected作为写回调被挂上。

在 redsocks_relay_connected中,将 httpr_relay_read_cb和 httpr_relay_write_cb分别作为 client->relay 的读、写回调被挂上(因此刚才的写回调 redsocks_relay_connected被顶替掉了),并立即调用写回调 httpr_relay_write_cb,在该函数中将刚才读入到httpr->client_buffer中的 HTTP Request 头(及紧跟其后的一些数据)写入到 client->relay 中,其间还处理了认证信息,最后使能了 client->relay 的读事件(这里 buffev 就是 client->relay):

buffev->wm_read.low = 1;
buffev->wm_read.high = HTTP_HEAD_WM_HIGH;
bufferevent_enable(buffev, EV_READ);

当 relay 端有数据(即 HTTP Response)回来时,回调httpr_relay_read_cb被调用,从 buffev->input 中逐行读入 HTTP Response 头并分析、复制到 httpr->relay_buffer中(这里 buffev 就是 client->relay),当HTTP Response 正常并且 HTTP Response 头全部读完后再将 httpr->relay_buffer中的数据全部写入到 client->client 中也就是送给应用程序,之后去调用 redsocks_start_relay,在该函数中将client->client 和 client->relay 如下挂上新的读、写回调,原来的回调都被顶替掉了:

client->client->readcb =redsocks_relay_clientreadcb;
client->client->writecb = redsocks_relay_clientwritecb;
client->relay->readcb = redsocks_relay_relayreadcb;
client->relay->writecb =redsocks_relay_relaywritecb;

所以这 4 个回调处理的是 HTTP Request 和 Response 中除了头信息之外的数据,它们的工作就是搬运数据(不涉及内存复制所以效率很高)并带有流量控制。这 4 个回调函数的第 2 个参数 _client 都是 client,第1 个参数 from 或 to 就是 client->client 或 client->relay,所以它们的第1 个参数都没被使用而只使用了第 2 个参数。

httpr_relay_read_cb返回时,client->relay->input中可能仍剩有数据(也就是紧跟在 HTTP Response 头之后的一些数据),这些数据会被回调 redsocks_relay_clientwritecb或者 redsocks_relay_relayreadcb读走送往 client 端。

阅读上述 4 个回调函数的代码,不难发现:

redsocks_relay_relayreadcb与 redsocks_relay_clientwritecb所做的工作部分重复了;

redsocks_relay_clientreadcb与 redsocks_relay_relaywritecb所做的工作部分重复了。

可以把它们分别看成是一个推一个拉,前一个回调是当有数据过来时就推到另一端,后一个回调是当数据都送走以后再去另一端拉新的数据,两个并不会发生冲突。

在函数 redsocks_relay_writecb(在redsocks_relay_relaywritecb和 redsocks_relay_clientwritecb中被调用)中判断是否传输结束,若结束则调用 redsocks_shutdown 结束整个流程。

每当 client 端有新的 HTTP Request 过来时,又重复执行上面的流程。

最后看一下传输结束时的处理流程。

先看回调函数 redsocks_event_error,该回调函数是在函数redsocks_connect_relay中创建 client->relay 时挂上的错误处理回调函数;另一方面,该回调函数也是在函数 redsocks_accept_client中创建 client->client 时挂上的错误处理回调函数,也就是说这个回调函数身兼 client->relay 与 client->client 二者的错误处理回调函数。

当 relay 端向 client 端传输 HTTP Response 结束时,回调函数 redsocks_event_error被调用(应该是由 relay 端的 socket 事件触发的),其参数 what 的值为 17(即EVBUFFER_READ|EVBUFFER_EOF),buffev 等于 client->relay,看下面代码:

if (what ==(EVBUFFER_READ|EVBUFFER_EOF)) {

struct bufferevent *antiev;

if (buffev == client->relay)

antiev = client->client;

else

antiev = client->relay;

redsocks_shutdown(client, buffev, SHUT_RD);

if (antiev != NULL && EVBUFFER_LENGTH(antiev->output) ==0)

redsoc ks_shutdown(client, antiev, SHUT_WR);

}

antiev 将等于 client->client,然后函数redsocks_shutdown 被调用,将 client->relay_evshut置上 EV_READ,接下来,如果EVBUFFER_LENGTH(antiev->output) ==0 也就是 client->client->output已经空了,就再调用函数 redsocks_shutdown 将 client->client_evshut置上 EV_WRITE。至此从relay 端向 client 端的传输完全结束了。

反过来,当 client 端向 relay 端传输 HTTP Request 结束时,回调函数 redsocks_event_error也被调用(应该是由 client 端的 socket 事件触发的),其参数 what 的值也为 17,buffev 则等于 client->client,跟上面相似的,也将导致函数redsocks_shutdown 被调用两次,最后使得 client->client_evshut被置上 EV_READ 及 client->relay_evshut被置上 EV_WRITE,至此两个方向的传输都完全结束了,这样一来,在函数redsocks_shutdown 的最后:

if (client->relay_evshut ==(EV_READ|EV_WRITE) && client->client_evshut ==(EV_READ|EV_WRITE)) {

redsocks_log_error(client, LOG_DEBUG, "both client and serverdisconnected");

redsocks_drop_client(client);

}

条件判断结果将为真,导致函数 redsocks_drop_client被调用,该函数进行一系列的清除和释放资源动作,包括释放 client 数据结构本身,至此本 client 完全终结。

附:redsocks中的几个关键概念和数据结构:

client 端:即应用程序(app)端;

relay 端:即代理服务器(proxy server)端;

client应用程序与redsocks 建立连接后创建的数据结构,应用程序每次与 redsocks 建立连接会创建一个新的 client;

client_fd relay_fd分别是与client 端通信和与 relay 端通信的 socket 句柄;

client->client client->relay分别是与client_fd 和 relay_fd 关联的 bufferevent 对象;

httpr->client_buffer httpr->relay_buffer分别是在http-relay 中用来分析、处理 HTTP Request 和 Response 头的缓冲区。httpr->client_buffer中除了 HTTP Request 头,还有紧跟其后的一些数据;而 httpr->relay_buffer中只有 HTTP Response 头。

  

爱华网本文地址 » http://www.413yy.cn/a/25101014/234196.html

更多阅读

android4.1环境搭配 android4.1.2源码下载

1、下载jdk1.7安装http://www.oracle.com/technetwork/java/javase/downloads/index.html2、下载eclipse安装http://www.oracle.com/technetwork/java/javase/downloads/index.html图中红色标识版本均可,推荐下载EclipseClassic

转载 汤姆·狄马克和他的TD序列法 狄马克td序列源码

cctv原文地址:汤姆·狄马克和他的TD序列法作者:hyx8895汤姆·狄马克和他的TD序列法  汤姆·狄马克(TomDeMarco),技术分析师,股市拐点指标创建者。是当前技术分析领域两位最有名的大师之一,他在70年代就发明了一套客观的方法来推算趋势

关于AGPS定位服务器地址的设置 android agps定位源码

最近在折腾三星i8000手机的GPS,可能是由于该机器的GPS芯片的缘故(或者有其他原因),开始的时候定位很慢,不是可以看见卫星少,就是即使可见卫星数量有,但都是无法锁定而定位,经过一段时间的摸索,认定是定位服务器的地址设定问题,因此更换了地址

交易师经典版特色功能1 分时九转 分时九转指标公式源码

交易师经典版特色功能(1)分时九转交易师由原“飞狐交易师”团队广州博庭科技全力打造。嗯,虽然迟了点,我还是选了一个重要的日子6月6日6时6分,做为我们新产品的首次发布,希望一切顺利。为了感谢大家一直以来的支持,首先在此郑重承诺,做为旗

声明:《redsocks源码阅读 socks5源码》为网友一叶知秋分享!如侵犯到您的合法权益请联系我们删除