`
totoxian
  • 浏览: 1074725 次
  • 性别: Icon_minigender_2
  • 来自: 西安
文章分类
社区版块
存档分类
最新评论

将stunnel修改成一个透明代理--附:setjmp/longjmp

 
阅读更多

stunnel的代码很简单,简单得没法形容,其执行过程分为下面几个部分:
1.main-loop:
while(1) {
fd = accept
do_client--可应用一些mpm
}
2.do_client:
if(server) {
init_ssl
init_remote
} else if (client) {
init_remote
init_ssl
}
transfer
3.init_ssl:
SSL_new
if(server) {
SSL_accept
} else if (client) {
SSL_connect
}
4.init_remote:
if (server) {
connect-real-server
连接真实的服务器
} else if (client) {
fd作为和用户通信的套接字
}
5.transfer:
while (没有关闭连接){
调用poll或者selece或者epoll等循环将数据从socket中读取然后写入SSL,以及从SSL中读取然后写入socket,实现透明数据转发
}
以上就是stunnel的工作流程,stunnel总是将自己作为client,在stunnel的客户端,它将自己作为stunnel客户端,在stuunel服务器,它将自己作为真实服务器的客户端,这样是很合理的,因为作为代理,它的意义将数据发送给最终的服务,并且向用户转发服务的响应,代理和转发过程中,通过修改stuunel的代码可以实现一个简单的七层过滤功能,不管是stunnel客户端还是stunnel服务器,在代理意义上,前面总是有stunnel的目的地,stunnel始终都是主动地去连接别的主机,即使stunnel服务器会接受stunnel客户端的连接,但是这无非只是为了建立一条安全的应用层隧道,和真正的业务数据无关。
按照stunnel的手册做,很容易使用stunnel配置一个反向代理服务器,诸如下面的配置文件:
server:
...
[https]
accept = 443
connect = 80
client:
...
[https]
accept = 448
connect = 443
这样在用户浏览器中配置stunnel客户端的448端口为代理服务器,所有的80端口的访问全部由stunnel来代理,这就是不透明的隧道式反向代理,所谓不透明是因为需要对用户浏览器进行配置,所谓反向代理是因为真正的目的地址完全由代理服务器决定,所谓隧道式指的是代理服务器并不是直接转发请求,而是分为了c-s模式,c-s之间建立一条安全隧道,这样比较适用于远程代理或者vpn等环境,同时也便于卸载用户环境的代理压力,可以将代理请求本身发往处理能力很强的远端stunnel服务器,本地的代理只需要接收代理请求并转发即可。
可是stunnel无法实现正向代理,因为对于http的正向代理需要解析http请求的头,而这个协议头是应用层数据,stunnel中并没有解析数据,只是透明地转发(详细情况参考transfer函数),因此如果想实现正向http代理则必须对stunnel代码进行一些修改,也就是解析stunnel服务器接收到的第一个请求包,从GET中解析出真实的地址信息后,然后再决定如何转发,这就涉及到了http协议,本文不谈。即使对http的正向代理实现了,那么对其它基于tcp协议的应用的正向代理如何实现呢?这就需要stunnel知道客户端需要访问的真实地址信息,因此这种代理很大程度上也就是透明代理,通过设置路由或者在windows上通过spi的方式将客户端对tcp应用的访问以stunnel客户端为下一跳,当请求过来后,通过下面的配置将请求重定向到stunnel客户端:
iptables -t nat -A PREROUTING --protocol tcp -m tcp -j REDIRECT --to-port xxx
xxx为stunnel客户端监听的端口。如此一来所有的请求都被定向到了stunnel,下面的问题是如何能得到这些请求原始的目的地信息,如果你很精通nat原理以及linux的实现,那么这个问题很简单,直接通过下面的调用就能得到:
getsockopt (fd, SOL_IP, SO_ORIGINAL_DST, &addr, &size);
得到了这个原始地址信息之后,需要想办法将之传给stunnel服务器,这样stunnel服务器才能根据这个地址信息来决定如何做,这时stunnel并不需要进行类似反向代理那样的配置。我们可以自定义一个通道将这个信息传过去,可是那样实现很松散,必须考虑连接和这个原始地址信息的对应关系,因此不用。由于stunnel客户端和stunnel服务器之间是一个ssl连接,在RFC5246中对ssl协议进行了更新,client-hello消息如下:
struct {
ProtocolVersion client_version;
Random random;
SessionID session_id;
CipherSuite cipher_suites<2..2^16-2>;
CompressionMethod compression_methods<1..2^8-1>;
select (extensions_present) {
case false:
struct {};
case true: //多了一个extension字段
Extension extensions<0..2^16-1>;
};
} ClientHello;
因此完全可以将这个地址信息通过这个client-hello消息的extension传给stunnel服务器,由于OpenSSL本身并没有将特性导出接口,故而需要对OpenSSL代码进行修改,本文省略。在stunnel服务器端,可以从这个hello消息中将该原始地址信息解析出来,然后以此信息进行连接决策。到此为止,整套的解决方案已经有了,下面就是如何修改代码的问题了。过程如下:
第一步:配置上述的nat规则;
第二步:其次修改prototypes.h中的CLI结构体,添加几个字段,并且添加相应的结构体定义:
typedef struct {
union {
struct sockaddr_in addr;
char szAddr[16];
char szPort[6];
} addr; //传输hello消息时,最好将信息编码为字符串,但是使用它进行connect的时候还是需要转化为sockaddr_in
char ext[0];
}ORIG_ADDR;
typedef struct {
...
} REAL_ADDR_CTX;
typedef struct {
...
ORIG_ADDR orig; //原始地址信息
REAL_ADDR_CTX orig_ctx; //ssl中和hello-extension相关的结构体
} CLI;
第三步:修改alloc_client_session,增加:
if (opt->option.client) {
int err = 1;
socklen_t sin_size = sizeof(struct sockaddr_in);
err = getsockopt (rfd, SOL_IP, SO_ORIGINAL_DST, &c->orig.addr, &sin_size);
...
//为c->orig.addr赋值,转化为字符串格式
}
第四步:修改init_ssl函数,服务器和客户端添加的代码不同:
1.SSL_new之后:
if (c->opt->option.client){
//对于client,用用户的真实目的地址信息初始化CLI的orig_ctx字段,也就是将之填入ssl相关的结构体,SSL_connect的时候会通过client-hello发送
} else {
//对于server,准备好CLI的orig_ctx字段,准备在SSL_accept之后接收用户的原始地址信息
}
2.SSL_accept(c->ssl)之后(肯定是server的情况):
取出client-hello-extension的消息,初始化CLI的orig_ctx结构体
第五步:修改connect_remote函数,初始化连接地址结构体前添加:
if(!c->opt->option.client){ //仅仅在server端添加
ret = callback(...);//在callback中可以根据策略修改最终的地址,也就是修改c->orig.addr.addr
memcpy(&addr.sa, &c->orig.addr.addr, sizeof addr);
}
以上五个步骤做完,stunnel也就成了一个透明代理了,用户无需在本地做任何配置(当然需要将网关设置为stunnel客户端,在windows上还可以利用spi),stunnel作为透明代理就转发了你的tcp请求(可能还会冲定向或者拦截),当然还可以进一步修改,使一切可配置化,不过这就和框架原理没有关系了。
附:setjmp和longjmp
stunnel使用setjmp来安装错误处理代码,这一点感觉很好,比如在一个连接刚建立并且处理业务之前,即run_client的开头,有如下调用:
static void run_client(CLI *c) {
...
error=setjmp(c->err);
if(!error) //直接调用setjmp,其返回为0,如果是从longjmp中返回的setjmp,其返回为1
do_client(c); //直接调用setjmp相当于仅仅安装错误处理代码,do_client完整的实现了业务逻辑,调用层次很深。
//如果do_client中以及其调用过程中的某处出现错误,就会调用longjmp(c->err),然后执行绪到达上面setjmp处,返回1,此时就要真正处理错误了,下面的代码全部是错误处理
s_log(...); //日志记录
...//资源清理后返回,结束此次连接
}
需要注意的是,虽然longjmp可以使得执行绪跳转到setjmp处,但是由于jmp_buf是基于当前栈帧的,因此必须在比setjmp调用层次更深的地方调用longjmp,也就是说setjmp的调用函数不能返回,返回了,longjmp就失效了,之所以有的情况下setjmp返回了longjmp还可用,那是因为栈虽然清了,为了效率只是改写了esp,ebp等寄存器的值,栈上的数据依然存在,因此longjmp恢复setjmp的jmp_buf时,数据依然是存在的,但是已经不可用了。因此一般的做法是,在处理的开始处调用setjmp安装错误处理代码,然后在该栈帧不返回前提下,后续的代码出错后调用longjmp。

分享到:
评论

相关推荐

    heroku-buildpack-stunnel:将 stunnel 推送到您的 buildpack 中,使您能够创建与其他资源的安全客户端连接

    Heroku buildpack:stunnel 这是一个提供 stunnel 的,旨在用作。 它使用 ,并从 buildpack 中提取。用法用法示例: $ ls -aProcfile package.json web.js .buildpacks$ heroku config:add BUILDPACK_URL=...

    Linux系统基础命令详细介绍.pdf

    在Linux系统中,命令行工具非常强大且灵活,每一个命令都具有特定的功能,且往往可以通过各种选项来调整其行为。 ### 查看系统信息 - 查看内核版本:`uname -r` - 查看CPU信息:`cat /proc/cpuinfo` - 查看内存信息...

    stunnel-5.55-win64-installer.exe

    亲测好使的 windows64 stunnel安装包。直接下载后,在本地安装即可使用!摘要为什么要写这么多,好奇怪啊

    bbc-stunnel:在 BBC MacBook 上轻松设置 stunnel

    要获得 stunnel,请运行 bash &lt;(curl -s https://raw.githubusercontent.com/jak/bbc-stunnel/master/install.sh) 启动后,您可以使用 、 或类的东西连接到localhost和上的6667端口,或上的6668端口。 移动 要...

    stunnel.tar.bz2(4.21和3.26)

    Stunnel是一个自由的跨平台软件,用于提供全局的TLS/SSL服务。针对本身无法进行TLS或SSL通信的客户端及服务器,Stunnel可提供安全的加密连接。该软件可在许多操作系统下运行,包括Unix-like系统,以及Windows。...

    Stunnel加密通道的架设指南.doc

    通过创建一个虚拟的加密隧道,Stunnel可以保护网络应用程序免受窃听攻击、中间人攻击等威胁。Stunnel支持多种协议,包括但不限于HTTP、FTP、SMTP、POP3等,并且兼容性强,能够在Windows、Linux等多种操作系统上运行...

    stunnel-5.56-win64-installer.zip

    **stunnel-5.56-win64-installer.zip** 是一个专为Windows 64位操作系统设计的软件安装包,包含 **stunnel** 的版本 5.56。**stunnel** 是一个开源的SSL/TLS封装器,它允许用户通过安全的加密通道来传输原本不支持...

    便携式文件夹加密器 5.55 - 可加密文件夹/u盘/移动硬盘

    便携式文件夹加密器] 是专为拥有大量商业秘密和...2.加密后,在被加密的文件夹下将生成一个解密程序,您可以使用解密程序方便地对文件进行解密或重加密 3.免费、绿色、体积小也是本软件的一大特色。软件大小仅为300多K

    linux下Qmail配置.docx

    #### 一、安装前准备 1. **操作系统选择**:本文档主要基于 Red Hat AS3/Array.0/8.0/7.3 进行介绍,适用于 Red Hat 系列的 Linux 发行版。 2. **软件包选择**:建议在安装 Red Hat 时选择安装 Apache、PHP 和 MySQL...

    Stunnel Proxy加密代理组合

    1. **客户端配置**:在客户端,Stunnel作为一个代理服务器运行,用户配置其连接到目标非加密服务,例如一个未加密的邮件服务器。客户端的所有请求首先通过Stunnel,再由Stunnel通过SSL/TLS连接到实际的服务端。 2. ...

    dumpt:具有tcpdump的Stunnel代理服务器,用于在配置了Google Workspace Secure LDAP时监视网络流量

    这个容器可让您将管理控制台中生成的.crt / .key对(以zip文件或单个.crt / .key文件的形式)添加到容器中的/data文件夹中,从而进行预配置Google Workspace Secure LDAP的Stunnel。 运行转储 通过运行以下命令在...

    stunnel-5.45

    stunnel的核心在于它的透明性,它作为一个代理运行,用户无需修改客户端或服务器端的配置,就能实现数据传输的安全加密。stunnel支持多种协议,包括SMTP、POP3、IMAP、FTP等,使得这些不安全的明文通信能够通过SSL/...

    stunnel-android,.zip

    stunnel-android作为一个开源项目,遵循自由软件的精神,源代码可供公众查看、修改和分发。开源意味着社区可以参与项目的开发,贡献代码、报告bug以及提出改进建议,这使得stunnel-android能够不断迭代和优化,以...

    RationalRoseEnterpriseEditionfor破解文件

    RationalRoseEnterpriseEditionfor破解文件,解决win 7 和winxp 中的报错问题

    docker-stunnel:进入容器

    它可用于将 SSL 功能添加到常用的 inetd 守护进程,如 POP2、POP3 和 IMAP 服务器,而无需对程序代码进行任何更改。 Stunnel 使用 OpenSSL 库进行加密,因此它支持编译到库中的任何加密算法。 此映像允许您在完全...

    coinbase-fix-example:Coinbase Pro FIX API的简单示例应用程序

    Coinbase Pro FIX示例这是一个简单的示例应用程序,演示了如何使用和(用于JVM的开源FIX引擎)连接到 。 构建和运行此应用程序需要Java Development Kit(JDK)11或更高版本以及Maven。用法要生成和运行该应用程序,...

    stunnel-5.53-win64-installer下载

    Stunnel是一个自由的跨平台软件,用于提供全局的TLS/SSL服务。针对本身无法进行TLS或SSL通信的客户端及服务器,Stunnel可提供安全的加密连接。该软件可在许多操作系统下运行,包括Unix-like系统,以及Windows。...

    centos-stunnel-systemd

    centos-stunnel-systemd 与stunnel一起使用的CentOS 7 systemd(systemctl)文件 将文件保存到: /lib/systemd/system/stunnel.service 使用如下命令: sudo systemctl start stunnel.service sudo systemctl ...

Global site tag (gtag.js) - Google Analytics