服务器运维面:RCON、查询、Steam 认证、封禁、纯净服务器与下载
本页解释服务器上与“玩法”无关、但决定一台服务器能否被找到、被管理、被安全连接的那部分引擎代码:远程控制台(RCON)的认证与失败封禁、无连接查询(A2S)与限流、Steam 游戏服务器的登录与心跳、客户端 Steam 票据的校验与踢出、IP/SteamID 封禁列表、sv_pure 文件一致性校验、资源下载,以及 net_ws.cpp 中的 Steam 数据报中继与通道加密密钥交换。连接握手与签入流程本身见 网络通道、协议与连接,这里只补充握手中与认证、加密相关的分支。
相关页面:网络通道与协议 · 专用服务器启动链 · 输入与命令(命令缓冲与权限) · 文件系统与资源缓存 · 匹配框架与 Game Coordinator · Demo 与 GOTV
相关源码文件#
- RCON:
engine/sv_rcon.cpp、engine/sv_rcon.h、engine/sv_remoteaccess.cpp、common/igameserverdata.h、engine/socketcreator.cpp、engine/cl_rcon.cpp - 无连接包与查询:
engine/baseserver.cpp、common/proto_oob.h、engine/sv_ipratelimit.cpp、engine/host_cmd.cpp - Steam 游戏服务器:
engine/sv_steamauth.cpp、engine/sv_master.cpp - 封禁:
engine/sv_filter.cpp - 纯净服务器:
engine/sv_main.cpp、engine/pure_server.cpp、engine/sv_client.cpp、engine/client.cpp、engine/cl_main.cpp - 下载:
engine/download.cpp、engine/net_chan.cpp - 传输与加密:
engine/net_ws.cpp、engine/net_steamsocketmgr.cpp、engine/baseclientstate.cpp、public/steamdatagram/isteamdatagramserver.h
1. 总览:一个服务器端口上的几类流量#
证据:服务器每帧 RunFrame 先处理套接字,再检查超时、更新主服务器:engine/baseserver.cpp:3261-3275;NET_ProcessSocket 中先按 IP 过滤并回送封禁提示,再区分无连接包与通道包:engine/net_ws.cpp:2003-2043、engine/sv_filter.cpp:35-38;通道包在接收阶段按通道密钥做 ICE 解密:engine/net_ws.cpp:1555-1578;无连接包分派:engine/baseserver.cpp:895-1420;RCON 在 NET_RunFrame 中驱动:engine/net_ws.cpp:3640-3648。
2. RCON:远程控制台#
2.1 监听与协议#
- 只有专用服务器或带
-usercon的进程才创建 RCON 监听,地址取本机地址 + 服务器 UDP 端口号:engine/net_ws.cpp:3734-3741。监听由CSocketCreator实现,是 TCP 流套接字:engine/sv_rcon.h:83、engine/socketcreator.cpp:78-106。 - 每个已接受的连接分配一个
listenerID,初始未认证:engine/sv_rcon.cpp:190-197。 - 帧格式为“4 字节长度 + 负载”;单个请求超过
RCON_MAX_RCON_COMMAND_LEN(1024 字节)时按认证失败计数并关闭连接:engine/sv_rcon.cpp:280-321、engine/sv_rcon.h:25。 - 负载是
requestID + requestType + 两个字符串,类型枚举为SERVERDATA_*:engine/sv_remoteaccess.cpp:96-116、common/igameserverdata.h:45-66。
证据:分包后在 SV_RedirectStart/End 之间交给远程访问层:engine/sv_rcon.cpp:323-327;认证分支 engine/sv_remoteaccess.cpp:228-241、engine/sv_remoteaccess.cpp:435-457;错误密码分支 engine/sv_remoteaccess.cpp:474-502;执行分支 engine/sv_remoteaccess.cpp:187-226、engine/sv_remoteaccess.cpp:649-654。
2.2 认证规则#
| 规则 | 证据 |
|---|---|
rcon_password 为空时任何密码都不通过(即 RCON 关闭) | engine/sv_rcon.cpp:153-161 |
修改 rcon_password 会关闭全部已接受连接,迫使重新认证;该 ConVar 带 FCVAR_SERVER_CANNOT_QUERY 与 FCVAR_DONTRECORD | engine/sv_rcon.cpp:81-91、engine/sv_rcon.cpp:142-146 |
认证状态按连接保存;EXECCOMMAND、REQUESTVALUE、SETVALUE、远程 bug 提交都先检查 IsAuthenticated | engine/sv_remoteaccess.cpp:118-120、engine/sv_remoteaccess.cpp:152-154、engine/sv_remoteaccess.cpp:189、engine/sv_remoteaccess.cpp:342、engine/sv_remoteaccess.cpp:462-466 |
未认证的 EXECCOMMAND 只记录 Bad Password,不计入失败次数;只有 AUTH 失败与超长请求会计数 | engine/sv_remoteaccess.cpp:210-224、engine/sv_rcon.cpp:316-321 |
SETVALUE 的 map 被翻译为 changelevel,mapcycle 会改写地图循环文件,其余拼成 <var> <value> 进入 CBUF_SERVER | engine/sv_remoteaccess.cpp:606-644 |
2.3 失败封禁#
HandleFailedRconAuth 按来源 IP(不含端口)记录失败:engine/sv_rcon.cpp:527-626
sv_rcon_whitelist_address命中的地址永不封禁;最多跟踪 32 个地址,满了淘汰最早失败的;每个地址最多保留 20 个失败时间戳。- 累计失败次数超过
sv_rcon_maxfailures(默认 10),或sv_rcon_minfailuretime(默认 30 秒)内的失败超过sv_rcon_minfailures(默认 5),即执行addip <sv_rcon_banpenalty> <ip>;sv_rcon_banpenalty默认为 0,而addip的 0 分钟表示永久:engine/sv_rcon.cpp:501-505、engine/sv_filter.cpp:145-149。 - 封禁成功时
BadPassword关闭该连接,否则回一个id=-1的认证失败响应:engine/sv_remoteaccess.cpp:480-501。
客户端侧的 rcon 命令通过 CRConClient 建立 TCP 连接并发送命令:engine/cl_main.cpp:2380、engine/cl_rcon.cpp:319、engine/cl_rcon.cpp:709-715。
3. 无连接查询(A2S)与限流#
限流在最前面:任何无连接包先经过 CheckConnectionLessRateLimits,按单 IP(默认平均 10 次/秒、30 秒窗口)和全局(默认 500 次/秒)限流,超限丢弃;线程化套接字泵在收包线程里也做同样检查:engine/baseserver.cpp:903-906、engine/sv_ipratelimit.cpp:22-26、engine/sv_ipratelimit.cpp:82-97、engine/net_steamsocketmgr.cpp:257-263。
服务器未激活时:专用服务器丢弃全部无连接包,监听服务器只处理首字节为 0 的局域网匹配包:engine/baseserver.cpp:912-922。
| 请求(首字节) | 行为 | 证据 |
|---|---|---|
A2S_INFO('T') | 仅当 Steam 心跳已启用时处理;host_info_show=1(默认)由引擎自己回一个精简的 S2A_INFO_SRC:协议号 17、主机名(host_name_store 为假时换成游戏名)、地图、csgo、AppID、玩家/最大真人/Bot 数、d/l、操作系统、是否有密码、是否 VAC 安全、版本号,以及游戏端口、GOTV 端口与名称、游戏标签、GameID;=2 转交 Steam;=0 不回复 | engine/baseserver.cpp:1218-1346、engine/host_cmd.cpp:111-113 |
A2S_PLAYER('U') | host_players_show=1(默认)只回一条伪记录“Max Players”(值为最大真人数,附运行时长),不暴露真实玩家;=2 转交 Steam | engine/baseserver.cpp:1348-1383 |
A2S_RULES('V') | host_rules_show 为真时转交 Steam,否则不回复 | engine/baseserver.cpp:1385-1392 |
A2S_SERVERQUERY_GETCHALLENGE('W') | 回复查询挑战值 | engine/baseserver.cpp:946-947 |
A2S_GETCHALLENGE / C2S_CONNECT | 连接握手(还没拿到 Steam 登录结果时丢弃挑战请求),见第 9 节与 网络通道 | engine/baseserver.cpp:936-944、engine/baseserver.cpp:965-1176 |
A2S_PING、A2S_RESERVE_CHECK 等 | 版本匹配时回 S2A_PING_RESPONSE;预约检查交给匹配逻辑 | engine/baseserver.cpp:1186-1207 |
| 其他 | 只有 -enablelegacypackets 时转交 Steam 的 HandleIncomingPacket | engine/baseserver.cpp:1394-1418 |
“挑战”其实是固定字符串:A2S_INFO 的校验 ValidInfoChallenge 只比较请求附带的字符串是否等于 A2S_KEY_STRING("Source Engine Query"),sv_enableoldqueries(默认 0)为真时连这一步也跳过:engine/baseserver.cpp:875-889、engine/baseserver.cpp:838-846、engine/baseserver.cpp:195、common/proto_oob.h:130。数字挑战值 CheckChallengeNr(按 IP 记录、有效期 CHALLENGE_LIFETIME)用于连接请求:engine/baseserver.cpp:370-400、engine/baseserver.cpp:561-565。
推断:因为
A2S_INFO/PLAYER/RULES都以g_bSteamMasterHeartbeatsEnabled为前提,局域网(sv_lan 1)或带-nomaster的服务器不会回答这些查询;该标志只在主服务器更新逻辑中被赋值(engine/baseserver.cpp:3148-3156)。
4. Steam 游戏服务器:激活、登录与心跳#
| 阶段 | 行为 | 证据 |
|---|---|---|
| 何时激活 | SV_ActivateServer 中,多人游戏或游戏 DLL 偏好 Steam 认证时调用 Steam3Server().Activate(),随后设置查询端口与服务器标签;模式未变时重复调用直接返回 | engine/sv_main.cpp:2568、engine/sv_main.cpp:2675-2687、engine/sv_steamauth.cpp:156-163 |
| 选择模式 | sv_lan → 无认证;不允许安全模式或 -insecure → 仅认证;否则认证 + VAC 安全 | engine/sv_steamauth.cpp:112-132、engine/sv_main.cpp:253 |
| 端口与地址 | 默认 Steam 端口 26900(-steamport 覆盖),ip/ip_steam 指定网卡;sv_master_share_game_socket(默认 1)时主服务器更新复用游戏套接字,查询端口即游戏端口 | engine/sv_steamauth.cpp:171-226、engine/sv_steamauth.cpp:63-64 |
| 初始化失败 | SteamGameServer_InitSafe 失败、NO_STEAM 构建或 Xbox 专用服务器:退回无认证并强制 sv_lan 1 | engine/sv_steamauth.cpp:228-243、engine/sv_steamauth.cpp:268-288 |
| 登录 | 未设置 sv_setsteamaccount 令牌时匿名登录(专用服务器会打印“仅限局域网”警告),否则用令牌持久账号登录;登录后开始监听 Steam 数据报中继 | engine/sv_steamauth.cpp:312-349、engine/sv_steamauth.cpp:1150-1167 |
| 回调泵 | CSteam3Server::RunFrame 至多每 0.1 秒调用一次 SteamGameServer_RunCallbacks;有无玩家变化时推送服务器详情 | engine/sv_steamauth.cpp:1037-1056、engine/sv_main.cpp:3492 |
| 心跳 | UpdateMasterServer 每帧转发主服务器更新器的待发包,并每 2 秒检查一次:活动的多人非单人服务器(无 -nomaster、未隐藏)才 EnableHeartbeats(true),同时刷新规则(仅 FCVAR_NOTIFY 变量)、玩家与详情;之后通知游戏 DLL 更新 GC 信息 | engine/baseserver.cpp:3114-3168、engine/baseserver.cpp:3170-3190 |
| 手动心跳 | heartbeat 命令调用 ForceHeartbeat(-fork 的父进程不发布自己);sv_search_key 变化时自动触发 | engine/sv_master.cpp:25-60 |
| 过期提示 | Steam 要求重启时提示服务器过期;Valve 官方服务器在非 -noautoupdate 时直接 sv_shutdown | engine/baseserver.cpp:3068-3112 |
5. 客户端 Steam 票据校验与踢出#
证据与要点:
ConnectClient依次检查协议、挑战值、仅限大厅/预约、监听服务器的背景图/单人限制、局域网 IP 限制(仅sv_lan时要求同 B 类网段或私有地址)与服务器密码:engine/baseserver.cpp:553-620、engine/baseserver.cpp:2784-2813。CheckChallengeType:拒绝非法认证协议;Steam 协议要求票据长度在(0, STEAM_KEYSIZE)内,然后调用NotifyClientConnect,失败且非局域网模式时拒绝连接:engine/baseserver.cpp:2713-2782、engine/baseserver.cpp:677-681。NotifyClientConnect:从票据前 8 字节取出 SteamID 后BeginAuthSession,无效票据、重复请求、版本不符、游戏不符、过期票据都拒绝;队列匹配预约(sv_mmqueue_reservation以Q开头)要求账号在名单中;用户信息里的accountid与经 SDR 代理的地址都必须与票据账号一致;SDR 客户端直接标记为已完全认证:engine/sv_steamauth.cpp:870-949。- 后端回调
OnValidateAuthTicketResponse:结果不是k_EAuthSessionResponseOK时交给失败处理并直接返回;结果为 OK 时,若 SteamID 在封禁列表中则拒绝并断开,SteamID 重复则断开,否则记录日志并通知插件与游戏 DLL,最后统一SetFullyAuthenticated:engine/sv_steamauth.cpp:687-736。注释说明“收不到回调时默认视为通过”,即客户端在回调到达前保持连接:engine/sv_steamauth.cpp:689-692。 - 失败处理
OnValidateAuthTicketResponseHelper:超时中的客户端只报超时;“在别处登录”“VAC 封禁”在非局域网模式断开;“未拥有游戏”直接断开;“未连接 Steam / 票据取消 / 票据无效”交给OnInvalidSteamLogonErrorForClient;VAC 检查超时视sv_steamauth_enforce而定:engine/sv_steamauth.cpp:780-834。 sv_steamauth_enforce(默认 2):对已完全认证的客户端,0 不踢,1 询问游戏 DLL(InvalidSteamLogon键值命令,用于“下次重生时踢”),2 立即踢;局域网模式永不因此踢人:engine/sv_steamauth.cpp:68-70、engine/sv_steamauth.cpp:742-773。- 断开时对真实账号调用
EndAuthSession,对匿名账号(Bot、直连 GOTV)发送SendUserDisconnect:engine/sv_steamauth.cpp:974-1008。
6. 封禁列表#
| 命令/机制 | 行为 | 证据 |
|---|---|---|
| IP 过滤生效点 | 每个收到的数据报先过 Filter_ShouldDiscard;sv_filterban=1(默认)为黑名单模式,=0 关闭,其他值为白名单模式;过期条目在检查时顺带删除 | engine/sv_filter.cpp:21、engine/sv_filter.cpp:45-78、engine/net_ws.cpp:2005-2009 |
addip <分钟> <ip>(别名 banip) | 0 分钟为永久;新增条目时踢掉第一个匹配的在线真人玩家并触发 server_addban 事件,原地更新已有条目则不踢;列表有上限 MAX_IPFILTERS | engine/sv_filter.cpp:131-175、engine/sv_filter.cpp:193-254 |
writeip | 只把永久 IP 封禁写成 cfg/banned_ip.cfg 中的 addip 0 x.x.x.x 行 | engine/sv_filter.cpp:388-423 |
banid <分钟> <userid 或 SteamID> [kick] | 局域网模式拒绝;支持 STEAM_x:y:z 与 STEAM64BITID_ 前缀;触发 server_addban 游戏事件,带 kick 且玩家在线时踢出 | engine/sv_filter.cpp:698-760、engine/sv_filter.cpp:882-926 |
writeid | 只把永久 SteamID 封禁写成 cfg/banned_user.cfg 中的 banid 0 <id> 行 | engine/sv_filter.cpp:508-539 |
| SteamID 封禁生效点 | 不在握手时检查,而是在 Steam 票据校验成功回调中检查 Filter_IsUserBanned | engine/sv_steamauth.cpp:707-711、engine/sv_filter.cpp:430 |
| 预约清空 | 服务器被匹配预约(非 R 类负载)时执行 removeallids | engine/vengineserver_impl.cpp:1600-1607 |
谁加载 banned_user.cfg / banned_ip.cfg? 引擎只定义文件名并负责写出:engine/sv_filter.cpp:26-28;Filter_Init 是空函数:engine/sv_filter.cpp:934-936;全仓库没有执行这两个文件的代码。按 Source 惯例由 server.cfg 中的 exec 加载(推断,仓库不含 cfg 内容,证据不足)。
7. sv_pure 与文件一致性校验#
证据与要点:
sv_pure是命令而不是 ConVar,值保存在静态g_sv_pure_mode(默认 1);若命令行把它设为 0,之后不能再在控制台开启;2被视为过时并改为 1;修改在下一张地图生效:engine/sv_main.cpp:174-240。ReloadWhitelist:监听服务器强制关闭纯净模式;单人或模式 0 时不建白名单;否则加载全局pure_server_whitelist.txt与maps/<地图>_whitelist.txt:engine/sv_main.cpp:2776-2840。白名单编码后放入启动字符串表:engine/sv_main.cpp:697-705;专用服务器ModInit时开启文件系统的白名单跟踪(见 文件系统)。- 客户端从字符串表解码白名单;单人、HLTV、回放时不处理:
engine/cl_main.cpp:1486-1542。进入SIGNONSTATE_FULL后,CheckFileCRCsWithServer每 0.2 秒最多发送 5 个文件系统报告的“未验证文件哈希”:engine/client.cpp:1844-1885。 - 服务端
CLCMsg_FileCRCCheck:不在纯净模式时忽略;交给g_PureFileTracker.DoesFileMatch;不匹配时通知游戏 DLLOnPureServerFileValidationFailure,sv_pure_kick_clients(默认 1)为真则断开,否则只警告:engine/sv_client.cpp:421-483、engine/sv_main.cpp:247。 DoesFileMatch的判定顺序:不在 VPK 中且不在“强制匹配”列表的文件直接通过(由服务器决定是否白名单,客户端无法把修改文件藏进白名单目录);服务器认为应在 VPK 中却以散文件出现则失败;双方都有整文件哈希时直接比较;VPK 内未识别的文件失败;有主哈希时与之比较;都没有时记录下来等待“共识”,暂时判为通过:engine/pure_server.cpp:617-778。
8. 资源下载#
| 环节 | 行为 | 证据 |
|---|---|---|
| 可下载清单 | 服务器的 downloadables 字符串表(建表见 网络通道) | engine/sv_main.cpp:587-656 |
| 何时下载 | 客户端在 SIGNONSTATE_NEW 等待 Steam 资源就绪后遍历清单;cl_downloadfilter 可设为 none/nosounds;创意工坊地图的 .bsp 改走 UGC 下载;队列非空时显示进度并暂停签入,否则继续 FinishSignonState_New | engine/client.cpp:1624-1641、engine/client.cpp:1663-1790、engine/client.cpp:61 |
| HTTP 还是通道 | CL_QueueDownload 以 sv_downloadurl(FCVAR_REPLICATED)为基址;以 http:///https:// 开头时先尝试下载 .bz2 压缩版本并在线程中进行,否则通过网络通道 RequestFile;本机就是服务器或文件已存在时跳过 | engine/download.cpp:589-649、engine/download.cpp:846-874、engine/download.cpp:953-957、engine/sv_main.cpp:261 |
| 服务器响应 | CGameClient::FileRequested:sv_allowdownload(默认 1)为真则 SendFile,否则 DenyFile;上传开关为 sv_allowupload | engine/sv_client.cpp:2168-2180、engine/sv_client.cpp:52-53 |
| 文件名安全检查 | IsValidFileForTransfer:拒绝绝对路径与 ..、cfg/、scripts/、addons/ 等目录、maps/ 下除 .bsp/.ain/.nav 外的文件、扩展名含空白,以及 .cfg/.exe/.dll/.bat/.vdf/... 等扩展名;队列、发送与接收三处都调用 | engine/net_chan.cpp:3585-3662、engine/download.cpp:591-592、engine/net_chan.cpp:627、engine/net_chan.cpp:2268 |
| 大小限制 | 通道上传上限 net_maxfilesize(默认 16 MB,最大 64) | engine/net_chan.cpp:52 |
9. 传输层:Steam 数据报中继与通道加密#
9.1 Steam 数据报中继(SDR)与线程化套接字#
- 地址类型除普通 IP 外还有
NSAT_PROXIED_GAMESERVER(客户端看到的经中继的服务器)与NSAT_PROXIED_CLIENT(服务器看到的经中继的客户端)。发送时分别走ISteamDatagramTransportClient::SendDatagram与ISteamDatagramTransportGameserver::SendDatagram:engine/net_ws.cpp:2097-2133。 - 服务器端:
sv_steamdatagramtransport_port非 0 时,Steam 登录后SteamDatagram_GameserverListen打开一个中继接口,失败则把该 ConVar 清零以免对外宣称:engine/net_ws.cpp:95、engine/net_ws.cpp:4450-4474、engine/sv_steamauth.cpp:349。接收时普通套接字没有数据才轮询中继,并按服务器、GOTV、GOTV1 三个通道区分来源:engine/net_ws.cpp:1380-1421。 - 客户端:
NET_InitSteamDatagramProxiedGameserverConnection复用或新建到目标服务器 SteamID 的中继连接;数据报客户端库按-perfectworld选择合作方配置:engine/net_ws.cpp:3015-3040、engine/net_ws.cpp:2950-2983、engine/net_ws.cpp:1423-1437。 - 中继库只以头文件与预编译库形式存在(
public/steamdatagram/、lib/public/*steamdatagramlib*),实现不在仓库中:public/steamdatagram/isteamdatagramserver.h:18。 - 另有一个与 SDR 无关的线程化套接字泵:默认启用(
-nothreadedsockets关闭),收包线程对无连接包做限流,对通道包按客户端做突发上限:engine/net_steamsocketmgr.cpp:21-29、engine/net_steamsocketmgr.cpp:65、engine/net_steamsocketmgr.cpp:225-275。
9.2 通道加密密钥交换#
证据:
- 证书来自
-certificate <文件>:文件头必须是CSv1,目录项依次给出公钥、签名、私钥的偏移与长度;任何格式错误都直接退出进程:engine/net_ws.cpp:4807-4864。指定-certificate或作为 Valve 官方服务器运行时,缺证书即退出:engine/host.cpp:5854-5869。登录 Steam 后服务器用自己的公网 IP 自检证书,失败同样退出(-ignore_certificate_address跳过):engine/sv_steamauth.cpp:503-528。 - 服务器在挑战回复中写入“是否要求加密通道”与公钥、签名:
engine/baseserver.cpp:1849-1866。 - 客户端用内置主公钥验证签名:被签名的数据是“服务器公钥 +
IP/掩码位”,掩码位从 32 递减到 16 逐个尝试;通过后用随机数生成会话密钥并以服务器公钥做 RSA-OAEP 加密:engine/net_ws.cpp:4562-4660。客户端把密钥按递增索引保存,验签失败则断开:engine/baseclientstate.cpp:1763-1806。 - 服务器用私钥解密并检查长度;插件可通过
-externalnetworkcryptkey接管密钥管理;sv实例持有私钥但未得到客户端密钥时拒绝连接:engine/baseserver.cpp:1068-1151、engine/net_ws.cpp:4715-4805。 - 有密钥的通道在发送时附加随机填充并按 ICE 块加密,接收时先解密:
engine/net_ws.cpp:2690-2700、engine/net_ws.cpp:1555-1578;密钥在创建通道时传入:engine/net_ws.cpp:755-786。 - 本快照中主公钥被置空(
Removed for partner depot),只记录其存在:engine/net_ws.cpp:4567。因此本仓库构建出的客户端无法验证任何要求加密的服务器证书,带-certificate的服务器在 Steam 登录后的自检也会失败退出(推断,依据是验签以该常量为唯一信任根)。
约束与边界#
- 裁剪:主服务器 Steam 详情中“官方地图”判断被删空(
Removed for partner depot),bOfficialMap恒为假:engine/sv_steamauth.cpp:1106-1108;加密主公钥被置空(见 9.2)。 - 观察:未认证可调用的 RCON 请求:非
DEDICATED构建中,SERVERDATA_TAKE_SCREENSHOT与SERVERDATA_SEND_CONSOLE_LOG分支没有IsAuthenticated检查:engine/sv_remoteaccess.cpp:257-285。客户端进程只有带-usercon(或启用ENABLE_RPT的 RPT 服务器)时才监听,影响范围有限(推断)。 - 观察:挑战回复中的长度未与缓冲比较:客户端把服务器给出的公钥/签名长度直接用于读入 1024 字节的栈缓冲,只在读完后检查位流是否溢出:
engine/baseclientstate.cpp:1767-1778。是否可被利用取决于bf_read::ReadBytes的越界行为,本次未验证。 - 弱随机源(观察):客户端会话密钥用以当前时间为种子的
RandomInt生成,而不是密码学随机数:engine/net_ws.cpp:4642-4648。 - 认证的乐观默认:握手阶段只要
BeginAuthSession被受理就放行,真正的校验结果异步到达;“已完全认证”标志只在回调为 OK 时设置(SDR 客户端在握手时即设置),而sv_steamauth_enforce的宽限只适用于已完全认证的客户端,未完全认证者出错时总是立即断开:engine/sv_steamauth.cpp:701-735、engine/sv_steamauth.cpp:943-944、engine/sv_steamauth.cpp:747-767。 - 条件编译:
NO_STEAM构建强制局域网模式;ENGINE_CONNECT_VIA_MMS才处理C2S_VALIDATE_SESSION、A2S_RESERVE;ENABLE_RPT才驱动 RPT 服务器:engine/sv_steamauth.cpp:240-243、engine/baseserver.cpp:949-963、engine/baseserver.cpp:1181-1184、engine/net_ws.cpp:3646-3648。 - 本页未展开:日志系统(
sv_log.cpp、log_address)、sv_uploaddata.cpp的统计上报、HLTV/GOTV 中继的认证与tv_password、服务器预约(reservation)与匹配框架交互的细节(见 匹配与 GC)、net_steamsocketmgr.cpp中 Steam P2P 伪套接字(USE_STEAM_SOCKETS)的实现。