将[UInt8]数组转换为xinpgen结构

时间:2017-06-10 13:58:07

标签: swift memory struct memory-layout

我有以下代码来获取有关tcp端口的信息:

var length = 0
if (sysctlbyname("net.inet.tcp.pcblist", nil, &length, nil, 0) < 0)
{
    perror("sysctlbyname")
}
else
{
    var buffer: [UInt8] = [UInt8](repeating: 0, count: Int(length))
    sysctlbyname("net.inet.tcp.pcblist", &buffer, &length, nil, 0)
}

我现在想要将缓冲区转换为更有用的东西&#34;。我读到返回值是一个名为&#34; xinpgen&#34;的结构。 如何将缓冲区转换为该结构?

我尝试使用以下代码直接将结果写入struct变量:

var length = 0
if (sysctlbyname("net.inet.tcp.pcblist", nil, &length, nil, 0) < 0)
{
    perror("sysctlbyname")
}
else
{
    var input = xinpgen()
    sysctlbyname("net.inet.tcp.pcblist", &input, &length, nil, 0)
}

电话本身并没有失败,似乎是成功的。该变量包含一些不为零的数据。但是在通话完成并且程序继续之后不久,应用程序崩溃了:

error: memory read failed for 0x0

如何使用缓冲区填充struct变量?为什么第二次调用会失败?

1 个答案:

答案 0 :(得分:1)

sysctlbyname("net.inet.tcp.pcblist", ...)返回的数据 不是一个xinpgen结构,而是一个&#34;打包列表&#34;的 结构。

您的代码将更多字节写入input的内存地址 变量比它的大小,行为是未定义的,并且非常崩溃 可能的。

我不知道是否记录了返回数据的结构,但是 inet.c的源代码显示了如何解析它。 显然缓冲区以struct xinpgen开头,紧接着 由可变数量的struct xtcpcb,每个元素都有 一个长度字段,包含到下一个结构的偏移量。

这是我尝试从上面的源代码翻译C代码 提交给Swift:

var length = 0
if (sysctlbyname("net.inet.tcp.pcblist", nil, &length, nil, 0) < 0) {
    fatalError("sysctlbyname")
}

var buffer = [UInt8](repeating: 0, count: Int(length))
sysctlbyname("net.inet.tcp.pcblist", &buffer, &length, nil, 0)

buffer.withUnsafeBytes { bufPtr in

    // Pointer to first xinpgen structure:
    var p = bufPtr.baseAddress!
    var xig = p.bindMemory(to: xinpgen.self, capacity: 1)

    // Skip first xinpgen structure:
    p += Int(xig.pointee.xig_len)
    xig = p.bindMemory(to: xinpgen.self, capacity: 1)

    while Int(xig.pointee.xig_len) > MemoryLayout<xinpgen>.size {
        // Cast xig to xtcpcb pointer and derefernce:
        let tcpcb = xig.withMemoryRebound(to: xtcpcb.self, capacity: 1) {
            $0.pointee
        }
        print(tcpcb)

        // Advance pointer to next structure
        p += Int(xig.pointee.xig_len)
        xig = p.bindMemory(to: xinpgen.self, capacity: 1)
    }
}