Ejemplo n.º 1
0
HRESULT HostDnsServiceWin::updateInfo()
{
    HostDnsInformation info;

    LONG lrc;
    int rc;

    std::string strDomain;
    std::string strSearchList;  /* NB: comma separated, no spaces */


    /*
     * We ignore "DhcpDomain" key here since it's not stable.  If
     * there are two active interfaces that use DHCP (in particular
     * when host uses OpenVPN) then DHCP ACKs will take turns updating
     * that key.  Instead we call GetAdaptersAddresses() below (which
     * is what ipconfig.exe seems to do).
     */
    for (DWORD regIndex = 0; /**/; ++regIndex) {
        char keyName[256];
        DWORD cbKeyName = sizeof(keyName);
        DWORD keyType = 0;
        char keyData[1024];
        DWORD cbKeyData = sizeof(keyData);

        lrc = RegEnumValueA(m->hKeyTcpipParameters, regIndex,
                            keyName, &cbKeyName, 0,
                            &keyType, (LPBYTE)keyData, &cbKeyData);

        if (lrc == ERROR_NO_MORE_ITEMS)
            break;

        if (lrc == ERROR_MORE_DATA) /* buffer too small; handle? */
            continue;

        if (lrc != ERROR_SUCCESS)
        {
            LogRel2(("HostDnsServiceWin: RegEnumValue error %d\n", (int)lrc));
            return E_FAIL;
        }

        if (keyType != REG_SZ)
            continue;

        if (cbKeyData > 0 && keyData[cbKeyData - 1] == '\0')
            --cbKeyData;     /* don't count trailing NUL if present */

        if (RTStrICmp("Domain", keyName) == 0)
        {
            strDomain.assign(keyData, cbKeyData);
            LogRel2(("HostDnsServiceWin: Domain=\"%s\"\n", strDomain.c_str()));
        }
        else if (RTStrICmp("DhcpDomain", keyName) == 0)
        {
            std::string strDhcpDomain(keyData, cbKeyData);
            LogRel2(("HostDnsServiceWin: DhcpDomain=\"%s\"\n", strDhcpDomain.c_str()));
        }
        else if (RTStrICmp("SearchList", keyName) == 0)
        {
            strSearchList.assign(keyData, cbKeyData);
            LogRel2(("HostDnsServiceWin: SearchList=\"%s\"\n", strSearchList.c_str()));
        }
    }

    /* statically configured domain name */
    if (!strDomain.empty())
    {
        info.domain = strDomain;
        info.searchList.push_back(strDomain);
    }

    /* statically configured search list */
    if (!strSearchList.empty())
    {
        vappend(info.searchList, strSearchList, ',');
    }


    /*
     * When name servers are configured statically it seems that the
     * value of Tcpip\Parameters\NameServer is NOT set, inly interface
     * specific NameServer value is (which triggers notification for
     * us to pick up the change).  Fortunately, DnsApi seems to do the
     * right thing there.
     */
    DNS_STATUS status;
    PIP4_ARRAY pIp4Array = NULL;

    // NB: must be set on input it seems, despite docs' claim to the contrary.
    DWORD cbBuffer = sizeof(&pIp4Array);

    status = DnsQueryConfig(DnsConfigDnsServerList,
                            DNS_CONFIG_FLAG_ALLOC, NULL, NULL,
                            &pIp4Array, &cbBuffer);

    if (status == NO_ERROR && pIp4Array != NULL)
    {
        for (DWORD i = 0; i < pIp4Array->AddrCount; ++i)
        {
            char szAddrStr[16] = "";
            RTStrPrintf(szAddrStr, sizeof(szAddrStr), "%RTnaipv4", pIp4Array->AddrArray[i]);

            LogRel2(("HostDnsServiceWin: server %d: %s\n", i+1,  szAddrStr));
            info.servers.push_back(szAddrStr);
        }

        LocalFree(pIp4Array);
    }


    /**
     * DnsQueryConfig(DnsConfigSearchList, ...) is not implemented.
     * Call GetAdaptersAddresses() that orders the returned list
     * appropriately and collect IP_ADAPTER_ADDRESSES::DnsSuffix.
     */
    do {
        PIP_ADAPTER_ADDRESSES pAddrBuf = NULL;
        ULONG cbAddrBuf = 8 * 1024;
        bool fReallocated = false;
        ULONG err;

        pAddrBuf = (PIP_ADAPTER_ADDRESSES) malloc(cbAddrBuf);
        if (pAddrBuf == NULL)
        {
            LogRel2(("HostDnsServiceWin: failed to allocate %zu bytes"
                     " of GetAdaptersAddresses buffer\n",
                     (size_t)cbAddrBuf));
            break;
        }

        while (pAddrBuf != NULL)
        {
            ULONG cbAddrBufProvided = cbAddrBuf;

            err = GetAdaptersAddresses(AF_UNSPEC,
                                         GAA_FLAG_SKIP_ANYCAST
                                       | GAA_FLAG_SKIP_MULTICAST,
                                       NULL,
                                       pAddrBuf, &cbAddrBuf);
            if (err == NO_ERROR)
            {
                break;
            }
            else if (err == ERROR_BUFFER_OVERFLOW)
            {
                LogRel2(("HostDnsServiceWin: provided GetAdaptersAddresses with %zu"
                         " but asked again for %zu bytes\n",
                         (size_t)cbAddrBufProvided, (size_t)cbAddrBuf));

                if (RT_UNLIKELY(fReallocated)) /* what? again?! */
                {
                    LogRel2(("HostDnsServiceWin: ... not going to realloc again\n"));
                    free(pAddrBuf);
                    pAddrBuf = NULL;
                    break;
                }

                PIP_ADAPTER_ADDRESSES pNewBuf = (PIP_ADAPTER_ADDRESSES) realloc(pAddrBuf, cbAddrBuf);
                if (pNewBuf == NULL)
                {
                    LogRel2(("HostDnsServiceWin: failed to reallocate %zu bytes\n", (size_t)cbAddrBuf));
                    free(pAddrBuf);
                    pAddrBuf = NULL;
                    break;
                }

                /* try again */
                pAddrBuf = pNewBuf; /* cbAddrBuf already updated */
                fReallocated = true;
            }
            else
            {
                LogRel2(("HostDnsServiceWin: GetAdaptersAddresses error %d\n", err));
                free(pAddrBuf);
                pAddrBuf = NULL;
                break;
            }
        }

        if (pAddrBuf == NULL)
            break;

        for (PIP_ADAPTER_ADDRESSES pAdp = pAddrBuf; pAdp != NULL; pAdp = pAdp->Next)
        {
            LogRel2(("HostDnsServiceWin: %ls (status %u) ...\n",
                     pAdp->FriendlyName ? pAdp->FriendlyName : L"(null)",
                     pAdp->OperStatus));

            if (pAdp->OperStatus != IfOperStatusUp)
                continue;

            if (pAdp->DnsSuffix == NULL || *pAdp->DnsSuffix == L'\0')
                continue;

            char *pszDnsSuffix = NULL;
            rc = RTUtf16ToUtf8Ex(pAdp->DnsSuffix, RTSTR_MAX,
                                 &pszDnsSuffix, 0, /* allocate */
                                 NULL);
            if (RT_FAILURE(rc))
            {
                LogRel2(("HostDnsServiceWin: failed to convert DNS suffix \"%ls\": %Rrc\n",
                        pAdp->DnsSuffix, rc));
                continue;
            }

            AssertContinue(pszDnsSuffix != NULL);
            AssertContinue(*pszDnsSuffix != '\0');
            LogRel2(("HostDnsServiceWin: ... suffix = \"%s\"\n", pszDnsSuffix));

            vappend(info.searchList, pszDnsSuffix);
            RTStrFree(pszDnsSuffix);
        }

        free(pAddrBuf);
    } while (0);


    if (info.domain.empty() && !info.searchList.empty())
        info.domain = info.searchList[0];

    if (info.searchList.size() == 1)
        info.searchList.clear();

    HostDnsMonitor::setInfo(info);

    return S_OK;
}
/**
 * Watcher thread.
 */
/*static*/ DECLCALLBACK(int) VirtualBoxSDS::i_watcherThreadProc(RTTHREAD hSelf, void *pvUser)
{
    VBoxSDSWatcher *pThis    = (VBoxSDSWatcher *)pvUser;
    VirtualBoxSDS  *pVBoxSDS = pThis->pVBoxSDS;
    RT_NOREF(hSelf);

    /*
     * This thread may release references to IVBoxSVCRegistration objects.
     */
    CoInitializeEx(NULL, COINIT_MULTITHREADED);

    /*
     * The loop.
     */
    RTCritSectEnter(&pVBoxSDS->m_WatcherCritSect);
    while (!pThis->fShutdown)
    {
        /*
         * Deal with the todo list.
         */
        uint32_t cHandles = pThis->cHandles;
        uint32_t cTodos   = pThis->cTodos;

        for (uint32_t i = 0; i < cTodos; i++)
        {
            VBoxSDSPerUserData *pUserData = pThis->aTodos[i].Data.pUserData;
            AssertContinue(pUserData);
            if (pThis->aTodos[i].hProcess != NULL)
            {
                /* Add: */
                AssertLogRelMsgBreakStmt(cHandles < RT_ELEMENTS(pThis->aHandles),
                                         ("cHandles=%u cTodos=%u i=%u iWatcher=%u\n", cHandles, cTodos, i, pThis->iWatcher),
                                         pThis->fShutdown = true);
                pThis->aHandles[cHandles] = pThis->aTodos[i].hProcess;
                pThis->aData[cHandles]    = pThis->aTodos[i].Data;
                cHandles++;
            }
            else
            {
                /* Remove: */
                uint32_t cRemoved = 0;
                uint32_t j        = cHandles;
                while (j-- > 1)
                    if (pThis->aData[j].pUserData == pUserData)
                    {
                        cHandles = pThis->removeHandle(j, cHandles);
                        pUserData->i_release();
                        cRemoved++;
                    }
                if (cRemoved != 1)
                    LogRel(("i_watcherThreadProc/#%u: Warning! cRemoved=%u pUserData=%p\n", pThis->iWatcher, cRemoved, pUserData));
            }
            /* Zap the entry in case we assert and leave further up. */
            pThis->aTodos[i].Data.setNull();
            pThis->aTodos[i].hProcess = NULL;
        }

        Assert(cHandles > 0 && cHandles <= RT_ELEMENTS(pThis->aHandles));
        pThis->cHandles          = cHandles;
        pThis->cHandlesEffective = cHandles;
        pThis->cTodos            = 0;

        if (pThis->fShutdown)
            break;

        /*
         * Wait.
         */
        RTCritSectLeave(&pVBoxSDS->m_WatcherCritSect);

        LogRel(("i_watcherThreadProc/#%u: Waiting on %u handles...\n", pThis->iWatcher, cHandles));
        DWORD const dwWait = WaitForMultipleObjects(cHandles, pThis->aHandles, FALSE /*fWaitAll*/, INFINITE);
        LogRel(("i_watcherThreadProc/#%u: ... wait returned: %#x (%d)\n", pThis->iWatcher, dwWait, dwWait));

        uint32_t const iHandle = dwWait - WAIT_OBJECT_0;
        if (iHandle < cHandles && iHandle > 0)
        {
            /*
             * A VBoxSVC process has terminated.
             *
             * Note! We need to take the user data lock before the watcher one here.
             */
            VBoxSDSPerUserData * const pUserData = pThis->aData[iHandle].pUserData;
            uint32_t const             iRevision = pThis->aData[iHandle].iRevision;
            RTPROCESS const            pid       = pThis->aData[iHandle].pid;

            pUserData->i_lock();
            RTCritSectEnter(&pVBoxSDS->m_WatcherCritSect);

            DWORD dwExit = 0;
            GetExitCodeProcess(pThis->aHandles[iHandle], &dwExit);
            LogRel(("i_watcherThreadProc/#%u: %p/%s: PID %u/%#x termination detected: %d (%#x)  [iRev=%u, cur %u]\n",
                    pThis->iWatcher, pUserData, pUserData->m_strUsername.c_str(), pid, pid, dwExit, dwExit,
                    iRevision, pUserData->m_iTheChosenOneRevision));

            /* Remove it from the handle array. */
            CloseHandle(pThis->aHandles[iHandle]);
            pThis->cHandles = cHandles = pThis->removeHandle(iHandle, cHandles);
            pThis->cHandlesEffective -= 1;

            /* If the process we were watching is still the current chosen one,
               unchoose it and decrement the client count.  Otherwise we were subject
               to a deregistration/termination race (unlikely). */
            if (pUserData->m_iTheChosenOneRevision == iRevision)
            {
                pUserData->i_unchooseTheOne(true /*fIrregular*/);
                pUserData->i_unlock();
                pVBoxSDS->i_decrementClientCount();
            }
            else
                pUserData->i_unlock();
            pUserData->i_release();
        }
        else
        {
            RTCritSectEnter(&pThis->pVBoxSDS->m_WatcherCritSect);
            AssertLogRelMsgBreak(iHandle == 0 || dwWait == WAIT_TIMEOUT,
                                 ("dwWait=%u (%#x) cHandles=%u\n", dwWait, dwWait, cHandles));
        }
    }

    RTCritSectLeave(&pThis->pVBoxSDS->m_WatcherCritSect);

    /*
     * In case we quit w/o being told, signal i_watchIt that we're out of action.
     */
    pThis->fShutdown = true;

    /*
     * Release all our data on the way out.
     */
    uint32_t i = pThis->cHandles;
    while (i-- > 1)
    {
        if (pThis->aData[i].pUserData)
        {
            pThis->aData[i].pUserData->i_release();
            pThis->aData[i].pUserData = NULL;
        }
        if (pThis->aHandles[i])
        {
            CloseHandle(pThis->aHandles[i]);
            pThis->aHandles[i] = NULL;
        }
    }
    if (pThis->aHandles[0])
    {
        CloseHandle(pThis->aHandles[0]);
        pThis->aHandles[0] = NULL;
    }

    i = pThis->cTodos;
    pThis->cTodos = 0;
    while (i-- > 0)
    {
        if (pThis->aTodos[i].Data.pUserData)
        {
            pThis->aTodos[i].Data.pUserData->i_release();
            pThis->aTodos[i].Data.pUserData = NULL;
        }
        if (pThis->aTodos[i].hProcess)
        {
            CloseHandle(pThis->aTodos[i].hProcess);
            pThis->aTodos[i].hProcess = NULL;
        }
    }

    if (ASMAtomicDecU32(&pThis->cRefs) == 0)
        RTMemFree(pThis);

    return VINF_SUCCESS;
}