为AquaDX等服务端使用Cloudflare CDN的思路

发布于

已知信息


AquaDX的官方说明文档中明确表示不支持使用CDN

Why?

通过抓包以及逆向分析得出:

解决方案

Cloudflare设置

首先需要在cloudflare边缘证书中关闭始终使用 HTTPS,设置最低 TLS 版本TLS 1.0

规则-Configuration Rules中设置AuqaDX的主机名,关闭自动 HTTPS 重写;SSL设置为关

新建Compression Rules设置压缩选项自动

PowerOn(Auth Server)

对于Amdaemon的PowerOn请求,我们知道PowerOn服务器用于返回最新的标题服务器地址给游戏,介于服务器返回的内容是一样的,实际负载很小,我们可以将poweron部署在另外一台服务器上,返回静态内容,定时从主服务器上拉最新的内容,这样我们可以使得主服务器的IP不会被泄露,即使PowerOn服务器遭到攻击也不会波及到主服务器

游戏设置

而对于游戏,我们需要对游戏打Patch

csharp
//Aquamai Dev//

private const int BufferSize = 1024;  
  
[HarmonyPrefix]  
[HarmonyPatch(typeof(NetHttpClient), "Decompress")]  
public static bool PreDecompress(NetHttpClient __instance)  
{  
    var traverse = Traverse.Create(__instance);  
    var temporaryStream = traverse.Field<MemoryStream>("_temporaryStream").Value;  
    var memoryStream = traverse.Field<MemoryStream>("_memoryStream").Value;  
    var buffer = traverse.Field<byte[]>("_buffer").Value;  
  
    memoryStream.SetLength(0L);  
    if (temporaryStream.Length == 0L)  
    {  
        return false;  
    }  
  
    var raw = temporaryStream.ToArray();  
   
    //  - 0x1F 0x8B            -> gzip    //  - 0x78 ?? + valid zlib -> zlib (the stock format: zlib header + raw deflate + adler32)    //  - otherwise            -> treat as plaintext    if (raw.Length >= 2 && raw[0] == 0x1F && raw[1] == 0x8B)  
    {  
        using var gz = new GZipStream(new MemoryStream(raw, writable: false), CompressionMode.Decompress);  
        CopyTo(gz, memoryStream, buffer);  
    }  
    else if (raw.Length >= 6 && raw[0] == 0x78 && TryInflateZlib(raw, memoryStream, buffer))  
    {  
        // zlib successfully decompressed  
    }  
    else  
    {  
        memoryStream.Write(raw, 0, raw.Length);  
    }  
  
    memoryStream.Seek(0L, SeekOrigin.Begin);  
    temporaryStream.Seek(0L, SeekOrigin.Begin);  
    temporaryStream.SetLength(0L);  
    return false;  
}  
  
private static bool TryInflateZlib(byte[] raw, MemoryStream output, byte[] buffer)  
{  
    var startLength = output.Length;  
    try  
    {  
        // 跳过 2 字节的 zlib 头部,忽略末尾的 4 字节 Adler32 校验和。
        using var input = new MemoryStream(raw, 2, raw.Length - 6, writable: false);  
        using var deflate = new DeflateStream(input, CompressionMode.Decompress);  
        CopyTo(deflate, output, buffer);  
        return true;  
    }  
    catch  
    {   
        output.SetLength(startLength);  
        return false;  
    }  
}  
  
private static void CopyTo(Stream from, Stream to, byte[] buffer)  
{  
    while (true)  
    {  
        var count = from.Read(buffer, 0, BufferSize);  
        if (count <= 0) break;  
        to.Write(buffer, 0, count);  
    }  
}

由于某些申必原因maimai手台玩家基本上都有装Aquamai,过段时间咱会给aquamai提交这份PR,具体能不能合并到主线呢就不知道了

当然如果你嫌麻烦,可以直接把游戏的Decompress();方法pass掉,然后在cf规则中关闭压缩