Since we've had a lot of problems with XPC (bad design on my part, I'm sure), I tried changing the data communications between the TPP and the userland proxy to use sockets -- in this case (I've so many, many cases), I am trying to do an http proxy (so the TPP connects to, say, port 12345, sends
CONNECT ${host}:${port} HTTP/1.0
X-Proxy-Host: ${host}:${port}
It then reads a response, looking for a 200.
So that part works -- once I added the networking client entitlement, I could connect and write that and read the response. Now we are cooking with gas, right?
The application doing the connection (eg, curl
) then sends the normal HTTP request, the TPP gets it, it writes it to the socket it created, the write succeeds (that is, returns the number of bytes in the request), and...
it doesn't show up on the interface. (Using tcpdump -i lo0 -s 0 -vvvvvvvvvvvvvvvvvvv -A port 12345
.) Since it doesn't show up on the interface, the user-land proxy doesn't get it, and things are very confused for everyone.
If the connect()
failed, I'd say, ah yes, sandboxed to heck and back, even with the entitlement can't do it. Or if the first write()
or read()
failed. But they don't fail, and the first round works. If the second write()
failed, I could see that.
But it both succeeds and doesn't succeed, and quantum confuses the heck out of me.
AH HA!
Honestly, I have no idea if anyone cares about this, but I'm going to document it anyway.
First, I verified that the problem was with my code by writing a very simple proxy. (I still can't see how one uses NERelay
!) After all weekend working on it, I got it all working. I cheated and used ObjC for the proxy code, because doing things with sockets was easier.
Second: having my simple TPP app successfully using a proxy, I took the changes I'd made to my code for the provider over to our real app, and it immediately continued to not work.
After days of working on that, the bulk of my problems were due to my Swift clumsiness.
In one case:
let nwritten = data.withUnsafeBytes { (ptr: UnsafeRawBufferPointer) in
var t = ptr
let count = data.count
let kr = Darwin.write(self.socket, &t, count)
return kr
}
That did a great job of writing the UnsafeRawBufferPointer
to the socket, when what I wanted was, obviously, the data that it pointed to.
Similarly, in another place, I had
var buffer = message.data(using: .ascii)!
let nwritten = Darwin.write(self.socket, &buffer, buffer.count)
and yeah that wasn't right. I changed them both to be variants of
let nwritten = buffer.withUnsafeBytes { ptr in
return Darwin.write(self.socket, ptr, buffer.count)
}
and then AMAZINGLY it all started working!