' ============================================================
'  应用盾 - 易语言盾模块源码 (集成到游戏/程序中使用, 参考实现)
' ============================================================
'  用途: 集成到你自己的易语言游戏/程序中, 让程序的网络连接
'        走本地盾代理(agent), 隐藏玩家真实IP。
'
'  使用前提:
'    1. 玩家电脑已启动 agent (易语言客户端点"启动盾")
'    2. 本地SOCKS5代理: 127.0.0.1:10800 (无需账号密码, agent会自动登录)
'
'  用法:
'    盾_初始化 ()
'    套接字 ＝ 盾_连接 (“目标IP或域名”, 端口)
'    盾_发送 (套接字, 到字节集 (“数据...”))
'    返回数据 ＝ 盾_接收 (套接字, 4096)
'    盾_关闭 (套接字)
'
'  说明:
'    - 基于 ws2_32 标准接口实现 SOCKS5 客户端握手, 无其他依赖
'    - 游戏若用UDP, 可直接用本地代理端口做SOCKS5 UDP (进阶)
'    - HTTP请求可用精易模块: 网页_访问_对象(网址,..., "127.0.0.1:10800")
' ============================================================

.版本 2
.程序集 盾模块, , 公开

.数据类型 sockaddr_in
    .成员 sin_family, 短整数型
    .成员 sin_port, 短整数型
    .成员 sin_addr, 字节型, , "4"
    .成员 sin_zero, 字节型, , "8"

.DLL命令 盾_WSAStartup, 整数型, "ws2_32.dll", "WSAStartup", 公开
    .参数 wVersionRequested, 短整数型
    .参数 lpWSAData, 整数型
.DLL命令 盾_socket, 整数型, "ws2_32.dll", "socket", 公开
    .参数 af, 整数型
    .参数 type, 整数型
    .参数 protocol, 整数型
.DLL命令 盾_connect, 整数型, "ws2_32.dll", "connect", 公开
    .参数 s, 整数型
    .参数 name, 整数型
    .参数 namelen, 整数型
.DLL命令 盾_send, 整数型, "ws2_32.dll", "send", 公开
    .参数 s, 整数型
    .参数 buf, 整数型
    .参数 len, 整数型
    .参数 flags, 整数型
.DLL命令 盾_recv, 整数型, "ws2_32.dll", "recv", 公开
    .参数 s, 整数型
    .参数 buf, 整数型
    .参数 len, 整数型
    .参数 flags, 整数型
.DLL命令 盾_closesocket, 整数型, "ws2_32.dll", "closesocket", 公开
    .参数 s, 整数型
.DLL命令 盾_htons, 短整数型, "ws2_32.dll", "htons", 公开
    .参数 hostshort, 短整数型

.子程序 盾_初始化, 逻辑型, 公开
    ' 程序启动后调用一次 (0x202 = 请求 Winsock 2.2)
.局部变量 wsadata, 字节集
    wsadata ＝ 取空白字节集 (400)
    .如果真 (盾_WSAStartup (514, 取变量数据地址 (wsadata)) ≠ 0)
        返回 (假)
    .如果真结束
    返回 (真)

.子程序 子程序_IP转4字节, 字节集
.参数 IP文本, 文本型
.局部变量 分段, 文本型, , "0"
.局部变量 结果, 字节集
.局部变量 i, 整数型
    分段 ＝ 分割文本 (IP文本, “.”, )
    结果 ＝ 取空白字节集 (4)
    .计次循环首 (取数组成员数 (分段), i)
        .如果 (i ≤ 4)
            结果 [i] ＝ 到字节 (分段 [i])
        .如果结束
    .计次循环尾
    返回 (结果)

.子程序 子程序_填充sockaddr, sockaddr_in
.参数 IP文本, 文本型
.参数 端口, 整数型
.局部变量 sa, sockaddr_in
.局部变量 ipb, 字节集
.局部变量 i, 整数型
    sa.sin_family ＝ 2                                ' AF_INET
    sa.sin_port ＝ 盾_htons (端口)
    ipb ＝ 子程序_IP转4字节 (IP文本)
    .计次循环首 (4, i)
        sa.sin_addr [i] ＝ ipb [i]
    .计次循环尾
    返回 (sa)

.子程序 盾_连接, 整数型, 公开
' 经本地盾代理(127.0.0.1:10800)连接目标
.参数 目标地址, 文本型
.参数 目标端口, 整数型
.局部变量 s, 整数型
.局部变量 sa, sockaddr_in
.局部变量 buf, 字节集
.局部变量 临时, 字节集
.局部变量 数据, 字节集
.局部变量 域名长度, 整数型
.局部变量 端口高, 字节型
.局部变量 端口低, 字节型
    s ＝ 盾_socket (2, 1, 6)                          ' AF_INET, SOCK_STREAM, TCP
    .如果真 (s ＝ -1)
        返回 (-1)
    .如果真结束
    sa ＝ 子程序_填充sockaddr (“127.0.0.1”, 10800)
    .如果真 (盾_connect (s, 取变量数据地址 (sa), 16) ≠ 0)
        盾_closesocket (s)
        返回 (-1)
    .如果真结束

    ' ── SOCKS5 握手: 本地代理无认证(方法0) ──
    临时 ＝ 到字节集 ({5, 1, 0})
    盾_send (s, 取变量数据地址 (临时), 3, 0)
    buf ＝ 取空白字节集 (2)
    盾_recv (s, 取变量数据地址 (buf), 2, 0)
    .如果真 (buf [1] ≠ 0)
        盾_closesocket (s)
        返回 (-1)
    .如果真结束

    ' ── 连接目标(优先按IP, 否则按域名) ──
    .如果 (子程序_是否为IP (目标地址))
        ' ATYP=1 IPv4
        数据 ＝ 到字节集 ({5, 1, 0, 1}) ＋ 子程序_IP转4字节 (目标地址)
    .否则
        ' ATYP=3 域名
        域名长度 ＝ 取文本长度 (目标地址)
        数据 ＝ 到字节集 ({5, 1, 0, 3}) ＋ 到字节集 (域名长度) ＋ 到字节集 (目标地址)
    .如果结束
    端口高 ＝ 到字节 (右移 (目标端口, 8))
    端口低 ＝ 到字节 (位与 (目标端口, 255))
    数据 ＝ 数据 ＋ 到字节集 ({端口高, 端口低})
    盾_send (s, 取变量数据地址 (数据), 取字节集长度 (数据), 0)

    ' ── 读取应答(4字节头 + 地址) ──
    buf ＝ 取空白字节集 (4)
    盾_recv (s, 取变量数据地址 (buf), 4, 0)
    .如果真 (buf [2] ≠ 0)                              ' REP ≠ 0 表示失败
        盾_closesocket (s)
        返回 (-1)
    .如果真结束
    .判断开始 (buf [4])
    .判断 (1)
        临时 ＝ 取空白字节集 (6)
        盾_recv (s, 取变量数据地址 (临时), 6, 0)
    .判断 (3)
        buf ＝ 取空白字节集 (1)
        盾_recv (s, 取变量数据地址 (buf), 1, 0)
        临时 ＝ 取空白字节集 (buf [1] ＋ 2)
        盾_recv (s, 取变量数据地址 (临时), buf [1] ＋ 2, 0)
    .判断 (4)
        临时 ＝ 取空白字节集 (18)
        盾_recv (s, 取变量数据地址 (临时), 18, 0)
    .默认
        盾_closesocket (s)
        返回 (-1)
    .判断结束
    返回 (s)

.子程序 子程序_是否为IP, 逻辑型
.参数 文本, 文本型
.局部变量 分段, 文本型, , "0"
.局部变量 i, 整数型
.局部变量 n, 整数型
    分段 ＝ 分割文本 (文本, “.”, )
    .如果真 (取数组成员数 (分段) ≠ 4)
        返回 (假)
    .如果真结束
    .计次循环首 (4, i)
        .如果真 (分段 [i] ＝ “”)
            返回 (假)
        .如果真结束
        n ＝ 到整数 (分段 [i])
        .如果真 (n ＜ 0 或 n ＞ 255)
            返回 (假)
        .如果真结束
    .计次循环尾
    返回 (真)

.子程序 盾_发送, 整数型, 公开
.参数 套接字, 整数型
.参数 数据, 字节集
    返回 (盾_send (套接字, 取变量数据地址 (数据), 取字节集长度 (数据), 0))

.子程序 盾_接收, 字节集, 公开
.参数 套接字, 整数型
.参数 最大长度, 整数型, 可空
.局部变量 buf, 字节集
.局部变量 n, 整数型
    .如果真 (最大长度 ＝ 0)
        最大长度 ＝ 65536
    .如果真结束
    buf ＝ 取空白字节集 (最大长度)
    n ＝ 盾_recv (套接字, 取变量数据地址 (buf), 最大长度, 0)
    .如果真 (n ≤ 0)
        返回 ({})
    .如果真结束
    返回 (取字节集左边 (buf, n))

.子程序 盾_关闭, 公开
.参数 套接字, 整数型
    盾_closesocket (套接字)
