【问题标题】:How to get a string from a bytes array?如何从字节数组中获取字符串?
【发布时间】:2021-05-02 03:56:07
【问题描述】:

我正在创建自己的 DNS 服务器和主机拦截器,我想从 DNS 请求消息 byte[] 中获取主机

dns 消息十六进制转储:

e07901000001000000000000057961686f6f03636f6d0000010001
.y...........yahoo.com.....

代码:

using System;
using System.Text;

public class Program
{
    public static void Main()
    {
        string b64 = "4HkBAAABAAAAAAAABXlhaG9vA2NvbQAAAQAB";
        int pad = b64.Length % 4;
        if (pad > 0 )
        {
            b64 += new string('=', 4 - pad);
        }
        byte[] decoded = Convert.FromBase64String(b64);
        int start = 13;
        int end = start;
        while(decoded[end] != 0){
            end++;
        }
        
        int hostLength = end-start;
        byte[] byteHost = new byte[hostLength];
        Array.Copy(decoded, start, byteHost, 0, hostLength);
        string host = Encoding.Default.GetString(byteHost);
        Console.WriteLine(host); // yahoo♥com
    }
}

问题:

  1. 我上面的方法是正确/高效/最快地获取主机名吗?
  2. 为什么我得到奇怪的字符替换点 yahoo♥com ?

更改为 Encoding.ASCII 或 Encoding.UTF8 无效

【问题讨论】:

  • 供参考:编写 DNS 服务器作为学术练习可能会很有趣,但我不会为“真实事物”这样做 - 只是得到一个 pi-hole;它具有内置的自定义主机阻塞等
  • 学习用的????
  • 使用它是否符合您的目标并提供预期的结果:new string(decoded.Select(b => (char)b).Where(char.IsLetterOrDigit).ToArray())?你可以用任何逻辑替换IsLetterOrDigit。
  • @OlivierRogier 它将删除点。
  • @uingtea 您的代码输出yahoo♥com 和sn-p àyyahoocom。你实际上期望什么? yahoo.com ?

标签: c# arrays string byte


【解决方案1】:
  1. 不需要第二个数组; Encoding.GetString 允许您传入偏移量和计数,因此:GetString(decoded, start, hostLength)
  2. 从不使用Encoding.Default;那个名字不好 - 它应该被称为Encoding.Wrong :) 找出数据的编码方式(可能是 UTF-8 或 ASCII),然后使用它
  3. 您应该可以使用IndexOf 找到终止'\0';还要考虑你的代码在找不到时应该怎么做

至于不寻常的字符:数据包含一个 03 字节,您会期望 .;检查 DNS 协议规范,看看这是否是预期的。 03 是 ETX(文本结束)。除此之外:我不知道。

【讨论】:

  • 谢谢,关于IndexOf 我尝试Array.IndexOf(decoded, (byte)0); 但它返回3 而不是18
  • @uingtea 你也可以提供起始偏移量
【解决方案2】:

找到答案了,03不是ETX而是下一个字符串的长度,看例子

00 05 79 61 68 6F 6F 03 63 6F 6D
.  .  y  a  h  o  o  .  c  o  m

05 表示yahoo 的长度,03 表示com

有效的主机名或域名仅包含 44-127 或 [a-z0-9-\.] 的 ASCII 范围,bücher.nu 之类的域将转换为xn--bcher-kva.nu,因此我将03,0C,09 之类的字节替换为点@987654332 或小于44 @

感谢@Marc Gravell 提供GetString(decoded, start, hostLength)

/*
I0sBAAABAAAAAAAABmMtcmluZwZtc2VkZ2UDbmV0AAABAAE
ldgBAAABAAAAAAAABWZwLXZwCWF6dXJlZWRnZQNuZXQAAAEAAQ
4HkBAAABAAAAAAAABXlhaG9vA2NvbQAAAQAB
*/
string b64 = "4VoBAAABAAAAAAAAIGYyNWIzNjgyMGUyNDljNGQxY2I0YzQzNGUxNjc5YTljA2Nsbwxmb290cHJpbnRkbnMDY29tAAABAAE";
int pad = b64.Length % 4;
if (pad > 0)
{
    b64 += new string ('=', 4 - pad);
}

byte[] decoded = Convert.FromBase64String(b64);
int start = 13;
int end = start;
while (decoded[end] != 0)
{
    if(decoded[end] < 44)
        decoded[end] = 0x2e;
    end++;
}

int hostLength = end - start;
string host = Encoding.ASCII.GetString(decoded, start, hostLength);
Console.WriteLine(host);

编辑:微优化,使用 1e9 或 10 亿循环进行基准测试

Convert.ToChar() 于 00:00:04 结束

for(int i =0; i<1e9; i++){
    while (decoded[end] != 0)
    {
        if(decoded[end] < 44)
            decoded[end] = 0x2e;
        host += Convert.ToChar(decoded[end]);
        end++;
    }
}

VS Encoding.ASCII.GetString() 于 00:03:20(200 秒)完成

for(int i =0; i<1e9; i++){
    while (decoded[end] != 0)
    {
        if(decoded[end] < 44)
            decoded[end] = 0x2e;
        end++;
    }
    int hostLength = end - start;
    string host = Encoding.ASCII.GetString(decoded, start, hostLength);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-31
    • 1970-01-01
    • 2019-09-06
    • 2015-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多