I'm trying to change the DNS servers on my Mac (10.10.4) using PyObjC (3.0.4).
Everything seems to work: I get an authentication dialog prompting that my program is trying to change network settings, and the commit/apply commands return True
, which would indicate they were successful.
However, the system settings aren't actually changed: they remain the same as before. Any idea why they don't "stick"?
The code (standalone, should work if you have a recent version of PyObjC installed):
#!/usr/bin/env python
import objc
from SystemConfiguration import *
# Open dynamic store and get primary interface
store = SCDynamicStoreCreate(None, 'MyApp', None, None)
primaryif = SCDynamicStoreCopyValue(store, 'State:/Network/Global/IPv4')['PrimaryInterface']
if primaryif:
print "Using %s as primary interface" % primaryif
else:
raise "Can't find primary interface"
# Load SecurityInterface framework to provide SFAuthorization
objc.initFrameworkWrapper(
frameworkName = "SecurityInterface",
frameworkIdentifier = "com.apple.securityinterface",
frameworkPath = objc.pathForFramework("/System/Library/Frameworks/SecurityInterface.framework"),
globals = globals()
)
# Access system preferences
preferences = SCPreferencesCreateWithAuthorization(None, 'MyApp', None, SFAuthorization.authorization().authorizationRef())
# Lock preferences
SCPreferencesLock(preferences, True)
# Get list of network services
networkSet = SCNetworkSetCopyCurrent(preferences)
networkSetServices = SCNetworkSetCopyServices(networkSet)
# Find the network service that belongs to the primary interface
for networkService in networkSetServices:
interface = SCNetworkServiceGetInterface(networkService)
if primaryif != SCNetworkInterfaceGetBSDName(interface):
continue
# Load currently configured DNS servers
networkProtocol = SCNetworkServiceCopyProtocol(networkService, kSCNetworkProtocolTypeDNS)
DNSDict = SCNetworkProtocolGetConfiguration(networkProtocol) or {}
# Set new DNS servers
DNSDict[kSCPropNetDNSServerAddresses] = [ '192.168.23.12', '8.8.4.4' ]
SCNetworkProtocolSetConfiguration(networkService, DNSDict)
# Unlock, commit and apply preferences
print "UL", SCPreferencesUnlock(preferences)
print "CO", SCPreferencesCommitChanges(preferences)
print "AP", SCPreferencesApplyChanges(preferences)
EDIT: most of the above code is based on this page, which also suggests "touching" the dynamic store to make the settings stick (the code to do this is commented out right at the end). However, it doesn't seem to do anything.
EDIT #2: by disassembling /usr/sbin/networksetup
I'm getting the idea that I need a set of specific rights (system.services.systemconfiguration.network
) before any changes are accepted.
sudo
? – MalteseDNSDict[kSCPropNetDNSServerAddresses] = [ '192.168.23.12', '8.8.4.4' ] TypeError: 'NoneType' object does not support item assignment
– Malteseen0
in my example). The example code is not performing error checking at every step. – EsmariaPyObjC
was ancient (which I think was the problem); I'll let you know what transpires after I update :) – MalteseDNSDict
may be empty when you rely on DHCP to provide DNS server (I have them set manually) so I also fixed that. As for updating PyObjC, I would recommend usingvirtualenv
. My experience with updating the system-provided PyObjC are not very good. – Esmaria.plist
files inSystem Configuration
never get updated (or even accessed) with the networksetup changes. I'm not exactly sure why that's happening (maybe Apple has changed something?), however, I did some digging around github and found an example that uses Python'sos
cmd to change the setup and it did indeed work. I realize it's a slightly different approach, although maybe it can be of some use perhaps. – Maltesecom.apple.networkextension.plist
,preferences.plist
, andresolv.conf
. If you run thenetworksetup
utility in terminal and set the dns there the values get set in those preferences. – Maltesenetworksetup
is the approach I'm actually using now (see this project). However, it's not ideal and I prefer setting the proxies (similar to DNS) programmatically. I'm almost certain that my call toSCPreferencesCreateWithAuthorization()
is incomplete: it has to request particular rights, however, I also found that it may not actually be possible to request those using PyObjC (yet). – Esmariaos
commands into everything that needs to be read/written. Your code looks like it should work from what I could tell. The only thing I wasn't sure about was how you were using lock/unlock - in other examples I have seen they appear to use lock set on False, and immediately after that unlock is called. When I have a chance I'll take a better look at the script and see if there's anything that might come to mind. – Maltesenetworksetup
either, but that changed with Mavericks). I should probably make sure though that the Objective-C version of my code is working as expected, I'll spend some time on that. – EsmariaSFAuthorization
. – Malteseflags
that need to be set; If you adjust your code to accompany them it might be worth a try in that regard. – Maltesenetworksetup
like I'm doing now :-) – Esmaria