类似反向代理,通过一台有公网 IP 的服务器中转使我们可以访问到 没有公网 IP 的服务
放张容易理解的图:
From: https://github.com/aploium/shootback
我自己的电脑 A 连入了内网,但不能直接连入互联网 或者说 网速有限制不能满足需求;
但外界的服务器 B 可以连上 A 提供的 http 服务 (由内网其他服务器转发),但仅仅支持普通的 http,不支持 websocket
需求是在这种情况下通过 B 向 A 发起 http 请求的方式实现 A 的连入互联网
A 想上网,但只能靠 B 向 A 主动发起 http 连接
B 发起 http 请求 和 A 的回应 必须完整后 才能抵达对方(长连接无效)
frp、上图的 shootback 的客户端和服务端都需要 tcp 连接,并不能支持 http 协议的连接
https://github.com/sensepost/reGeorg
reGeorg 可以把 web 服务变为 socks 代理,但本质上还是 B 通过 A 的方式去请求 A 能得到的资源(比如说 A 所在的内网资源),我这个需求是要反过来 A 通过 B 去访问外网
A 实现一个 socks5 代理 和 http 服务器,B 实现 http 客户端; 使用$$tap 来让上层应用走 A 的 socks5 代理上网
B 轮询 A 问有没有请求,如果有就记下来,与真实目标建立 socket 连接,下次 http 请求的时候带上请求的相应内容
目前我的实现是 B 开 100 个线程轮询 A,A 在 10s 内如果有请求就在 http 响应中给出请求内容(A 的 socket send 的内容)和 request id ; 10s 超时就让 B 继续轮询; B 收到回应后检查 request id 对应的 socket、往 socket 里面 send ; B 对每个 socket 循环 recv,有内容就发起对 A 的 http 请求,带上 request id 和 socket recv 得到内容
但在高并发的时候 A 与 B 之间的 http 通讯并不一定稳定,可能请求失败(丢包)、后来的请求先到达对方(乱序);感觉就要实现一个 http 之上的 tcp 协议。。。
多线程写起来也没把握,写炸了也不明不白;这种情景下的实现能不能用 select 或者 epoll
还有一个性能上的问题:这种情景下 socket send 一次相对原生的 tcp 而言代价就很高,但 socks5 代理 recv 以及 socket 连真实服务器 recv 可能不需要交互接连两次 /多次,这时候可以合并多次 recv 得到的内容以减轻代价(如下载大文件);但在 https 握手这种交互情况下,为了减轻延迟 recv 了一次就需要马上从真实服务器得到回应 上层应用才会发起下一个包,如果设计成每隔 1s 才发一次包就意味着 https 握手就需要 3~4s 才能完成。 可不可能实现智能判断 socket 一次 recv 后下一次 recv 多久后会到达?
问问大佬们有啥项目实现了这种情景 或者 给点建议
1
cy97cool OP 需求忘了说一点: B 向 A 的 http 请求的传输速度是高于百兆的,我目前瞎写的实现了 20M/s 的下载速度,但日常使用不稳定
想实现低延迟稳定的日常使用和高带宽的下载速度 |
2
pqee 2017-11-23 11:48:16 +08:00 1
HTTP 协议的本质只是格式要求,即第一行写什么第二行写什么。没有 TCP 可以考虑 UDP,都没有就废了,做不到的。
|
3
pqee 2017-11-23 11:54:40 +08:00 1
仔细读了一下发现楼主已经走了很远了,佩服!
帮你 google 了一下 tcp over http,找到了几个不错的:github.com/jpillora/chisel stackoverflow.com/a/14115584 |
4
cy97cool OP @pqee 感谢回复,
我看了看 chisel 本质上等同于 reGeorg,都是在让 B 走 http 请求 A(墙外)能访问的资源, 我的需求不是这样 而在于让 A(内网不能上网)借助 B 的 http 请求来上网 |
5
boyxupers 2017-11-23 13:05:15 +08:00 via iPhone
ssh -R ?
|
6
cy97cool OP |
7
jjianwen68 2017-11-23 13:36:06 +08:00
squid 可以不
|
8
boyxupers 2017-11-23 19:16:04 +08:00 via iPhone 1
@cy97cool 那么 http 不走 tcp 协议吗?
这里必然是得看限制具体是什么,如果可以 https,那么可以走 TLS 的 session ticket,如果不可以,模拟个 get 头,剩下的走 session ticket |
9
iceheart 2017-11-23 19:34:11 +08:00 via Android 1
逆向端口转发,跟 http 没关系,就是 tcp over tcp, 所有基于 tcp 的协议都能支持。对应的是正向转发,链路对称加密传输的话,前边放个普通代理就翻 wall 了,类似 ss 功能。
逆向加正向组合能在任何位置连进任何物理可达的网络。 |
10
cy97cool OP @boyxupers
限制是由于这个 http 服务是被反向代理的,这么说吧: B 可以访问到 A,是走服务器 C 中转的,而服务器 C 会先等到完成一个请求再把请求结果返回,所以说只支持 http 短连接 我对 session ticket 的理解不多,我觉得自己用 uuid1()生成一个 request id 就行? |
11
cy97cool OP @iceheart 不是很理解大佬的想法,大佬要不要再给点关键词给我学习一个
这种情景下的限制就是接触不到底层的 tcp 的,B 不能访问到 A 的 tcp 端口 只能走服务器 C 按 http 协议访问 A (所以 B 并不完全物理可达 A 只是 http 可达) 在这种限制下一切基于 tcp 的端口转发都要自己重新实现 正向的方式很简单,B 要访问 A 发个 socket 请求转为 http 主动请求等着回应就好; 而反过来 A 是不能主动请求 B 的,只能 A 被动接受 B 的轮询 |
12
iceheart 2017-11-23 23:50:12 +08:00 via Android 1
不好意思,又看了一遍需求才发现 B 到 A 是要经过反向代理的。大方向上没的选,只能是用 http 做载体。具体实现上我提些建议。我不建议你去实现 socks5 或者 http 代理协议,因为这些可以在 B 端搭一个现成的。你只需要实现在 A 端 listen 一个端口,A 向这个端口发起的 tcp 连接会被 B 转到一个指定的 ip:port 就好。至于这个端口跑 http 代理还是 socks 代理,哪个方便你就布哪个,何必自己写呢?你只需实现关键的 tcp 转发就完了呀。
tcp over http 反向转发: 建议 1.要有一个固定数量的 http 请求池,来接受 A 的数据请求。 2.AB 两端维护一个 tcp 连接集合,有状态的。来源就是来自 A listen 的端口上的连接,状态的变更就源自 AB 两端的 TCP 连接。连接标识可以用 A 端的 fd。 3.通信,A->B : http 等待请求池里取一个发应答回去,就到 B 那边了。 B->A :新发一个 http post 4.连接池维护:A 收到 http 请求扔到等待请求池里。 A 每秒检测一次这个池,超一秒的就返回空 http 应答。 |
13
Arnie97 2017-11-24 01:23:38 +08:00 via Android 1
想说 chisel 来着,发现#4 已经否定了。我感觉你说的 A、B 方向问题可以通过再套一层来解决?我这里以 chisel 和 openvpn 举个例子,其他协议同理。
A 部署 chisel server,openvpn server B 部署 chisel client,openvpn client B 通过 chisel 转发,和 A 建立起 openvpn 连接 此时 A,B 之间已经可以互访了,在 B 上开 NAT,A 的路由指向 B 即可 |
14
Arnie97 2017-11-24 01:27:49 +08:00 via Android 1
另外,chisel 是 Go 写的,我不是很了解这门语言是怎么垃圾回收的,反正在我 128MB 内存的容器里没法长期稳定运行,跑了一段时间后会内存不足…
|