NPS 客户端辅助模块:控制与 P2P 打洞细节

引言

在上一篇文章中,我们深入剖析了 NPS 客户端的核心 client.go,了解了其连接管理和流量转发机制。本篇文章将继续探索 NPS 客户端的其他辅助模块,特别是 nps/client/control.go 文件。这个文件包含了客户端的一些控制逻辑,例如获取任务状态、从配置文件启动客户端,以及一些 P2P UDP 打洞相关的辅助函数。

control.go:客户端的控制中心

control.go 文件提供了客户端的一些高级控制功能,使得用户可以通过配置文件或命令行参数来管理客户端的行为。

TLS 加密控制

control.go 中定义了 tlsEnable1 变量以及 SetTlsEnable()GetTlsEnable() 函数,用于控制客户端与服务端之间是否启用 TLS 加密。这为用户提供了在客户端层面配置加密传输的灵活性。

GetTaskStatus():获取任务状态

GetTaskStatus() 函数用于获取客户端上配置的隧道和主机任务的运行状态。

工作流程:

  1. 加载配置:从指定的配置文件路径加载客户端配置。
  2. 建立控制连接:创建一个类型为 common.WORK_CONFIG 的连接到服务端。
  3. 发送状态请求:向服务端发送 common.WORK_STATUS 标志,请求任务状态。
  4. 发送验证信息:发送客户端的 vkey 的 MD5 值进行验证。
  5. 接收状态:从服务端接收任务状态列表。
  6. 打印状态:遍历客户端配置中的主机和隧道任务,根据服务端返回的状态信息打印每个任务的运行状态(“ok”或“not running”)。

这个功能对于用户快速了解客户端上各个隧道和主机的运行情况非常有用。

StartFromFile():从配置文件启动客户端

StartFromFile() 函数是客户端从配置文件启动的主要入口。它负责加载配置、与服务端建立连接、发送配置信息以及启动本地服务。

工作流程:

  1. 加载配置:从指定的配置文件路径加载客户端配置 (config.Config)。
  2. 连接重试:在一个循环中尝试连接服务端,如果连接失败,则根据 AutoReconnection 配置进行重试。
  3. 建立主控制连接:通过 NewConn() 函数建立与服务端的主控制连接。
  4. 处理公共 vkey:如果配置了公共 vkey,客户端会向服务端发送其全局配置,并获取服务端分配的临时 vkey
  5. 持久化 vkey:将获取到的 vkey 写入 npc_vkey.txt 文件,以便后续使用。
  6. 发送主机配置:遍历配置文件中的主机 (cnf.Hosts),通过 c.SendInfo(v, common.NEW_HOST) 将每个主机配置发送给服务端。
  7. 发送隧道任务:遍历配置文件中的隧道任务 (cnf.Tasks),通过 c.SendInfo(v, common.NEW_TASK) 将每个隧道任务发送给服务端。
    • 如果隧道模式是 file,还会启动一个本地文件服务器 (startLocalFileServer)。
  8. 启动本地服务:遍历配置文件中的本地服务 (cnf.LocalServer),启动本地监听 (StartLocalServer)。
  9. 打印登录信息:根据配置打印 Web 访问的用户名和密码。
  10. 启动 TRPClient:最后,创建一个新的 TRPClient 实例并调用其 Start() 方法,启动客户端的核心逻辑。

NewConn():创建与服务端的连接

NewConn() 函数负责创建客户端与服务端之间的底层连接,并进行版本验证和密钥验证。

核心逻辑:

  1. 选择连接类型:根据 tp 参数(tcp 或其他,如 KCP)选择不同的连接方式。
    • TCP 连接
      • 如果配置了 proxyUrl,则通过 SOCKS5 代理或 HTTP 代理建立连接。
      • 如果启用了 TLS (GetTlsEnable()),则建立 TLS 连接。
      • 否则,建立普通的 TCP 连接。
    • KCP 连接:通过 kcp.DialWithOptions() 建立 KCP 连接。
  2. 设置连接超时:为连接设置 10 秒的读写超时。
  3. 发送连接测试:发送 common.CONN_TEST 标志。
  4. 版本验证:发送客户端版本信息,并与服务端返回的版本信息进行比对。
  5. 密钥验证:发送客户端的 vkey 的验证值,并接收服务端的验证结果。如果验证失败,则返回错误。
  6. 发送连接类型:发送连接类型(connType,如 WORK_MAINWORK_CONFIGWORK_CHAN)。
  7. 设置连接活跃状态:调用 c.SetAlive(tp) 设置连接的活跃状态。

NewHttpProxyConn():HTTP 代理连接

NewHttpProxyConn() 函数用于通过 HTTP 代理建立连接。它会发送一个 CONNECT 请求到 HTTP 代理服务器,然后通过代理服务器建立与目标地址的隧道。

P2P UDP 打洞辅助函数

control.go 中还包含了一系列 P2P UDP 打洞相关的辅助函数,这些函数在 client.gonewUdpConn() 中被调用:

  • getRemoteAddressFromServer():向 P2P 服务器发送 UDP 数据包,包含客户端的 P2P 密码和角色信息。
  • handleP2PUdp():处理 P2P UDP 打洞的核心逻辑。它会监听本地 UDP 端口,向 P2P 服务器发送探测包,并尝试接收来自 P2P 服务器的对方地址信息。
  • sendP2PTestMsg():在 P2P 打洞成功后,向对方发送测试消息,并等待对方的确认。
  • newUdpConnByAddr():根据地址创建一个 UDP 连接。
  • getNextAddr():根据给定的地址和偏移量,计算下一个地址(用于 P2P 打洞时的端口探测)。
  • getAddrInterval():计算两个地址之间的端口间隔。
  • getRandomPortArr():生成一个随机的端口数组。

这些辅助函数共同协作,实现了客户端在复杂网络环境下进行 P2P UDP 打洞的能力。

总结

nps/client/control.go 文件是 NPS 客户端的重要辅助模块,它提供了客户端的启动、配置加载、任务状态查询以及 P2P UDP 打洞等高级控制功能。通过这些功能,NPS 客户端能够灵活地适应不同的部署环境和网络条件,并与服务端协同工作,实现高效稳定的内网穿透。

在下一篇文章中,我们将继续探索 NPS 客户端的其他辅助模块,例如 health.goregister.go