I have the following code to get information about the tcp ports:
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)
}
I now want to convert the buffer into something more "useful". I read that the return value is a struct called "xinpgen". How can I convert the buffer into that struct?
I tried the following code to directly write the result into the struct variable:
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)
}
The call itself doesn't fail and seems to be successful. The variable contains some data that isn't zero. But shortly after the call is finished and the program continues, the app crashes:
error: memory read failed for 0x0
How can I use the buffer to fill the struct variable? And why does the second call fail?
The data returned by sysctlbyname("net.inet.tcp.pcblist", ...)
is not a single xinpgen
structure, but a "packed list" of
structures.
Your code writes more bytes to the memory address of the input
variable than its size, the behaviour is undefined and a crash very
likely.
I don't know if the structure of the returned data is documented, but
the source code of inet.c shows how it can be parsed.
Apparently the buffer starts with a struct xinpgen
, followed
by a variable number of struct xtcpcb
, and each element has
a length field which contains the offset to the next structure.
This is my attempt to translate the C code from the above source file to 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)
}
}