void CHTTPSock::ReadLine(const CString& sData) { if (m_bGotHeader) { return; } CString sLine = sData; sLine.TrimRight("\r\n"); CString sName = sLine.Token(0); if (sName.Equals("GET")) { m_bPost = false; m_sURI = sLine.Token(1); m_bHTTP10Client = sLine.Token(2).Equals("HTTP/1.0"); ParseURI(); } else if (sName.Equals("POST")) { m_bPost = true; m_sURI = sLine.Token(1); ParseURI(); } else if (sName.Equals("Cookie:")) { VCString vsNV; sLine.Token(1, true).Split(";", vsNV, false, "", "", true, true); for (const CString& s : vsNV) { m_msRequestCookies[s.Token(0, false, "=").Escape_n(CString::EURL, CString::EASCII)] = s.Token(1, true, "=").Escape_n(CString::EURL, CString::EASCII); } } else if (sName.Equals("Authorization:")) { CString sUnhashed; sLine.Token(2).Base64Decode(sUnhashed); m_sUser = sUnhashed.Token(0, false, ":"); m_sPass = sUnhashed.Token(1, true, ":"); m_bBasicAuth = true; // Postpone authorization attempt until end of headers, because cookies should be read before that, otherwise session id will be overwritten in GetSession() } else if (sName.Equals("Content-Length:")) { m_uPostLen = sLine.Token(1).ToULong(); if (m_uPostLen > MAX_POST_SIZE) PrintErrorPage(413, "Request Entity Too Large", "The request you sent was too large."); } else if (sName.Equals("X-Forwarded-For:")) { // X-Forwarded-For: client, proxy1, proxy2 if (m_sForwardedIP.empty()) { const VCString& vsTrustedProxies = CZNC::Get().GetTrustedProxies(); CString sIP = GetRemoteIP(); VCString vsIPs; sLine.Token(1, true).Split(",", vsIPs, false, "", "", false, true); while (!vsIPs.empty()) { // sIP told us that it got connection from vsIPs.back() // check if sIP is trusted proxy bool bTrusted = false; for (const CString& sTrustedProxy : vsTrustedProxies) { if (sIP.WildCmp(sTrustedProxy)) { bTrusted = true; break; } } if (bTrusted) { // sIP is trusted proxy, so use vsIPs.back() as new sIP sIP = vsIPs.back(); vsIPs.pop_back(); } else { break; } } // either sIP is not trusted proxy, or it's in the beginning of the X-Forwarded-For list // in both cases use it as the endpoind m_sForwardedIP = sIP; } } else if (sName.Equals("If-None-Match:")) { // this is for proper client cache support (HTTP 304) on static files: m_sIfNoneMatch = sLine.Token(1, true); } else if (sName.Equals("Accept-Encoding:") && !m_bHTTP10Client) { SCString ssEncodings; // trimming whitespace from the tokens is important: sLine.Token(1, true).Split(",", ssEncodings, false, "", "", false, true); m_bAcceptGzip = (ssEncodings.find("gzip") != ssEncodings.end()); } else if (sLine.empty()) { if (m_bBasicAuth && !m_bLoggedIn) { m_bLoggedIn = OnLogin(m_sUser, m_sPass, true); // After successful login ReadLine("") will be called again to trigger "else" block // Failed login sends error and closes socket, so no infinite loop here } else { m_bGotHeader = true; if (m_bPost) { m_sPostData = GetInternalReadBuffer(); CheckPost(); } else { GetPage(); } DisableReadLine(); } } }
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; }