ss-libev 源码解析udp篇 (4)
本篇分析remote_recv_cb,这是整个udp转发的反方向,即读取从后端发送过来的数据再发送给前端。对于ss-server,读取到的数据是目标地址的udp服务器发送回来的响应数据,ss-server需要将其包装成ss的格式并加密发送给ss-local;对于ss-local接收到的数据就是ss-server发送过来的加密数据,ss-local解密后需要再将其包装成socks5的格式发送给socks5客户端,至此整个udp转发过程完成。
下面具体分析:
- 首先是接收数据
struct sockaddr_storage src_addr;
socklen_t src_addr_len = sizeof(struct sockaddr_storage);
memset(&src_addr, 0, src_addr_len);
buffer_t *buf = ss_malloc(sizeof(buffer_t));
balloc(buf, buf_size);
// recv
r = recvfrom(remote_ctx->fd, buf->data, buf_size, 0, (struct sockaddr *)&src_addr, &src_addr_len);
buf->len = r;
- 下面是ss-local的处理,对于读取到的,来自remote的数据解密。并分析addr header。但这儿的分析只是为了检查addr header是否合法,不需要再获取到addr,因为remote_ctx里面已经有src_addr
#ifdef MODULE_LOCAL
int err = server_ctx->crypto->decrypt_all(buf, server_ctx->crypto->cipher, buf_size);
int len = parse_udprealy_header(buf->data, buf->len, NULL, NULL, NULL);
之后local会将解密的数据加上3个字节的0,构造成socks5的udp包格式。因为ss的加密前的udp包格式和socks5的格式只相差3个字节,而这三个字节前两个RSV固定为0,第三个FRAG对于ss来说只支持0,所以这儿加上3个0字节就构成了socks5格式的udp包。代码如下:
// Construct packet
brealloc(buf, buf->len + 3, buf_size);
memmove(buf->data + 3, buf->data, buf->len);
memset(buf->data, 0, 3);
buf->len += 3;
- 下面是ss-server的处理,首先remote_ctx中已经记录了addr_header,但是对于ip地址,ss又重新构造了addr header,填入的是前面recvfrom获取到的目的服务器的addr。也就是说对于域名的情况,直接使用之前保存的addr header。这儿我也不清楚作者为什么要对于ip地址的情况重新构造。之后将addr header附加到接收到的数据前面,并加密,准备发送往ss-local。
#ifdef MODULE_REMOTE
rx += buf->len; //统计接收到的下行数据量
char addr_header_buf[512];
char *addr_header = remote_ctx->addr_header;
int addr_header_len = remote_ctx->addr_header_len;
//如果是ip地址从src_addr中构造出addr header
if (remote_ctx->af == AF_INET || remote_ctx->af == AF_INET6) {
addr_header_len = construct_udprealy_header(&src_addr, addr_header_buf);
addr_header = addr_header_buf;
}
// Construct packet
brealloc(buf, buf->len + addr_header_len, buf_size);
memmove(buf->data + addr_header_len, buf->data, buf->len);
memcpy(buf->data, addr_header, addr_header_len);
buf->len += addr_header_len;
int err = server_ctx->crypto->encrypt_all(buf, server_ctx->crypto->cipher, buf_size);
if (err) {
// drop the packet silently
goto CLEAN_UP;
}
#endif
- 之后就是发送数据了,local和server的代码一样:
int s = sendto(server_ctx->fd, buf->data, buf->len, 0,
(struct sockaddr *)&remote_ctx->src_addr, remote_src_addr_len);
if (s == -1) {
ERROR("[udp] remote_recv_sendto");
goto CLEAN_UP;
}
- 最后还要重置timeout timer:
// handle the UDP packet successfully,
// triger the timer
ev_timer_again(EV_A_ & remote_ctx->watcher);
- 小结:udp和tcp不同的地方在于,tcp是流,udp是数据包;ss处理tcp是不光要监听读事件还要监听写事件,因为数据是不断从流里面过来,要持续的读写才能完成转发;而udp只需要监听读事件,因为一次recvfrom就是一个完整的udp包,所以对应一个sendto发出去即可,不需要考虑写了一部分这种事情。udp转发就分析到这儿了,总体看来比TCP简单很多。
ss-libev 源码解析udp篇 (4)的更多相关文章
- ss-libev 源码解析udp篇 (3)
本篇分析server_recv_cb,这个是udp转发中最重要的函数. server_recv_cb: 当ss-local或ss-server接收到来自前端的udp数据包时调用.这个函数代码比较多,除 ...
- ss-libev 源码解析udp篇 (2)
UDP relay的代码基本都在udprelay.c中,无论ss-local还是ss-server的代码都在一起,使用宏MODULE_LOCAL,MODULE_REMOTE等区分开.代码虽然不是很多, ...
- ss-libev 源码解析udp篇 (1)
shadowsocks-libev udp转发原理简介 ss_local作为一个sock5服务器,接收来自socks5客户端的数据包.在ss_local启动后,即创建一个udp socket,并bin ...
- jQuery2.x源码解析(缓存篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...
- jQuery2.x源码解析(构建篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...
- jQuery2.x源码解析(设计篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...
- jQuery2.x源码解析(回调篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 通过艾伦的博客,我们能看出,jQuery的pro ...
- Shiro源码解析-Session篇
上一篇Shiro源码解析-登录篇中提到了在登录验证成功后有对session的处理,但未详细分析,本文对此部分源码详细分析下. 1. 分析切入点:DefaultSecurityManger的login方 ...
- myBatis源码解析-类型转换篇(5)
前言 开始分析Type包前,说明下使用场景.数据构建语句使用PreparedStatement,需要输入的是jdbc类型,但我们一般写的是java类型.同理,数据库结果集返回的是jdbc类型,而我们需 ...
随机推荐
- 网络攻防工具介绍——Metasploit
Metasploit 简介 Metasploit是一款开源的安全漏洞检测工具,可以帮助安全和IT专业人士识别安全性问题,验证漏洞的缓解措施,并管理专家驱动的安全性进行评估,提供真正的安全风险情报.这些 ...
- Spring Security核心概念介绍
Spring Security是一个强大的java应用安全管理库,特别适合用作后台管理系统.这个库涉及的模块和概念有一定的复杂度,而大家平时学习Spring的时候也不会涉及:这里基于官方的参考文档,把 ...
- 20145109 《Java程序设计》第九周学习总结
JDBC 1 . DriverManager Class.forName("oracle.jdbc.driver.OracleDriver").newInstance(); 2 . ...
- HDU 1698 just a hook - 带有lazy标记的线段树(用结构体实现)
2017-08-30 18:54:40 writer:pprp 可以跟上一篇博客做个对比, 这种实现不是很好理解,上一篇比较好理解,但是感觉有的地方不够严密 代码如下: /* @theme:segme ...
- jvm垃圾回收策略
一.jvm堆内存的分代划分 在基于分代的内存回收策略中,堆空间通常都被划分为3个代,年轻代,年老代(或者tenured代),永久代.在年轻代中又被划分了三个小的区域,分别为:Eden(伊甸)区,S0区 ...
- Glibc-2.3.4编译
$tar xf Glibc2.3.4.tar.bz2 $mkdir build_glibc $cd build_glibc ../glibc-2.3.4/configure --prefix=/too ...
- lucene的分词器宝典
分词器概念介绍: Analyzer类(分词器)就是把一段文本中的词按某些规则取出,提供和以后查询时使用的工具类,注意在创建索引时会用到分词器,在使用字符串搜索时也会用到分词器,这两个地方要使用同一个分 ...
- Bigdecimal: Non-terminating decimal expansion; no exact representable decimal result.
做除法没有指定保留小数点后几位,就会抛出此异常. 因为会除不尽 Non-terminating decimal expansion; no exact representable decimal re ...
- Oracle归档的开启和关闭
--1.开启归档 [步骤] a.一致性关闭数据库(shutdown [immediate | transactional |normal]) b.启动到mount阶段(startup mount) c ...
- EasyUI datagrid 多条件查询
<script type="text/javascript"> $(function () { $("#dg").datagrid({ url: ' ...