We have found a VPN that does not work while our TPP is running, and I have a hypothesis why, and it does not make any sense.
It only fails when our TPP asks for UDP flows.
Their VPN claims to fail at a DNS query, but it's getting EPIPE (this is Twingate for the curious). Looking at all the logs I can on the system, including dtruss and dtrace, I see that it does a sendto, and gets that errno. I can't, of course, determine more.
By adding more logging, I can see that their VPN tunnel provider tries to open up a UDP flow to 8.8.8.8 port 53. First red flag: I did not think we were supposed to get DNS queries -- my guess is that only means for apps that use the system DNS libraries, implying (to me) that this VPN has their own DNS code.
We look at the app name, and decide we don't care for it -- handleNewUDPFlow(_:initialEndpoint:) returns false/NO.
I see this in the system logs:
2024-06-26 11:06:56.342680+0100 0x300c839 Default 0x0 40823 0 ${us}.Redirector: (NetworkExtension) [com.apple.networkextension:] [Extension ${us}]: provider rejected new flow UDP ${them}.macos.tunnelprovider[{length = 20, bytes = 0xca1b405e014154c2e38e20159d033f9b2d3eea18}] local port 0 interface en0(bound)
which is all correct. But then the very next log entry is
2024-06-26 11:06:56.342717+0100 0x300cc14 Info 0x0 0 0 kernel: (399482302): received connect result 61
which, there you go, ECONNREFUSED which will be turned into EPIPE by sendto. (ETA: No, that's not what happens at all. I see other port 53 queries in my logs, and they follow the same, er, flow -- TPP refuses them, next log entry for the flow by the system is result 61.)
There is no traffic to 8.8.8.8 over any of the interfaces.
I have tried using a NENetworkRule that _excludes` port 53, but it does not allow that at all.
I am very deeply confused by all of this, to the point I'm not quite sure how to begin to articulate a request for help. If anyone has any thoughts, comments, questions, commiserative howls of agony, I'd appreciate it.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/include/c++/v1/__format/formatter_floating_point.h:66:30: error:
'to_chars' is unavailable: introduced in macOS 13.3
66 | to_chars_result __r = std::to_chars(__first, __last, __value, __fmt);
Which, ok, I can accept that this is true. Except that this worked on Friday, on the same machine running Sonoma and the previous version of Xcode. The project is configured for a deployment target of 12.0, so it should have failed before, but didn't.
(This is a CMake-generated xcodeproj, but that also should not have been any change.)
This has been going on for at least a couple of hours for us: notarizing doesn't complete. Our last job ran for over 90 minutes before CircleCI timed it out. We're using xcrun notarytool submit with the --wait option; it contined to say "Current status: In Progress" for, as I said, 90 minutes or so. (Normally it takes about 70 seconds.)
https://developer.apple.com/system-status/ says everything is normal. This does not seem to be the case for us. 😄
In doing some work, I realized I didn't understand XPC (or at least, the higher-level APIs) at all. So I did what I usually try to do, which is to write a completely, brain-dead simple program to use it, and then keep expanding until I understand it. This also, to my utmost embarrassment, will make it transparently clear how ignorant I am. (Note that, for this, I am not using Xcode -- just using swiftc to compile, and then manually run.)
I started with this, to be the server side:
import Foundation
class ConnectionHandler: NSObject, NSXPCListenerDelegate {
override init() {
super.init()
print("ConnectionHandler.init()")
}
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
print("ConnectionHandler.listener()")
return false
}
}
let handler = ConnectionHandler()
let listener = NSXPCListener(machServiceName: "com.kithrup.test")
listener.delegate = handler
listener.resume()
print("listener = \(listener)")
dispatchMain()
That ... does absolutely nothing, of course, but runs, and then I tried to write the client side:
import Foundation
@objc protocol Hello {
func hello()
}
class HelloClass: NSObject, Hello {
override init() {
super.init()
}
func hello() {
print("In HelloClass.hello()")
}
}
let hello = HelloClass()
let connection = NSXPCConnection(machServiceName: "com.kithrup.test", options: [])
connection.exportedInterface = NSXPCInterface(with: Hello.self)
let proxy = connection.remoteObjectProxyWithErrorHandler({ error in
print("Got error \(error)")
}) as? Hello
print("proxy = \(proxy)")
connection.resume()
dispatchMain()
That gets proxy as nil. Because it can't coerce it to something of Hello protocol. And at no point, do I get the message from the server-side listener.
So clearly I am doing everything wrong. Can anyone offer some hints?
There's the NSWorkspaceSessionDidBecomeActiveNotification et al, but that doesn't say who the current user is. Is there a way to find out that? (I mean, I could have a LaunchAgent that send "I've become active!" log message or something, and then use the most recent one. Is there another way?)
As I asked earlier, I was trying to figure out who the current user is. Since the proposed solution doesn't work, I thought, okay, let's try sending notifications from a LaunchAgent! Only, right, notifications are per-process, so I tried Distributed Notifications... and that doesn't seem to work across users. (Now, since I log while also sending the distributed notifications, I can see that it is working the way I want. Except that willPowerOffNotification doesn't actually seem to happen with a logout. Maybe that's because it's a CLI program? But the other notifications do work...)
We're using CMake here, so we can build on Windows, Linux, and macOS. So now I'm trying to convert from Xcode to CMake (which then generates an xcode project, whee).
The main problems I'm running into are figuring out which settings to do via CMakeLists.txt. That's mostly tiresome. But theres a new issue, and I don't know enough about CMake to figure it out: compiling my .swift file generates a ${PROJECT}-Swift.h file, which is used by the ObjC files. Which is great.
Except I don't know how to tell CMake about that. (And I haven't figured out what variable describes where Xcode puts it, but that's more of a tiresome issue than head-against-desk issue...)
Has anyone run into and hopefully figured this out?
The NSWorkspace method that does this, fullPathForApplication, is deprecated. So what's the alternative? I do note that oascript can do it by id of app "App Name", so unless that's going away too, there must be some way of doing it, no?
I create a protocol that had, among other things:
@objc func setList(_: [MyType], withReply: @escaping (Error?) -> Void)
The daemon part is in Swift, while the calling part is in Objective-C. Because why not? (Actually, because the calling part has to deal with C++ code, so that's ObjC++; however, I wanted the stronger typing and runtime checking for the daemon part, so I wrote it in Swift.) The ObjC part uses NSArray<MyType*>.
I set up an NSXPCConnection link, and create a (synchronous) proxy with the right protocol name. But when I try to do the XPC setList call, I get an error. I assume that's because it doesn't like the signature. (Surely this is logged somewhere? I couldn't find it, if so. 😩) But... if I have a signature of @objc func addItem(_: MyType, withReply: @escaping (Error?) -> Void), then it works. So I assume it's the array. (Oh, I've also tried it without the @objc; the protocol itself is defined as @objc.)
I've tried changing to protocol signature to using NSArray, but same thing.
I got the permission from Apple (yay), and when I generate a profile on the portal, I can select it. But when I download it... it doesn't have it. Looking at the profile on the portal again, it says I have "Enabled Capabilities Endpoint Security, In-App Purchase". (Although how did that get there?)
Consider:
sef% mdfind 'kMDItemDisplayName =[c] "Zoom.us"'
sef%
sef% mdfind 'kMDItemDisplayName =[c] "zoom.us"'
sef%
vs
sef% mdfind 'kMDItemDisplayName == "zoom.us"'
/Applications/zoom.us.app
(Using '==' vs '=' doesn't seem to make a difference.)
I followed a tutorial on SwiftUI and macOS (and wow was it worth it, I understand more of what I was doing wrong before!). After the tutorial was done, I thought, okay, let's try adding CloudKit to it. Which was rather trivial to do, I was impressed. Then I thought, ok, let's try being able to have local or CloudKit, depending on runtime. But when I did that, the file was opened readonly, due to "Store opened without NSPersistentHistoryTrackingKey but previously had been opened with NSPersistentHistoryTrackingKey."
This seems to be something a lot of people have run into, but for some reason I haven't found any actual fixes. Anyone know?
Unless I'm missing something (always possible)... there is no way to tell if you get a valid proxy back -- the error handler is asynchronous, and I don't see a way to say "this proxy is valid."
Am I missing something?
Just to make sure I'm not once again doing something dumb or stupid: this notification only gets sent to the application that loads & starts the VPN? If I want some other process to be aware of it, I have to use the SC* APIs?
In a deamon, I register using:
[[NSDistributedNotificationCenter defaultCenter] addObserver:loader
selector:@selector(getNotifications:)
name:kProxyStartNotification
object:@"MyProxy"];
In the transparent proxy provider, I post using:
static let dnc = DistributedNotificationCenter.default()
// later
Self.dnc.postNotificationName(.proxyStart,
object:"MyProxy",
deliverImmediately:true)
(I also tried putting the same observer code in the containing app, just to be sure.)
(Also, btw: the automatic conversion of kProxyStartNotification to Notification.Name.proxyStart is really sweet.)
I'm not getting the notification in other processes. I tried putting an observer in the extension, and sure enough that did get the notification, so it is being generated. Just... not leaving my process.
I tried checking for sandbox violations, using Quinn's instructions, but see nothing.
I also tried
[[NSDistributedNotificationCenter defaultCenter] addObserver:loader
selector:@selector(getNotifications:)
name:nil
object:@"MyProxy"];
(and the same in Swift, for the network extension), and -- as can be guessed by my still asking this question -- no luck.