bool CUser::IsHostAllowed(const CString& sHostMask) const { if (m_ssAllowedHosts.empty()) { return true; } for (const CString& sHost : m_ssAllowedHosts) { if (sHostMask.WildCmp(sHost)) { return true; } else { // CIDR notation checker, e.g. "192.0.2.0/24" or "2001:db8::/32" // Try to split the string into an IP and routing prefix VCString vsSplitCIDR; if (sHost.Split("/", vsSplitCIDR, false) != 2) continue; const CString sRoutingPrefix = vsSplitCIDR.back(); const int iRoutingPrefix = sRoutingPrefix.ToInt(); if (iRoutingPrefix < 0 || iRoutingPrefix > 128) continue; // If iRoutingPrefix is 0, it could be due to ToInt() failing, so // sRoutingPrefix needs to be all zeroes if (iRoutingPrefix == 0 && sRoutingPrefix != "0") continue; // Convert each IP from a numeric string to an addrinfo addrinfo aiHints; memset(&aiHints, 0, sizeof(addrinfo)); aiHints.ai_flags = AI_NUMERICHOST; addrinfo* aiHost; int iIsHostValid = getaddrinfo(sHostMask.c_str(), nullptr, &aiHints, &aiHost); if (iIsHostValid != 0) continue; aiHints.ai_family = aiHost->ai_family; // Host and range must be in // the same address family addrinfo* aiRange; int iIsRangeValid = getaddrinfo(vsSplitCIDR.front().c_str(), nullptr, &aiHints, &aiRange); if (iIsRangeValid != 0) { freeaddrinfo(aiHost); continue; } // "/0" allows all IPv[4|6] addresses if (iRoutingPrefix == 0) { freeaddrinfo(aiHost); freeaddrinfo(aiRange); return true; } // If both IPs are valid and of the same type, make a bit field mask // from the routing prefix, AND it to the host and range, and see if // they match bool bIsHostInRange = false; if (aiHost->ai_family == AF_INET) { if (iRoutingPrefix > 32) { freeaddrinfo(aiHost); freeaddrinfo(aiRange); continue; } const sockaddr_in* saHost = (sockaddr_in*)(aiHost->ai_addr); const sockaddr_in* saRange = (sockaddr_in*)(aiRange->ai_addr); // Make IPv4 bitmask const in_addr_t inBitmask = htonl((~0u) << (32 - iRoutingPrefix)); // Compare masked IPv4s bIsHostInRange = ((inBitmask & saHost->sin_addr.s_addr) == (inBitmask & saRange->sin_addr.s_addr)); } else if (aiHost->ai_family == AF_INET6) { // Make IPv6 bitmask in6_addr in6aBitmask; memset(&in6aBitmask, 0, sizeof(in6aBitmask)); for (int i = 0, iBitsLeft = iRoutingPrefix; iBitsLeft > 0; ++i, iBitsLeft -= 8) { if (iBitsLeft >= 8) { in6aBitmask.s6_addr[i] = (uint8_t)(~0u); } else { in6aBitmask.s6_addr[i] = (uint8_t)(~0u) << (8 - iBitsLeft); } } // Compare masked IPv6s bIsHostInRange = true; const sockaddr_in6* sa6Host = (sockaddr_in6*)(aiHost->ai_addr); const sockaddr_in6* sa6Range = (sockaddr_in6*)(aiRange->ai_addr); for (int i = 0; i < 16; ++i) { if ((in6aBitmask.s6_addr[i] & sa6Host->sin6_addr.s6_addr[i]) != (in6aBitmask.s6_addr[i] & sa6Range->sin6_addr.s6_addr[i])) { bIsHostInRange = false; } } } freeaddrinfo(aiHost); freeaddrinfo(aiRange); if (bIsHostInRange) return true; } } return false; }