ssh2-HTTPAgent源码阅读

ssh2包中提供了HTTPAgent,使用该HTTP Agent是可以接管请求,底层再通过SSH建立的与目标机器连接,从而实现内网穿透。

最近需要开发自定义的HTTP Agent,因此这里研究下实现。

整个HTTPAgent实现是在项目下的lib/http-agents.js

1
2
3
4
5
6
7
8
const { Agent: HttpAgent } = require('http');
const { Agent: HttpsAgent } = require('https');

for (const ctor of [HttpAgent, HttpsAgent]) {

...
exports[ctor === HttpAgent ? 'SSHTTPAgent' : 'SSHTTPSAgent'] = SSHAgent;
}

这里可以看到for循环将nodejs下的Agent进行改造并重新导出。

nodejs下的Agent构造函数如下

1
new Agent([options])

这里因为底层还需要SSH的配置,因此构造函数就是2个参数

1
2
3
4
5
6
constructor(connectCfg, agentOptions) {
super(agentOptions);

this._connectCfg = connectCfg;
this._defaultSrcIP = (agentOptions && agentOptions.srcIP) || 'localhost';
}

第一个参数connectCfg即SSH连接需要的参数,第二个是丢给了nodejs下的Agent,因此具体配置直接查看nodejs文档即可,ssh2这里主要是丢给了父类即Agent.

agentOptions中的srcIP在nodejs下是没有的,这个参数是ssh2这里增加的,主要用于底层TCP连接。

createConnection(options, cb)是Agent根据连接池策略动态创建。这里主要就是将TCP连接socket以回调形式返回为第二个参数cb。

这里是new了一个SSH的client,根据connectCfg建立连接,ready后,执行forwardOut,然后得到了TCP连接的socket,在cb(null, decorateStream(stream, ctor, options)); 这里的stream即socket。

之所以还走decorateStream,我理解是socket转为HTTP Agent需要的socket还需要一些属性方法,因此走空方法赋值。

针对异常部分的处理。

  1. 如果tcp连接通讯中出现了异常,cb的第一个参数即error返回,同时会主动断掉ssh的client连接
  2. 如果tcp连接关闭了,则主动断掉ssh的client连接,此时不用cb返回error.

tcp本地起服务是需要占用端口的,这里即localPort,如果没有明确指定则都0,但0不代表最终端口是0而是系统找寻空置的端口。

1
const srcPort = (options && options.localPort) || 0;