rinetd源码笔记

rinetd简介

rinetd是一个TCP端口转发服务,在应用层实现端口映射功能, 由Thomas Boutell写的,以GPL开源。 官网对rinetd的介绍是:

This program is used to efficiently redirect connections from one IP address/port combination to another. It is useful when operating virtual servers, firewalls and the like.

通常我们都是用Linux iptables配置端口映射,但rinetd还是有其用武之地:

  1. 没有管理员权限配置iptables规则
  2. 有些服务只监听在localhost,外界无法访问,此时就可以通过rinetd进行代理
  3. rinetd支持Windows平台,这样在仅有Windows时也能够实现Linux NAT功能

rinetd的部署情况,可以用以下文字图表示(rinetd在中间充当了中介的角色):

    --------------
    |   client   |
    --------------
              |
             /          Internet
            /
------------------------
|   FIREWALL           |
------------------------
        |               intranet
        |
    -----------
    | rinetd  |
    -----------
        |
        |
        |
    -------------------
    | internal server  |
    -------------------

为方便后文描述,这里定义:

  • 客户端:连接的发起方,比如浏览器。
  • 服务端:服务提供方,比如某个只能监听在localhost的HTTP代理服务。
  • rinetd:为客户端和服务端搭桥引线,从而把服务发布出去。

rinetd有比较明显的局限:

  • 只能转发TCP;
  • 不支持转发FTP,因为FTP需要不止一个socket才能工作(WHY)

源码笔记

只有5个源码文件,2000多行,非常小巧。 而且其中的getopt.[hc]两个文件是专门用于处理Windows平台的参数处理的。

  756 getopt.c
  129 getopt.h
  195 match.c
    9 match.h
 1565 rinetd.c
 2654 total

DEBUG宏是调试代码开关,定义了此宏表示进入调试版本

套接字相关变量的命名习惯

  • se - server sockets

    rinetd自身监听的套接字,客户端实际连接此套接字

  • re - remote sockets

    连接客户端的socket,由accept(2)接收连接后产生的。

  • lo - local sockets

    与re是一对,由rinetd发起连接服务端的socket

  • co - connections

    由re-lo套接字对组成,表示一条虚拟连接

  • coTotal

    允许的最大虚拟连接数,初始值为64;当全部占用后,以翻倍(即乘2)速度增长,不会缩小。

缓冲区

每一个连接有两个缓冲区:input和output,其中input缓冲区的生产者是re,消费者是lo, 缓冲的数据由客户端流向被代理服务端;output则相反。

每个缓冲区及对应的读写下标都是存放在不同的全局变量中,通过相同的下标索引。 这里如果把每个连接相关的东西封装到一个结构体中,逻辑上更加直观些,见whatacold/rinetd:)

coInput         缓冲区数组,为指针数组,共有coTotal个槽,每个指针指向一个bufferSpace字节的缓冲区
coInputRPos
coInputWPos
coOnput
coOnputRPos
coOnputWPos

RPos表示可写入的起始下标(R为Read,表示处理select的可读事件,需要写入缓冲区,变量名有点别扭), RPos-WPos表示已缓存的字节数。

input和output并非环形缓冲区,因为每次消费数据之后,都会判断RPos和WPos两个游标是否相等,若相等,俩游标都重置为0。

以上几个套接字间的关系,以及和缓冲区的关系可以用下图表示: 各socket间及缓冲区关系图

主循环

rinetd是一个单线程服务,有一个select主循环,处理5个网络套接字事件:

handleAccept(i);        // 看是否有新的连接发起
handleRemoteRead(i);    // 处理客户端发来的数据
handleRemoteWrite(i);   // 把缓冲区数据发给客户端
handleLocalRead(i);     // 处理“被代理”服务器发来的数据
handleLocalWrite(i);    // 把缓冲区数据发给“被代理”服务器

C文件内,对于内部的变量,加static可以防止被别人通过extern引用到(编译器会报错)。

参考

官网

man手册(源码中的index.html)

rinetd - a TCP port redirector此篇文章介绍了如何安装配置rinetd。

social