本篇分析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. 20135320赵瀚青LINUX内核分析第一周学习笔记

    赵瀚青原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.概述 第一周的学习内容主 ...

  2. Pycharm安装Python第三方库

    转自:http://blog.csdn.net/qiannianguji01/article/details/50397046 除了使用easy_insatll和pip工具安装Python第三方库外还 ...

  3. nxp的layerscape系列芯片中的rcw指定了一些什么信息

    答:指定了一些可以配置的硬件信息(如可以配置uart相关的引脚功能).引导镜像(uboot)的读取地址以及从何种介质(flash,sd)启动系统的信息

  4. webform CustomValidator

    https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.webcontrols.customvalidator?view=netframew ...

  5. ContOs 将SpringBoot的jar制作成系统服务

    将jar包上传到  你想要的目录下 如 /java/service/demo.jar 进入 /etc/systemd/system 目录下 新建  demo.service 内容如下 -------- ...

  6. 【深入理解JVM】:Java对象的创建、内存布局、访问定位

    对象的创建 一个简单的创建对象语句Clazz instance = new Clazz();包含的主要过程包括了类加载检查.对象分配内存.并发处理.内存空间初始化.对象设置.执行ini方法等. 主要流 ...

  7. eclipse及tomcat web站點

    eclipse环境下如何配置tomcat https://www.cnblogs.com/Leo_wl/p/4769760.htmleclipse环境下如何配置tomcat,并且把项目部署到Tomca ...

  8. try throw catch typeid

    QString str = ui.ll->text(); try { if (str == NULL) { throw 1; } else { throw 1.2; } } catch (int ...

  9. php获取经纬度

    <?php header("content-type:text/html;charset=utf-8"); function ipjwd() { $getIp=$_SERVE ...

  10. [java]Stream API——collect、reduce、orElse(x)

    一.collect 1.R collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) supplier:一个能创造 ...