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类型,而我们需 ...
随机推荐
- Python for循环文件
for 循环遍历文件:打印文件的每一行 #!/usr/bin/env python fd = open('/tmp/hello.txt') for line in fd: print line, 注意 ...
- USB详解
USB作为一种串行接口,应用日益广泛.如同每个工程设计人员必须掌握I2C,RS232这些接口一样,我们也必须掌握USB.但是USB的接口协议实在有点费解,Linux UCHI驱动作者之一Alan St ...
- Python学习笔记(十二)—Python3中pip包管理工具的安装【转】
本文转载自:https://blog.csdn.net/sinat_14849739/article/details/79101529 版权声明:本文为博主原创文章,未经博主允许不得转载. https ...
- Redis中RedisTemplate和Redisson管道的使用
当对Redis进行高频次的命令发送时,由于网络IO的原因,会耗去大量的时间.所以Redis提供了管道技术,就是将命令一次性批量的发送给Redis,从而减少IO. 一.Jedis对redis的管道进行操 ...
- Graph_Master(连通分量_D_Trajan缩点+dfs)
hdu_2242 题目大意:求将一张无向图(n个点,m条边)移除一条边分为不连通两部分,使得两部分的点权和最接近,若无法分为两部分,则输出impossible. 题解:拿到题面还算清晰,就是先tarj ...
- [Pytorch]PyTorch使用tensorboardX(转
文章来源: https://zhuanlan.zhihu.com/p/35675109 https://www.aiuai.cn/aifarm646.html 之前用pytorch是手动记录数据做图, ...
- Harbor 定制页面 和 二次开发指南
harbor的官方地址:https://github.com/goharbor/harbor 想对Harbor进行二次开发,首先要指定一个harbor的版本,这里我们以Harbor:1.6.2为例: ...
- C# 一些常用的字符串扩展方法
以下可能是常用的.net扩展方法,记录下 EString.cs文件 /// <summary> /// 扩展字符串类 /// </summary> public static ...
- java中使用Lambda表达式的5种语法
1,标准写法 思考下述情况: String[] arr = {"program", "creek", "is", "a" ...
- Learning R笔记(一)
基本操作 帮助文档:?函数.演示:demo(函数).参数列表:formals(函数),返回为成对列表pairlist. 用all.equal函数检查浮点数是否相等,容忍度默认为1.5e-8,如果相等返 ...