/* $Id: HostDnsServiceDarwin.cpp 52901 2014-09-30 15:32:03Z vboxsync $ */ /** @file * Darwin specific DNS information fetching. */ /* * Copyright (C) 2004-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ #include #include #include #include #include #include #include #include #include #include #include "../HostDnsService.h" struct HostDnsServiceDarwin::Data { SCDynamicStoreRef m_store; CFRunLoopSourceRef m_DnsWatcher; CFRunLoopRef m_RunLoopRef; CFRunLoopSourceRef m_Stopper; bool m_fStop; RTSEMEVENT m_evtStop; static void performShutdownCallback(void *); }; static const CFStringRef kStateNetworkGlobalDNSKey = CFSTR("State:/Network/Global/DNS"); HostDnsServiceDarwin::HostDnsServiceDarwin():HostDnsMonitor(true),m(NULL) { m = new HostDnsServiceDarwin::Data(); } HostDnsServiceDarwin::~HostDnsServiceDarwin() { if (!m) return; monitorThreadShutdown(); CFRelease(m->m_RunLoopRef); CFRelease(m->m_DnsWatcher); CFRelease(m->m_store); RTSemEventDestroy(m->m_evtStop); delete m; m = NULL; } void HostDnsServiceDarwin::hostDnsServiceStoreCallback(void *, void *, void *info) { HostDnsServiceDarwin *pThis = (HostDnsServiceDarwin *)info; ALock l(pThis); pThis->updateInfo(); pThis->notifyAll(); } HRESULT HostDnsServiceDarwin::init() { SCDynamicStoreContext ctx; RT_ZERO(ctx); ctx.info = this; m->m_store = SCDynamicStoreCreate(NULL, CFSTR("org.virtualbox.VBoxSVC"), (SCDynamicStoreCallBack)HostDnsServiceDarwin::hostDnsServiceStoreCallback, &ctx); AssertReturn(m->m_store, E_FAIL); m->m_DnsWatcher = SCDynamicStoreCreateRunLoopSource(NULL, m->m_store, 0); if (!m->m_DnsWatcher) return E_OUTOFMEMORY; int rc = RTSemEventCreate(&m->m_evtStop); AssertRCReturn(rc, E_FAIL); CFRunLoopSourceContext sctx; RT_ZERO(sctx); sctx.perform = HostDnsServiceDarwin::Data::performShutdownCallback; m->m_Stopper = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &sctx); AssertReturn(m->m_Stopper, E_FAIL); HRESULT hrc = HostDnsMonitor::init(); AssertComRCReturn(hrc, hrc); return updateInfo(); } void HostDnsServiceDarwin::monitorThreadShutdown() { ALock l(this); if (!m->m_fStop) { CFRunLoopSourceSignal(m->m_Stopper); CFRunLoopWakeUp(m->m_RunLoopRef); RTSemEventWait(m->m_evtStop, RT_INDEFINITE_WAIT); } } int HostDnsServiceDarwin::monitorWorker() { m->m_RunLoopRef = CFRunLoopGetCurrent(); AssertReturn(m->m_RunLoopRef, VERR_INTERNAL_ERROR); CFRetain(m->m_RunLoopRef); CFArrayRef watchingArrayRef = CFArrayCreate(NULL, (const void **)&kStateNetworkGlobalDNSKey, 1, &kCFTypeArrayCallBacks); if (!watchingArrayRef) { CFRelease(m->m_DnsWatcher); return E_OUTOFMEMORY; } if(SCDynamicStoreSetNotificationKeys(m->m_store, watchingArrayRef, NULL)) CFRunLoopAddSource(CFRunLoopGetCurrent(), m->m_DnsWatcher, kCFRunLoopCommonModes); CFRelease(watchingArrayRef); monitorThreadInitializationDone(); while (!m->m_fStop) { CFRunLoopRun(); } CFRelease(m->m_RunLoopRef); /* We're notifying stopper thread. */ RTSemEventSignal(m->m_evtStop); return VINF_SUCCESS; } static wchar_t *darwinCFStringToUtf16(CFStringRef pStringRef) { /* Number of characters (UTF16 encoded, 2 bytes) in the pStringRef. */ CFIndex ccStringRef = CFStringGetLength(pStringRef); if (ccStringRef > 0) { size_t cbTmpBuf = (size_t)ccStringRef * sizeof(wchar_t) + 1 /* end of string */; wchar_t *pTmpBuf = (wchar_t *)RTMemAlloc(cbTmpBuf); if (pTmpBuf) { CFIndex ccConverted = CFStringGetBytes(pStringRef, CFRangeMake(0, ccStringRef), kCFStringEncodingUTF16, 0, false, (UInt8 *)pTmpBuf, (CFIndex)cbTmpBuf - 1 /* w/o end of string */, NULL); if (ccConverted > 0) { /* Set end of string. */ pTmpBuf[ccConverted] = 0; return pTmpBuf; } RTMemFree(pTmpBuf); } } return NULL; } HRESULT HostDnsServiceDarwin::updateInfo() { CFPropertyListRef propertyRef = SCDynamicStoreCopyValue(m->m_store, kStateNetworkGlobalDNSKey); /** * 0:vvl@nb-mbp-i7-2(0)# scutil * > get State:/Network/Global/DNS * > d.show * { * DomainName : vvl-domain * SearchDomains : { * 0 : vvl-domain * 1 : de.vvl-domain.com * } * ServerAddresses : { * 0 : 192.168.1.4 * 1 : 192.168.1.1 * 2 : 8.8.4.4 * } * } */ if (!propertyRef) return S_OK; HostDnsInformation info; CFStringRef domainNameRef = (CFStringRef)CFDictionaryGetValue( static_cast(propertyRef), CFSTR("DomainName")); if (domainNameRef) { wchar_t *pwszDomainName = darwinCFStringToUtf16(domainNameRef); if (pwszDomainName) { info.domain = std::wstring(pwszDomainName); RTMemFree(pwszDomainName); } } int i, arrayCount; CFArrayRef serverArrayRef = (CFArrayRef)CFDictionaryGetValue( static_cast(propertyRef), CFSTR("ServerAddresses")); if (serverArrayRef) { arrayCount = CFArrayGetCount(serverArrayRef); for (i = 0; i < arrayCount; ++i) { CFStringRef serverAddressRef = (CFStringRef)CFArrayGetValueAtIndex(serverArrayRef, i); if (!serverArrayRef) continue; wchar_t *pwszServerAddress = darwinCFStringToUtf16(serverAddressRef); if (!pwszServerAddress) continue; info.servers.push_back(std::wstring(pwszServerAddress)); RTMemFree(pwszServerAddress); } } CFArrayRef searchArrayRef = (CFArrayRef)CFDictionaryGetValue( static_cast(propertyRef), CFSTR("SearchDomains")); if (searchArrayRef) { arrayCount = CFArrayGetCount(searchArrayRef); for (i = 0; i < arrayCount; ++i) { CFStringRef searchStringRef = (CFStringRef)CFArrayGetValueAtIndex(searchArrayRef, i); if (!searchArrayRef) continue; wchar_t *pwszSearchString = darwinCFStringToUtf16(searchStringRef); if (!pwszSearchString) continue; info.searchList.push_back(std::wstring(pwszSearchString)); RTMemFree(pwszSearchString); } } CFRelease(propertyRef); setInfo(info); return S_OK; } void HostDnsServiceDarwin::Data::performShutdownCallback(void *info) { HostDnsServiceDarwin::Data *pThis = static_cast(info); pThis->m_fStop = true; }