本篇分析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)的更多相关文章

  1. ss-libev 源码解析udp篇 (3)

    本篇分析server_recv_cb,这个是udp转发中最重要的函数. server_recv_cb: 当ss-local或ss-server接收到来自前端的udp数据包时调用.这个函数代码比较多,除 ...

  2. ss-libev 源码解析udp篇 (2)

    UDP relay的代码基本都在udprelay.c中,无论ss-local还是ss-server的代码都在一起,使用宏MODULE_LOCAL,MODULE_REMOTE等区分开.代码虽然不是很多, ...

  3. ss-libev 源码解析udp篇 (1)

    shadowsocks-libev udp转发原理简介 ss_local作为一个sock5服务器,接收来自socks5客户端的数据包.在ss_local启动后,即创建一个udp socket,并bin ...

  4. jQuery2.x源码解析(缓存篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...

  5. jQuery2.x源码解析(构建篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...

  6. jQuery2.x源码解析(设计篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...

  7. jQuery2.x源码解析(回调篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 通过艾伦的博客,我们能看出,jQuery的pro ...

  8. Shiro源码解析-Session篇

    上一篇Shiro源码解析-登录篇中提到了在登录验证成功后有对session的处理,但未详细分析,本文对此部分源码详细分析下. 1. 分析切入点:DefaultSecurityManger的login方 ...

  9. myBatis源码解析-类型转换篇(5)

    前言 开始分析Type包前,说明下使用场景.数据构建语句使用PreparedStatement,需要输入的是jdbc类型,但我们一般写的是java类型.同理,数据库结果集返回的是jdbc类型,而我们需 ...

随机推荐

  1. gerrit代码审核工具之“error unpack failed error Missing unknown”错误解决思路

    使用gerrit代码审核工具时遇到error: unpack failed: error Missing unknown d6d7c89bd1d77f44c5c8e99437aaffbfc0684e7 ...

  2. SaltStack部署配置Tomcat-第三篇

    实验目标 简单部署tomcat及安装java环境 实现步骤 编写salt的状态模块 [root@linux-node1 web]# pwd /srv/salt/base/web [root@linux ...

  3. mac 下 安装 mongodb

    使用brew安装,不过brew不再更新, 通过 sudo chown -R $(whoami):admin /usr/local 这条语句终端有提醒的 xcode-select --install 需 ...

  4. du,df 磁盘管理

    du会把指定目录下所有文件.目录.目录下的文件都统计.是建立在文件系统能看到的的确确是有这样一些文件的基础上的.也就是说我们能在文件系统里面看到的文件才会被du统计. df命令可以获取硬盘被占用了多少 ...

  5. HTML5如何做横屏适配

    在移动端中我们经常碰到横屏竖屏的问题,那么我们应该如何去判断或者针对横屏.竖屏来写不同的代码呢. 首先在head中加入如下代码: 1 <meta name="viewport" ...

  6. virtual dom & mvvm

    虚拟dom 用js对象来表示dom树的结构,然后用这个对象来构建一个真正的dom树插入文档中: 当状态有变时,重新构造一个新的对象树,然后比较新的和旧的树,记录两个数的差异: 把差异部分应用到真正的d ...

  7. es6之Iterator

    1.任何数据结构只要部署了Iterator接口(本质是一个指针对象),也就是部署了Symbol.iterator属性,便可以完成遍历操作:数组原生就具备Iterator接口,就可以用for...of遍 ...

  8. Memory Manager surface area changes in SQL Server 2012

    here were various changes to memory related DMVs, DBCC memory status, and Perfmon counters in SQL Se ...

  9. 利用python3.x实现小爬虫下载贴吧内图片

    Hi, I'm back.   寒假在家只有一台笔记本,也懒得把台式机上的键盘拆下来用,因此编程被我暂时搁置,转而在网易云课堂上学了一下Python.可惜的是云课堂的Python教程是基于Python ...

  10. CSS布局框架 960GS

    1.960GS 特点 小巧简单,功能单一(仅仅做排版的工作,其他东西靠自己.)(三个文件:reset.css,960.css,font.css) 界面宽960px,适合目前主流1/2以上显示器都满屏宽 ...