974 lines
32 KiB
C++
974 lines
32 KiB
C++
/*
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
All rights reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
|
|
compliance with the License. You may obtain a copy of the License
|
|
at http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
|
|
EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
|
TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
|
|
|
|
|
See the Apache Version 2.0 License for specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
#include "DrCommon.h"
|
|
|
|
#pragma warning (push)
|
|
#pragma warning (disable:4365)
|
|
#include <ws2tcpip.h>
|
|
#pragma warning (pop)
|
|
|
|
#pragma prefast(disable:24002, "struct sockaddr not ipv6 compatible")
|
|
|
|
#pragma unmanaged
|
|
|
|
DrLastAccessTable *g_pDrLastAccessTable;
|
|
|
|
void DrInitLastAccessTable()
|
|
{
|
|
g_pDrLastAccessTable = new DrLastAccessTable();
|
|
LogAssert(g_pDrLastAccessTable != NULL);
|
|
}
|
|
|
|
// returns false if not a valid ip/node address
|
|
bool DrNodeAddress::Set(const struct sockaddr_in *pSockAddr, Size_t addrLen)
|
|
{
|
|
if (pSockAddr == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (addrLen < sizeof(struct sockaddr_in)) {
|
|
return false;
|
|
}
|
|
|
|
if (pSockAddr->sin_family != AF_INET) {
|
|
return false;
|
|
}
|
|
|
|
Set(pSockAddr->sin_addr, ntohs(pSockAddr->sin_port));
|
|
|
|
return true;
|
|
}
|
|
|
|
// Looks up a host name using DNS
|
|
// Note that this is a blocking request
|
|
// Returns up to addressBuffLen entries. If there are more entries than this, the list is truncated without error.
|
|
DrError DrNodeAddress::LookupHostName(
|
|
const char *pszHostName,
|
|
/* out */ DrIpAddress *pAddressBuff,
|
|
UInt32 addressBuffLen,
|
|
/* out */ UInt32 *pNumReturnedAddresses)
|
|
{
|
|
struct addrinfo hints;
|
|
struct addrinfo *pResults = NULL;
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
*pNumReturnedAddresses = 0;
|
|
|
|
hints.ai_family = PF_INET;
|
|
int ret = EAI_AGAIN;
|
|
|
|
while (ret == EAI_AGAIN) {
|
|
ret = getaddrinfo(pszHostName, NULL, &hints, &pResults);
|
|
|
|
if (ret == 0 && pResults == NULL) {
|
|
ret = EAI_NODATA;
|
|
}
|
|
}
|
|
|
|
if (ret != 0) {
|
|
goto done;
|
|
}
|
|
|
|
UInt32 n = 0;
|
|
struct addrinfo *pResultEntry = pResults;
|
|
while (pResultEntry != NULL && addressBuffLen > 0) {
|
|
if (pResultEntry->ai_family == PF_INET && pResultEntry->ai_addr != NULL && pResultEntry->ai_addrlen == sizeof(sockaddr_in)) {
|
|
const struct sockaddr_in *paddr = (const struct sockaddr_in *)(const void *)(pResultEntry->ai_addr);
|
|
DrIpAddress addr = ntohl(paddr->sin_addr.S_un.S_addr);
|
|
*pAddressBuff = addr;
|
|
pAddressBuff++;
|
|
addressBuffLen--;
|
|
n++;
|
|
}
|
|
pResultEntry = pResultEntry->ai_next;
|
|
}
|
|
|
|
if (n == 0) {
|
|
ret = EAI_NODATA;
|
|
}
|
|
|
|
*pNumReturnedAddresses = n;
|
|
ret = 0;
|
|
|
|
done:
|
|
if (pResults != NULL) {
|
|
freeaddrinfo(pResults);
|
|
}
|
|
|
|
switch(ret) {
|
|
case EAI_NODATA:
|
|
ret = DrError_HostNotFound;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
// Converts the contained IP/port address to a string of the form "#.#.#.#:port". If the contained port number matches defaultPort, the
|
|
// port number is not included in the string.
|
|
DrStr& DrNodeAddress::AppendToString(DrStr& strOut, DrPortNumber defaultPort) const
|
|
{
|
|
if (m_wPort == defaultPort) {
|
|
strOut.AppendF("%u.%u.%u.%u",
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b1,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b2,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b3,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b4);
|
|
} else {
|
|
strOut.AppendF("%u.%u.%u.%u:%u",
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b1,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b2,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b3,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b4,
|
|
(DrIpAddress ) m_wPort);
|
|
}
|
|
|
|
return strOut;
|
|
}
|
|
/* JC
|
|
// Converts the contained IP/port address to a string of the form "#.#.#.#:port". If the contained port number matches defaultPort, the
|
|
// port number is not included in the string.
|
|
DrWStr& DrNodeAddress::AppendToString(DrWStr& strOut, DrPortNumber defaultPort) const
|
|
{
|
|
if (m_wPort == defaultPort) {
|
|
strOut.AppendF(L"%u.%u.%u.%u",
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b1,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b2,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b3,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b4);
|
|
} else {
|
|
strOut.AppendF(L"%u.%u.%u.%u:%u",
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b1,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b2,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b3,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b4,
|
|
(DrIpAddress ) m_wPort);
|
|
}
|
|
|
|
return strOut;
|
|
}
|
|
*/
|
|
|
|
// Converts the contained IP/port address to a string of the form "#.#.#.#:port". If the contained port number matches defaultPort, the
|
|
// port number is not included in the string.
|
|
// buffSize must be at least 22 or DrError_StringTooLong is returned.
|
|
DrError DrNodeAddress::ToAddressPortString(char *pBuffer, Size_t buffSize, DrPortNumber defaultPort) const
|
|
{
|
|
HRESULT hr;
|
|
if (m_wPort == defaultPort) {
|
|
hr =StringCbPrintfA(pBuffer, buffSize, "%u.%u.%u.%u",
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b1,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b2,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b3,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b4);
|
|
} else {
|
|
hr =StringCbPrintfA(pBuffer, buffSize, "%u.%u.%u.%u:%u",
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b1,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b2,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b3,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b4,
|
|
(DrIpAddress ) m_wPort);
|
|
}
|
|
|
|
return SUCCEEDED(hr) ? DrError_OK : DrError_StringTooLong;
|
|
}
|
|
|
|
// Converts the contained IP/port address to a string of the form "#.#.#.#:port". If the contained port number matches defaultPort, the
|
|
// port number is not included in the string.
|
|
// buffSize must be at least 22 or DrError_StringTooLong is returned.
|
|
DrError DrNodeAddress::ToAddressPortString(WCHAR *pBuffer, Size_t buffSize, DrPortNumber defaultPort) const
|
|
{
|
|
HRESULT hr;
|
|
if (m_wPort == defaultPort) {
|
|
hr =StringCbPrintfW(pBuffer, buffSize, L"%u.%u.%u.%u",
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b1,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b2,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b3,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b4);
|
|
} else {
|
|
hr =StringCbPrintfW(pBuffer, buffSize, L"%u.%u.%u.%u:%u",
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b1,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b2,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b3,
|
|
(DrIpAddress ) m_ina.S_un.S_un_b.s_b4,
|
|
(DrIpAddress ) m_wPort);
|
|
}
|
|
|
|
return SUCCEEDED(hr) ? DrError_OK : DrError_StringTooLong;
|
|
}
|
|
|
|
|
|
static DrError InternalParseHostPortName(
|
|
Size_t hostLength,
|
|
/*out */ DrPortNumber *pPort,
|
|
const char *pszName,
|
|
DrPortNumber defaultPort,
|
|
UInt32 *pInstanceNumOut)
|
|
{
|
|
LogAssert(pPort != NULL);
|
|
|
|
if (hostLength == 0) {
|
|
return DrError_InvalidParameter;
|
|
}
|
|
|
|
Size_t instanceLength = hostLength;
|
|
UInt32 uInstance = 0;
|
|
if (pszName[hostLength] == '!') {
|
|
char c;
|
|
for (instanceLength = hostLength + 1; (c = pszName[instanceLength]) != '\0' && c != ':'; instanceLength++) {
|
|
if (c >= '0' && c <= '9') {
|
|
uInstance = (10 * uInstance) + (UInt32)(c - '0');
|
|
if (uInstance > 65535) {
|
|
return DrError_InvalidParameter;
|
|
}
|
|
} else {
|
|
return DrError_InvalidParameter;
|
|
}
|
|
}
|
|
}
|
|
|
|
DrPortNumber finalPort;
|
|
|
|
if (pszName[instanceLength] == ':') {
|
|
const char *pszPort = pszName + instanceLength + 1;
|
|
DrError err = DrStringToPortNumber(pszPort, &finalPort);
|
|
if (err != DrError_OK) {
|
|
return err;
|
|
}
|
|
} else {
|
|
finalPort = defaultPort;
|
|
}
|
|
|
|
if (uInstance != 0 && finalPort != DrInvalidPortNumber && finalPort != DrAnyPortNumber) {
|
|
finalPort = (DrPortNumber)(finalPort + uInstance);
|
|
}
|
|
|
|
*pPort = finalPort;
|
|
if (pInstanceNumOut != NULL) {
|
|
*pInstanceNumOut = uInstance;
|
|
}
|
|
return DrError_OK;
|
|
}
|
|
|
|
inline static Size_t InternalHostLength(const char *pszName)
|
|
{
|
|
Size_t hostLength;
|
|
|
|
LogAssert(pszName != NULL);
|
|
|
|
for (hostLength = 0; pszName[hostLength] != '\0' && pszName[hostLength] != ':' && pszName[hostLength] != '!'; hostLength++) {
|
|
// Just counting
|
|
}
|
|
|
|
return hostLength;
|
|
}
|
|
|
|
// Parses a name in the forms:
|
|
// "#.#.#.#"
|
|
// "dns-name"
|
|
// "#.#.#.#!instance-num"
|
|
// "dns-name!instance-num"
|
|
// "#.#.#.#:port"
|
|
// "dns-name:port"
|
|
// "#.#.#.#!instance-num:port"
|
|
// "dns-name!instance-num:port"
|
|
// and splits out the host name and port.
|
|
// If ":port" is missing, uses the default port.
|
|
// if instance-num is present, it is added to the final port number.
|
|
// Returns DrError_InvalidParameter if the string is malformed.
|
|
DrError DrNodeAddress::ParseHostPortName(
|
|
/* out */ char *pHostNameBuffer,
|
|
Size_t buffLen,
|
|
/*out */ DrPortNumber *pPort,
|
|
const char *pszName,
|
|
DrPortNumber defaultPort,
|
|
UInt32 *pInstanceNumOut)
|
|
{
|
|
LogAssert(pHostNameBuffer != NULL);
|
|
Size_t hostLength = InternalHostLength(pszName);
|
|
|
|
if (hostLength == 0 || buffLen < hostLength+1) {
|
|
return DrError_InvalidParameter;
|
|
}
|
|
|
|
memcpy(pHostNameBuffer, pszName, hostLength);
|
|
pHostNameBuffer[hostLength] = '\0';
|
|
|
|
return InternalParseHostPortName(
|
|
hostLength,
|
|
pPort,
|
|
pszName,
|
|
defaultPort,
|
|
pInstanceNumOut);
|
|
}
|
|
|
|
// Parses a name in the form "#.#.#.#:port" or "dns-name:port" and splits out the host name and port.
|
|
// If ":port" is missing, uses the default port.
|
|
// strOut is replaced with the parsed host name
|
|
// Returns DrError_InvalidParameter if the string is malformed.
|
|
DrError DrNodeAddress::ParseHostPortName(DrStr& strOut, /*out */ DrPortNumber *pPort, const char *pszName, DrPortNumber defaultPort, UInt32 *pInstanceNumOut)
|
|
{
|
|
Size_t hostLength = InternalHostLength(pszName);
|
|
|
|
if (hostLength == 0) {
|
|
return DrError_InvalidParameter;
|
|
}
|
|
|
|
strOut.Set(pszName, hostLength);
|
|
|
|
return InternalParseHostPortName(
|
|
hostLength,
|
|
pPort,
|
|
pszName,
|
|
defaultPort,
|
|
pInstanceNumOut);
|
|
}
|
|
|
|
// Parses a stringified IP address in the form "#.#.#.#" into a host-order IP address.
|
|
// Returns DrError_InvalidParameter if the string is malformed.
|
|
DrError DrNodeAddress::ParseIpAddress(const char *pszIpAddress, /* out */ DrIpAddress *pIpAddr)
|
|
{
|
|
DrIpAddress ipAddr = 0;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (i > 0) {
|
|
ipAddr = ipAddr << 8;
|
|
if (*pszIpAddress != '.') {
|
|
return DrError_InvalidParameter;
|
|
}
|
|
pszIpAddress++;
|
|
}
|
|
if (*pszIpAddress < '0' || *pszIpAddress > '9') {
|
|
return DrError_InvalidParameter;
|
|
}
|
|
UInt32 uByte = 0;
|
|
for (int j = 0; j < 3; j++) {
|
|
char c = *pszIpAddress;
|
|
if (c < '0' || c > '9') {
|
|
break;
|
|
}
|
|
uByte = 10 * uByte + (UInt32)(c - '0');
|
|
if (uByte > 255) {
|
|
return DrError_InvalidParameter;
|
|
}
|
|
pszIpAddress ++;
|
|
}
|
|
ipAddr = ipAddr | uByte;
|
|
}
|
|
if (*pszIpAddress != '\0') {
|
|
return DrError_InvalidParameter;
|
|
}
|
|
|
|
*pIpAddr = ipAddr;
|
|
return DrError_OK;
|
|
}
|
|
|
|
// Parses a name in the form "#.#.#.#:port" or "dns-name:port" and resolves it to an address.
|
|
// If ":port" is missing, uses the default port.
|
|
// If there is more than one address associated with a DNS name, uses the first one.
|
|
// Returns DrError_InvalidParameter if the string is malformed.
|
|
// Note that this method may block for DNS resolution if a DNS name is used.
|
|
DrError DrNodeAddress::Set(const char *pszName, DrPortNumber defaultPort)
|
|
{
|
|
char buff[k_MaxHostNameLength+1];
|
|
DrPortNumber port;
|
|
DrIpAddress ipAddr;
|
|
|
|
DrError err = ParseHostPortName(buff, sizeof(buff), &port, pszName, defaultPort);
|
|
if (err != DrError_OK) {
|
|
return err;
|
|
}
|
|
|
|
// first try to parse it as a numeric IP address
|
|
err = ParseIpAddress(buff, &ipAddr);
|
|
if (err == DrError_OK) {
|
|
Set(ipAddr, port);
|
|
} else {
|
|
UInt32 nRet = 0;
|
|
err = LookupHostName(buff, &ipAddr, 1, &nRet);
|
|
if (err != DrError_OK) {
|
|
return err;
|
|
}
|
|
LogAssert(nRet == 1);
|
|
Set(ipAddr, port);
|
|
}
|
|
|
|
return DrError_OK;
|
|
}
|
|
|
|
// This call may block for DNS
|
|
// It resolves the specified host name (with optional ":port") to a list of IP addresses and *appends* those to this DrNodeAddressList, filling in
|
|
// the port number for each.
|
|
// Note that since this request appends to the existing list, you must Clear() or Discard() the list before you make this
|
|
// call if you want the results to replace the existing set.
|
|
// Returns DrError_HostNotFound if no hosts match the name.
|
|
DrError DrNodeAddressList::ResolveHostName(const char *pszHostName, DrPortNumber defaultPort)
|
|
{
|
|
char buff[k_MaxHostNameLength+1];
|
|
DrPortNumber port;
|
|
DrIpAddress ipAddrs[32];
|
|
UInt32 nResults = 0;
|
|
|
|
DrError err = DrNodeAddress::ParseHostPortName(buff, sizeof(buff), &port, pszHostName, defaultPort);
|
|
if (err != DrError_OK) {
|
|
return err;
|
|
}
|
|
|
|
// first try to parse it as a numeric IP address
|
|
err = DrNodeAddress::ParseIpAddress(buff, &(ipAddrs[0]));
|
|
if (err == DrError_OK) {
|
|
nResults = 1;
|
|
} else {
|
|
err = DrNodeAddress::LookupHostName(buff, ipAddrs, 32, &nResults);
|
|
if (err != DrError_OK) {
|
|
return err;
|
|
}
|
|
LogAssert(nResults > 0);
|
|
}
|
|
|
|
GrowTo(nResults);
|
|
|
|
for (UInt32 i = 0; i < nResults; i++) {
|
|
DrNodeAddress a;
|
|
a.Set(ipAddrs[i], port);
|
|
AddEntry(&a);
|
|
}
|
|
|
|
return DrError_OK;
|
|
}
|
|
|
|
// This call may block for DNS
|
|
// It resolves the host name to a list of IP addresses and *appends* those to the specified DrNodeAddressList, filling in
|
|
// the port number for each.
|
|
// Note that since this request appends to the existing list, you must Clear() or Discard() the list before you make this
|
|
// call if you want the results to replace the existing set.
|
|
// Returns DrError_HostNotFound if no hosts match the name.
|
|
DrError DrHostAndPort::ResolveToAddresses(DrNodeAddressList *pAddresses)
|
|
{
|
|
DrError err = pAddresses->ResolveHostName(m_pszHostName, m_portNumber);
|
|
return err;
|
|
}
|
|
|
|
/* JC
|
|
DrError DrPodNameToFaultDomain(__in PCSTR pszPodName, __out XsFaultDomain *pFaultDomainOut)
|
|
{
|
|
DrError err = DrError_OK;
|
|
*pFaultDomainOut = 0;
|
|
if (pszPodName == NULL || pszPodName[0] == '\0') {
|
|
// just return 0
|
|
} else if (_strnicmp(pszPodName, "pod", 3) == 0) {
|
|
err = DrStringToUInt16(pszPodName+3, pFaultDomainOut);
|
|
} else {
|
|
*pFaultDomainOut = 0;
|
|
err = DrError_InvalidParameter;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
PCSTR DrFaultDomainToPodName( __in XsFaultDomain faultDomain)
|
|
{
|
|
DrStr32 strPod("pod");
|
|
strPod.AppendUInt32((UInt32)faultDomain);
|
|
return g_DrInternalizedStrings.InternalizeString(strPod);
|
|
}
|
|
|
|
DrError DrHostAndPort::ReplaceFaultDomainFromPod()
|
|
{
|
|
m_faultDomain = 0;
|
|
DrError err = DrPodNameToFaultDomain(m_pszPodName, &m_faultDomain);
|
|
m_fValidFaultDomain = (err == DrError_OK);
|
|
return err;
|
|
}
|
|
*/
|
|
|
|
class DrHostAndPortParser : public DrPropertyParser
|
|
{
|
|
public:
|
|
DrHostAndPortParser(DrHostAndPort *pEntry)
|
|
{
|
|
m_pEntry = pEntry;
|
|
}
|
|
|
|
virtual ~DrHostAndPortParser()
|
|
{
|
|
}
|
|
|
|
virtual DrError OnParseProperty(DrMemoryReader *reader, UInt16 enumID, UInt32 dataLen, void *cookie)
|
|
{
|
|
if (reader->GetStatus() == DrError_OK) {
|
|
switch(enumID) {
|
|
case Prop_Dryad_Port:
|
|
{
|
|
UInt16 port;
|
|
|
|
if (reader->ReadNextUInt16Property(Prop_Dryad_Port, &port) == DrError_OK) {
|
|
m_pEntry->SetPort(port);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case Prop_Dryad_ShortHostName:
|
|
case Prop_Dryad_LongHostName:
|
|
{
|
|
const char *pszHost;
|
|
if (reader->ReadNextStringProperty(enumID, &pszHost) == DrError_OK) {
|
|
m_pEntry->SetHostName(pszHost);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* case Prop_Dryad_UpgradeDomain:
|
|
{
|
|
UInt16 upgradeDomain;
|
|
|
|
if (reader->ReadNextUInt16Property(Prop_Dryad_UpgradeDomain, &upgradeDomain) == DrError_OK) {
|
|
m_pEntry->SetUpgradeDomain(upgradeDomain);
|
|
}
|
|
}
|
|
break;
|
|
*/
|
|
// NOTE: current code serializes Prop_Dryad_PodName. This will eventually be deprecated in favor
|
|
// of Prop_Dryad_FaultDomain. Here we accept either in preparation for future deprecation.
|
|
case Prop_Dryad_PodName:
|
|
{
|
|
const char *pszPodName;
|
|
if (reader->ReadNextStringProperty(Prop_Dryad_PodName, &pszPodName) == DrError_OK) {
|
|
m_pEntry->SetPodNameNoFaultDomainUpdate(pszPodName);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/*JC
|
|
// NOTE: old versions don't serialize this way, but eventually in XStore we will switch to this after all unserializers have been updated.
|
|
// To allow for that while maintaining compatibility with the current API, we will reencode the fault domain into pod name form if
|
|
// the pod is not provided.
|
|
case Prop_Dryad_FaultDomain:
|
|
{
|
|
UInt16 uFaultDomain = 0;
|
|
DrError err = reader->ReadNextUInt16Property(Prop_Dryad_FaultDomain, &uFaultDomain);
|
|
if (err == DrError_OK) {
|
|
m_pEntry->SetFaultDomainNoPodUpdate(uFaultDomain);
|
|
}
|
|
}
|
|
break;
|
|
*/
|
|
|
|
default:
|
|
reader->SkipNextPropertyOrAggregate();
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
return reader->GetStatus();
|
|
}
|
|
|
|
private:
|
|
DrHostAndPort *m_pEntry;
|
|
};
|
|
|
|
DrError DrHostAndPort::Unserialize(DrMemoryReader *pReader)
|
|
{
|
|
//JC // We keep track of whether pod and/or fault domain were explicitly provided.
|
|
// We keep track of whether pod was explicitly provided.
|
|
|
|
//JC m_fValidFaultDomain = false;
|
|
m_fValidPod = false;
|
|
//JC m_faultDomain = 0;
|
|
m_pszPodName = NULL;
|
|
|
|
DrHostAndPortParser p(this);
|
|
DrError err = pReader->ReadAggregate(DrTag_DrHostAndPort, &p, NULL);
|
|
|
|
/*JC
|
|
// If fault domain was provided, but pod was not, synthesize the pod. Otherwise, if fault domain
|
|
// was not provided, synthesize it.
|
|
if (m_fValidFaultDomain) {
|
|
if (m_fValidPod) {
|
|
ReplacePodFromFaultDomain();
|
|
}
|
|
} else {
|
|
ReplaceFaultDomainFromPod();
|
|
}
|
|
m_fValidPod = true;
|
|
*/
|
|
|
|
return err;
|
|
}
|
|
|
|
DrError DrHostAndPort::Serialize(DrMemoryWriter *pWriter) const
|
|
{
|
|
pWriter->WriteUInt16Property(Prop_Dryad_BeginTag, DrTag_DrHostAndPort);
|
|
|
|
// BUGBUG: should have been LONGATOM. We serialize a long version if length >= 255
|
|
{
|
|
|
|
size_t length = 0;
|
|
if (m_pszHostName != NULL) {
|
|
length = strlen(m_pszHostName);
|
|
}
|
|
if (length >= (size_t)_UI8_MAX) {
|
|
// BUGBUG: breaks compat for long host names, but no worse than before
|
|
pWriter->WriteLongStringPropertyWithLength(Prop_Dryad_LongHostName, m_pszHostName, length);
|
|
} else {
|
|
pWriter->WriteShortStringPropertyWithLength(Prop_Dryad_ShortHostName, m_pszHostName, length);
|
|
}
|
|
}
|
|
|
|
pWriter->WriteUInt16Property(Prop_Dryad_Port, m_portNumber);
|
|
/*JC if (m_fValidFaultDomain) {
|
|
pWriter->WriteUInt16Property(Prop_Dryad_FaultDomain, m_faultDomain);
|
|
}
|
|
*/
|
|
if (m_fValidPod && m_pszPodName != NULL) {
|
|
// Note: Prop_Dryad_PodName will eventually be deprecated in favor of Prop_Dryad_FaultDomain. We will continue to serialize
|
|
// this way until all unserializers have been updated.
|
|
pWriter->WriteLongStringProperty(Prop_Dryad_PodName, m_pszPodName);
|
|
}
|
|
//JC pWriter->WriteUInt16Property(Prop_Dryad_UpgradeDomain, m_upgradeDomain);
|
|
|
|
pWriter->WriteUInt16Property(Prop_Dryad_EndTag, DrTag_DrHostAndPort);
|
|
|
|
return pWriter->GetStatus();
|
|
}
|
|
|
|
// If this is an update, preserve the ordering of everything already in the list except for the primary, which must be at the top
|
|
// If forceReordering is true, overwrite the existing ordering with the new one (but primary must be first). This is called
|
|
// when we DemoteHost() and are putting the new table back
|
|
DrHostNameList& DrHostNameList::Set(const DrHostNameList& other, bool forceReordering)
|
|
{
|
|
// Sice strings are internalized, we can just copy the pointers
|
|
GrowTo(other.m_numEntries);
|
|
if (other.m_numEntries > 0)
|
|
{
|
|
if (m_numEntries == 0 || forceReordering)
|
|
{
|
|
// There wasn't anything in the existing list, or we want to force reordering, so do a direct copy
|
|
for (UInt32 i = 0; i < other.m_numEntries; i++)
|
|
{
|
|
m_pMultipleHosts[i] = other.m_pMultipleHosts[i];
|
|
}
|
|
|
|
if ((other.m_primary >= other.m_numEntries) || (other.m_primary == INVALID_PRIMARY_HOST))
|
|
{
|
|
// Invalid primary, ignore
|
|
m_primary = INVALID_PRIMARY_HOST;
|
|
}
|
|
else
|
|
{
|
|
if (other.m_primary != 0)
|
|
{
|
|
// Move primary to top
|
|
DrHostAndPort temp = m_pMultipleHosts[0];
|
|
m_pMultipleHosts[0] = m_pMultipleHosts[other.m_primary];
|
|
m_pMultipleHosts[other.m_primary] = temp;
|
|
}
|
|
|
|
m_primary = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We're updating on top of something we already have
|
|
// Ensure that order is preserved, except that if the primary changes, it is always at the top
|
|
|
|
// We will change this below when we encounter the primary
|
|
// Right now we don't know what index this will be in the new list
|
|
m_primary = INVALID_PRIMARY_HOST;
|
|
|
|
// Remove hosts which are not exists in new list
|
|
for (UInt32 i = 0; i < m_numEntries; ++i)
|
|
{
|
|
UInt32 j;
|
|
|
|
// Does the entry available in both local and new lists?
|
|
for (j = 0; j < other.m_numEntries; ++j)
|
|
{
|
|
if (m_pMultipleHosts[i] == other.m_pMultipleHosts[j])
|
|
break;
|
|
}
|
|
|
|
// If host is not found in new list, remove it locally
|
|
if (j == other.m_numEntries)
|
|
{
|
|
--m_numEntries;
|
|
|
|
for (j = i; j < m_numEntries; ++j)
|
|
m_pMultipleHosts[j] = m_pMultipleHosts[j+1];
|
|
|
|
i--;
|
|
}
|
|
}
|
|
|
|
// New entries go at the end
|
|
UInt32 newEntries = m_numEntries;
|
|
|
|
// Add new hosts
|
|
for (UInt32 i = 0; i < other.m_numEntries; i++)
|
|
{
|
|
UInt32 j;
|
|
|
|
// Does the entry already exist?
|
|
for (j = 0; j < m_numEntries; j++)
|
|
{
|
|
if (m_pMultipleHosts[j] == other.m_pMultipleHosts[i])
|
|
break;
|
|
}
|
|
|
|
if (j < m_numEntries)
|
|
{
|
|
// Found - update fields that aren't checked by the == operator above (e.g. upgrade domain)
|
|
m_pMultipleHosts[j] = other.m_pMultipleHosts[i];
|
|
|
|
if (other.m_primary == i)
|
|
m_primary = j;
|
|
}
|
|
else
|
|
{
|
|
LogAssert(newEntries < m_numAllocated);
|
|
|
|
// New entry, append to end of list
|
|
m_pMultipleHosts[newEntries] = other.m_pMultipleHosts[i];
|
|
|
|
// Convert primary from old index to new index
|
|
if (other.m_primary == i)
|
|
m_primary = newEntries;
|
|
|
|
newEntries++;
|
|
}
|
|
}
|
|
|
|
// Now move primary to top of list
|
|
if ((m_primary != INVALID_PRIMARY_HOST) && (m_primary != 0))
|
|
{
|
|
DrHostAndPort temp = m_pMultipleHosts[0];
|
|
m_pMultipleHosts[0] = m_pMultipleHosts[m_primary];
|
|
m_pMultipleHosts[m_primary] = temp;
|
|
|
|
m_primary = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_numEntries = other.m_numEntries;
|
|
return *this;
|
|
}
|
|
|
|
DrError DrHostNameList::Serialize(DrMemoryWriter *pWriter) const{
|
|
pWriter->WriteUInt16Property(Prop_Dryad_BeginTag, DrTag_DrHostNameList);
|
|
|
|
pWriter->WriteUInt32Property(Prop_Dryad_NumEntries, m_numEntries);
|
|
for (UInt32 i = 0; i < m_numEntries; i++) {
|
|
m_pMultipleHosts[i].Serialize(pWriter);
|
|
}
|
|
pWriter->WriteUInt32Property(Prop_Dryad_PrimaryHost, m_primary);
|
|
//pWriter->WriteUInt32Property(Prop_Dryad_NextHost, m_nextHost);
|
|
|
|
pWriter->WriteUInt16Property(Prop_Dryad_EndTag, DrTag_DrHostNameList);
|
|
|
|
return pWriter->GetStatus();
|
|
}
|
|
DrError DrHostNameList::OnParseProperty(DrMemoryReader *reader, UInt16 property, UInt32 dataLen, void *cookie){
|
|
if (reader->GetStatus() == DrError_OK) {
|
|
switch(property) {
|
|
case Prop_Dryad_BeginTag:
|
|
{
|
|
UInt16 tagId;
|
|
if (reader->PeekNextUInt16Property(Prop_Dryad_BeginTag, &tagId) == DrError_OK) {
|
|
switch(tagId) {
|
|
case DrTag_DrHostAndPort:
|
|
{
|
|
DrHostAndPort *pHost = AddEntry();
|
|
pHost->Unserialize(reader);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
reader->SetStatus(DrError_InvalidProperty);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case Prop_Dryad_NumEntries:
|
|
{
|
|
UInt32 numEntries;
|
|
if (reader->ReadNextUInt32Property(Prop_Dryad_NumEntries, &numEntries) == DrError_OK) {
|
|
GrowTo(numEntries);
|
|
}
|
|
}
|
|
break;
|
|
case Prop_Dryad_PrimaryHost:
|
|
{
|
|
UInt32 primary;
|
|
if(reader->ReadNextUInt32Property(Prop_Dryad_PrimaryHost, &primary) == DrError_OK){
|
|
SetPrimary(primary);
|
|
}
|
|
}
|
|
break;
|
|
case Prop_Dryad_NextHost:
|
|
{
|
|
UInt32 nextHost;
|
|
if(reader->ReadNextUInt32Property(Prop_Dryad_NextHost, &nextHost) == DrError_OK){
|
|
//SetNextHost(nextHost);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
reader->SkipNextPropertyOrAggregate();
|
|
break;
|
|
}
|
|
|
|
}
|
|
return reader->GetStatus();
|
|
}
|
|
|
|
|
|
void DrHostNameList::SelectOneHost(DrHostAndPort &host, bool wantPrimary)
|
|
{
|
|
// The primary (or next node to try) is always the first entry in the list
|
|
host.Set(m_pMultipleHosts[0]);
|
|
}
|
|
|
|
|
|
// This call may block for DNS
|
|
// It resolves the list of host names to a list of IP addresses and *appends* those to the specified DrNodeAddressList, filling in
|
|
// the port number for each.
|
|
// Note that since this request appends to the existing list, you must Clear() or Discard() the list before you make this
|
|
// call if you want the results to replace the existing set.
|
|
// Returns DrError_HostNotFound if no hosts match the name.
|
|
DrError DrHostNameList::ResolveToAddresses(DrNodeAddressList *pAddresses)
|
|
{
|
|
bool errSet = false;
|
|
DrError err = DrError_HostNotFound;
|
|
for (UInt32 i = 0; i < m_numEntries; i++) {
|
|
DrError err2 = m_pMultipleHosts[i].ResolveToAddresses(pAddresses);
|
|
// we succeed this call if at least one host was resolved successfully. Otherwise, we fail with the first
|
|
// error returned.
|
|
if (!errSet || err2 == DrError_OK) {
|
|
err = err2;
|
|
errSet = true;
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
DrError DrHostNameList::ResolveOneHostToAddresses(DrNodeAddressList *pAddressses, bool wantPrimary, DrHostAndPort &host){
|
|
SelectOneHost(host, wantPrimary);
|
|
return host.ResolveToAddresses(pAddressses);
|
|
}
|
|
|
|
|
|
DrLastAccessTable::DrLastAccessTable()
|
|
{
|
|
memset(m_head, 0, sizeof(m_head));
|
|
}
|
|
|
|
DrLastAccessEntry* DrLastAccessTable::FindOrCreate(const DrNodeAddress& nodeAddress)
|
|
{
|
|
DrLastAccessEntry *entry = Find(nodeAddress);
|
|
if (entry != NULL)
|
|
return entry;
|
|
|
|
UInt32 bucket = nodeAddress.Hash() % k_numLastAccessTableBuckets;
|
|
entry = new DrLastAccessEntry();
|
|
entry->m_nodeAddress = nodeAddress;
|
|
entry->m_nextHash = m_head[bucket];
|
|
m_head[bucket] = entry;
|
|
return entry;
|
|
}
|
|
|
|
DrLastAccessEntry* DrLastAccessTable::Find(const DrNodeAddress& nodeAddress)
|
|
{
|
|
UInt32 bucket = nodeAddress.Hash() % k_numLastAccessTableBuckets;
|
|
|
|
for (DrLastAccessEntry* search = m_head[bucket]; search != NULL; search = search->m_nextHash)
|
|
{
|
|
if (search->m_nodeAddress == nodeAddress)
|
|
return search;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void DrLastAccessTable::UpdateSuccess(const DrNodeAddress& nodeAddress)
|
|
{
|
|
Lock();
|
|
|
|
DrLastAccessEntry* entry = FindOrCreate(nodeAddress);
|
|
|
|
entry->m_nextAttemptAllowed = DrTimeStamp_LongAgo;
|
|
entry->m_delayTime = 0;
|
|
entry->m_lastError = DrError_OK;
|
|
|
|
Unlock();
|
|
}
|
|
|
|
// Send failure.
|
|
// Returns true if we were already at the maximum allowed delay value
|
|
bool DrLastAccessTable::UpdateFailure(const DrNodeAddress& nodeAddress, DrError error)
|
|
{
|
|
Lock();
|
|
|
|
DrLastAccessEntry* entry = FindOrCreate(nodeAddress);
|
|
bool wasMax;
|
|
|
|
wasMax = (entry->m_delayTime >= k_maxDelayedSendInterval);
|
|
|
|
if (!wasMax && (error == DrError_TalkToPrimaryServer))
|
|
{
|
|
entry->m_delayTime = entry->m_delayTime*2 + k_initialDelayedSendTimeInterval;
|
|
if (entry->m_delayTime > k_maxDelayedSendInterval)
|
|
entry->m_delayTime = k_maxDelayedSendInterval;
|
|
}
|
|
|
|
entry->m_nextAttemptAllowed = DrGetCurrentTimeStamp() + entry->m_delayTime;
|
|
entry->m_lastError = error;
|
|
|
|
Unlock();
|
|
return wasMax;
|
|
}
|
|
|
|
// When can we send to this node address?
|
|
// *when will be DrTimeStamp_LongAgo if there is no delay associated
|
|
DrError DrLastAccessTable::GetDelay(const DrNodeAddress& nodeAddress, DrTimeStamp* when)
|
|
{
|
|
DrError error;
|
|
Lock();
|
|
|
|
DrLastAccessEntry* entry = Find(nodeAddress);
|
|
if (entry == NULL)
|
|
{
|
|
*when = DrTimeStamp_LongAgo;
|
|
error = DrError_OK;
|
|
}
|
|
else
|
|
{
|
|
*when = entry->m_nextAttemptAllowed;
|
|
error = entry->m_lastError;
|
|
}
|
|
|
|
Unlock();
|
|
|
|
return error;
|
|
}
|