IPCSocket and the App Sandbox

Anyone else have this working? App works fine until it’s Sandboxed, at that point I get error 106 whenever I try to connect. From within the application I check isWriteable which is true, but isReadable always fails (on the file that the socket points to).

I’ve tried pointing to the temporary folder and a folder within the application support directory.

I’ll be plenty pissed if Apple have broken this (for the App Sandbox), especially given the time I’ve just invested in creating this solution to work around their APIs from hogging the main thread.

[quote=306921:@Sam Rowlands]Anyone else have this working? App works fine until it’s Sandboxed, at that point I get error 106 whenever I try to connect. From within the application I check isWriteable which is true, but isReadable always fails (on the file that the socket points to).

I’ve tried pointing to the temporary folder and a folder within the application support directory.

I’ll be plenty pissed if Apple have broken this (for the App Sandbox), especially given the time I’ve just invested in creating this solution to work around their APIs from hogging the main thread.[/quote]

From prior investigations, I know that UNIX domain sockets (used by IPCSocket) are supported under the sandbox. Have you checked to see if any violations get logged? And how long is the native path for the file you’re using for the socket?

Thanks Joe,
I have checked in the console and there were no violations logged.

When the application is Sandboxed the path does include the applications bundle identifier (I tried both the temp folder and the application support folder). So the length of the path is an issue?

I found some docs on Apple’s site that says I can use a shared “Application Group Container”, I’ve added the code and I will test that.

Someone found if you manually register the port (???) and request the temporary entitlement for using Mach-O services it works; but Apple rejected them for the temporary entitlement.

Forgive me if I am wrong, but my understanding is that IPC (in it’s basic form is a pipe to a file), so in theory I could re-invent the wheel and use fsevents so that the controller & worker apps receive notification when the file is changed.

Do you have any other ideas of a mechanism that would allow these two apps to notify one another when data is written? Ideally I’d like an event based solution, rather than having to have timers running and checking.

Not sure that is how it works. I remember reading that in fact it uses TCPSocket.

I did enable all networking option just invade this was the case, but it didn’t help.

I’m aware from my desk today so it’s a little hard to test the Application Groups.

I did find an article which says that IPC is easily crackable, if that’s the case that I understand why it’s blocked and I suspect that Apple wants us to use XPC instead - which is a totally different ‘bag of pain’ as far as I can tell.

Perhaps you can use a TCP Socket with a LoopbackInterface ?

NetworkInterface.Loopback

Docs say:
Represents the loopback network interface. This interface can be used with sockets to make them listen only for connections originating on the same machine (similar to IPCSockets).

And how are these Loopbacks used? The docs are a bit thin on this.

Create a tcpsocket and assign it like so:

TCPSocket1.NetworkInterface = NetworkInterface.Loopback
TCPSocket.Port = APortNumber

That’s how i understand it (it’s a shared property of the NetworkInterface class).
I should be able to connect locally.

NetworkInterface.Loopback returns “127.0.0.1” as local address and 255.0.0.0 as subnetmask.
It’s an easy way to keep the TCPSocket connection local (to the computer) itself.

Not sure though, perhaps @Greg O’Lone could explain?

Correct me if I’m wrong, but isn’t it just a exchange of files? So yes, it would probably be easier to reinvent the wheel and write your own ipc classes.

I run a sandboxed helper service which uses a timer to look for message-files, coming from the main app, and vice versa. Works fine for me and I don’t see any negative impact on CPU usage.

[quote=307273:@Derk Jochems]Perhaps you can use a TCP Socket with a LoopbackInterface ?
NetworkInterface.Loopback [/quote]
Very interesting option, thanks for suggesting it.

[quote=307283:@Oliver Osswald]Correct me if I’m wrong, but isn’t it just a exchange of files? So yes, it would probably be easier to reinvent the wheel and write your own ipc classes.

I run a sandboxed helper service which uses a timer to look for message-files, coming from the main app, and vice versa. Works fine for me and I don’t see any negative impact on CPU usage.[/quote]
If IPC really is a simple pipe to a file, then yeah exactly…

https://forum.xojo.com/conversation/post/168010

This invites security issues because any process on the machine could connect to it.

This applies only for Windows.

It’s not a pipe, it’s a Unix domain socket. How long is the path you’re trying to use?

And Unix domain sockets use the file system, right?

From Wiki:

https://en.wikipedia.org/wiki/Unix_domain_socket

Sorry Joe, this is the second time you asked.

socketFile = specialFolder.temporary.child( nthfield( uuidGen.result, "-", 1 ) + ".mtchk" )

results in 86 characters long.

/private/var/folders/qj/707mr8cx0c557zv3b006yh1r0000gp/T/TemporaryItems/DFDC91CD.mtchk

Once sandboxed the same code results in 130 characters long.

/private/var/folders/qj/707mr8cx0c557zv3b006yh1r0000gp/T/com.ohanaware.xojobinaryobjectserialization/TemporaryItems/BF96C34E.mtchk

Is there another shared temporary folder that I might be able to use, which results in a much shorter path?

Sorry my ignorance for the underlying technologies is clearly showing through.

So here’s what I’m thinking at the moment if I we’re unable to get IPC working.

#1 The Controller creates a serialized file containing the instructions and properties (and a identifier indicating that this is a instruction).
#2 The Controller then launches the helper and passes it the path of the data file.
#3 The Worker reads the file, gets what it needs and starts doing the job.
#4 Once the worker has completed, it updates that file with the results (and a identifier indicating that this is a response).
#5 When the worker has quit, the Controller then re-reads that file, if it find the instruction identifier, it knows that the worker failed. If it finds the result it calls it’s “Completed” event passing in the deserialized values.

As for security, I’m thinking to add signing to the data. So that a signature is created from the data with some hidden salting. However that may come in a future version.

[quote=307529:@Sam Rowlands]Sorry Joe, this is the second time you asked.

socketFile = specialFolder.temporary.child( nthfield( uuidGen.result, "-", 1 ) + ".mtchk" )

results in 86 characters long.

/private/var/folders/qj/707mr8cx0c557zv3b006yh1r0000gp/T/TemporaryItems/DFDC91CD.mtchk

Once sandboxed the same code results in 130 characters long.

/private/var/folders/qj/707mr8cx0c557zv3b006yh1r0000gp/T/com.ohanaware.xojobinaryobjectserialization/TemporaryItems/BF96C34E.mtchk

Is there another shared temporary folder that I might be able to use, which results in a much shorter path?

Sorry my ignorance for the underlying technologies is clearly showing through.[/quote]

What happens if you place it in /tmp? Or whatever NSTemporaryDirectory returns? I’m guessing this might give you a shorter path to work with…

So from this code:

declare function NSTemporaryDirectory lib "Foundation" ( ) as CfStringRef socketFile = getFolderItem( NSTemporaryDirectory, folderitem.pathTypeNative ).child( nthfield( uuidGen.result, "-", 1 ) + ".mtchk" )

I get the full path of (115 characters):

/private/var/folders/qj/707mr8cx0c557zv3b006yh1r0000gp/T/com.ohanaware.xojobinaryobjectserialization/82AE5DDE.mtchk

And I get a bonafide Sandbox violation

sandboxd[134] ([2068]): MTCHK Controller(2068) deny file-write* /private/var/folders/qj/707mr8cx0c557zv3b006yh1r0000gp/T/com.ohanaware.xojobinaryobjectserialization

Using the tmp directory via the following code

socketFile = getFolderItem( "/tmp/" + nthfield( uuidGen.result, "-", 1 ) + ".mtchk", folderItem.pathTypeNative )

Gives me “/private/tmp/1707724F.mtchk” which is 27 characters, and gives me a Sandbox violation.

sandboxd[134] ([2104]): MTCHK Controller(2104) deny file-write-create /private/tmp/1707724F.mtchk

Also I then get socket error 107 :frowning:

[quote=307296:@Oliver Osswald]And Unix domain sockets use the file system, right?
[/quote]
Most everything in Unix is a “file” - but they’re not always just plain old text or binary files
Some have special handling

Okay, so progress!
I’ve implemented the code to use “Group Containers”, added the entitlements to App Wrapper and that stage works!

There’s a whole bunch of code required for it, but the end results is only 86 characters long.

/Users/rowlands/Library/Group Containers/QXAFMEPH6X.com.ohanaware.mcthk/64CDA630.mtchk

There’s a whole of Sandbox violations as the shell class wasn’t able to launch the helper, my code currently doesn’t know how to handle this, so I’ll use this opportunity to correct that.

Then I’ll add in the NSTask code and code sign the helper.

Hurrah! Thanks all for getting me over this hurdle, now on to the next!

DONE!
NSTask is now in there and it’s working exactly like it does when it’s not Sandboxed… What a PITA!

If anyone is interested, remind me in several months once the application has shipped and hopefully the code has been proven.