void TCPConnection::AsyncHandshakeCompleted(const boost::system::error_code& error) { handshake_in_progress_ = false; if (!error) { is_ssl_ = true; // Send welcome message to client. auto cipher_info = GetCipherInfo(); String sMessage; sMessage.Format(_T("TCPConnection - TLS/SSL handshake completed. Session Id: %d, Remote IP: %s, Version: %s, Cipher: %s, Bits: %d"), session_id_, SafeGetIPAddress().c_str(), String(cipher_info.GetVersion()).c_str(), String(cipher_info.GetName()).c_str(), cipher_info.GetBits()); LOG_TCPIP(sMessage); receive_buffer_.consume(receive_buffer_.size()); OnHandshakeCompleted(); } else { HandshakeFailed_(error); } operation_queue_.Pop(IOOperation::BCTHandshake); ProcessOperationQueue_(0); }
void TCPConnection::StartAsyncConnect_(const String &ip_adress, int port) { IPAddress adress; adress.TryParse(ip_adress, true); tcp::endpoint ep; ep.address(adress.GetAddress()); ep.port(port); //// Check that we don't try to connect to a port we're listening on. Doing //// that could start evil loops. //tcp::endpoint endpoint = *endpoint_iterator; if (LocalIPAddresses::Instance()->IsLocalPort(ep.address(), remote_port_)) { String sMessage; sMessage.Format(_T("Could not connect to %s on port %d since this would mean connecting to myself."), remote_ip_address_.c_str(), remote_port_); OnCouldNotConnect(sMessage); LOG_TCPIP(_T("TCPConnection - ") + sMessage); return; } // Attempt a connection to the first endpoint in the list. Each endpoint // will be tried until we successfully establish a connection. socket_.async_connect(ep, std::bind(&TCPConnection::AsyncConnectCompleted, shared_from_this(), std::placeholders::_1)); }
bool TCPConnection::Connect(const AnsiString &remote_ip_address, long remotePort, const IPAddress &localAddress) { #if _DEBUG if (!StringParser::IsValidIPAddress(remote_ip_address)) { ErrorManager::Instance()->ReportError(ErrorManager::High, 5506, "TCPConnection::Connect", Formatter::Format("Attempting to connect to {0} - Not a valid IP address.", remote_ip_address)); } #endif remote_port_ = remotePort; remote_ip_address_ = remote_ip_address; is_client_ = true; LOG_TCPIP(Formatter::Format("Connecting to {0}:{1}...", remote_ip_address_, remotePort)); if (!localAddress.IsAny()) { boost::system::error_code error_code; if (localAddress.GetType() == IPAddress::IPV4) socket_.open(boost::asio::ip::tcp::v4(), error_code); else if (localAddress.GetType() == IPAddress::IPV6) socket_.open(boost::asio::ip::tcp::v6(), error_code); if (error_code) { String errorMessage = Formatter::Format("Failed to open local socket on IP address {0}", localAddress.ToString()); OnCouldNotConnect(errorMessage); ReportError(ErrorManager::Medium, 5520, "TCPConnection::Connect", errorMessage, error_code); return false; } socket_.bind(boost::asio::ip::tcp::endpoint(localAddress.GetAddress(), 0), error_code); if (error_code) { String errorMessage = Formatter::Format("Failed to bind to IP address {0}.", localAddress.ToString()); ReportError(ErrorManager::Medium, 4330, "TCPConnection::Connect", errorMessage, error_code); OnCouldNotConnect(errorMessage); boost::system::error_code ignored_error_code; socket_.close(ignored_error_code); return false; } } // Start an asynchronous resolve to translate the server and service names // into a list of endpoints. StartAsyncConnect_(remote_ip_address, remotePort); return true; }
void TCPConnection::HandshakeFailed_(const boost::system::error_code& error) { // The SSL handshake failed. This may happen for example if the user who has connected // to the TCP/IP port disconnects immediately without sending any data. String sMessage; sMessage.Format(_T("TCPConnection - TLS/SSL handshake failed. Session Id: %d, Remote IP: %s, Error code: %d, Message: %s"), session_id_, SafeGetIPAddress().c_str(), error.value(), String(error.message()).c_str()); LOG_TCPIP(sMessage); OnHandshakeFailed(); }
void TCPServer::HandleAccept(shared_ptr<TCPConnection> pConnection, const boost::system::error_code& error) { if (error.value() == 995) { String sMessage; sMessage.Format(_T("TCP - AcceptEx failed. Error code: %d, Message: %s"), error.value(), String(error.message())); LOG_DEBUG(sMessage); /* 995: The I/O operation has been aborted because of either a thread exit or an application request This happens when the servers are stopped. We shouldn't post any new accepts or do anything else in this situation. */ return; } // Post another AcceptEx. We should always have outstanding unless the // server is stopping, which we are taking care of above. StartAccept(); if (!error) { boost::asio::ip::tcp::endpoint localEndpoint = pConnection->GetSocket().local_endpoint(); boost::asio::ip::tcp::endpoint remoteEndpoint = pConnection->GetSocket().remote_endpoint(); IPAddress localAddress (localEndpoint.address()); IPAddress remoteAddress (remoteEndpoint.address()); String sMessage = Formatter::Format("TCP - {0} connected to {1}:{2}.", remoteAddress.ToString(), localAddress.ToString(), port_); LOG_TCPIP(sMessage); shared_ptr<SecurityRange> securityRange = PersistentSecurityRange::ReadMatchingIP(remoteAddress); if (!securityRange) { LOG_TCPIP("TCP - Connection dropped - No matching IP range."); return; } bool allow = SessionManager::Instance()->GetAllow(sessionType_, securityRange); if (!allow) { // Session creation failed. May not be matching IP range, or enough connections have been created. String message; message.Format(_T("Client connection from %s was not accepted. Blocked either by IP range or by connection limit."), String(remoteAddress.ToString())); LOG_DEBUG(message); // Give option to hold connection for anti-pounding & hopefully minimize DoS // NOTE: We really need max connections per IP as well int iBlockedIPHoldSeconds = IniFileSettings::Instance()->GetBlockedIPHoldSeconds(); if (iBlockedIPHoldSeconds > 0) { Sleep(iBlockedIPHoldSeconds * 1000); message.Format(_T("Held connection from %s for %i seconds before dropping."), String(remoteAddress.ToString()), iBlockedIPHoldSeconds); LOG_DEBUG(message); } return; } if (!FireOnAcceptEvent(remoteAddress, localEndpoint.port())) return; pConnection->SetSecurityRange(securityRange); pConnection->Start(); } else { if (error.value() == 10009 || error.value() == 995) { // Not really errors.. return; } // The outstanding accept-ex failed. This may or may not be an error. Default to being positive. String sMessage; sMessage.Format(_T("TCP - AcceptEx failed. Error code: %d, Message: %s"), error.value(), String(error.message())); LOG_TCPIP(sMessage); } }
bool DNSResolver::Resolve_(const String &sSearchFor, std::vector<String> &vecFoundNames, WORD wType, int iRecursion) { if (iRecursion > 10) { String sMessage = Formatter::Format("Too many recursions during query. Query: {0}, Type: {1}", sSearchFor, wType); ErrorManager::Instance()->ReportError(ErrorManager::Low, 4401, "DNSResolver::Resolve_", sMessage); return false; } PDNS_RECORD pDnsRecord = NULL; PIP4_ARRAY pSrvList = NULL; DWORD fOptions = DNS_QUERY_STANDARD; DNS_STATUS nDnsStatus = DnsQuery(sSearchFor, wType, fOptions, NULL, &pDnsRecord, NULL); PDNS_RECORD pDnsRecordsToDelete = pDnsRecord; if (nDnsStatus != 0) { if (pDnsRecordsToDelete) _FreeDNSRecord(pDnsRecordsToDelete); bool bDNSError = IsDNSError_(nDnsStatus); if (bDNSError) { String sMessage; sMessage.Format(_T("DNS - Query failure. Query: %s, Type: %d, DnsQuery return value: %d."), sSearchFor.c_str(), wType, nDnsStatus); LOG_TCPIP(sMessage); return false; } return true; } std::vector<DnsRecordWithPreference> foundDnsRecordsWithPreference; do { switch (pDnsRecord->wType) { case DNS_TYPE_A: { SOCKADDR_IN addr; memset(&addr, 0, sizeof addr); addr.sin_family = AF_INET; addr.sin_addr = *((in_addr*)&(pDnsRecord->Data.AAAA.Ip6Address)); char buf[128]; DWORD bufSize = sizeof(buf); if (WSAAddressToStringA((sockaddr*)&addr, sizeof addr, NULL, buf, &bufSize) == 0) { DnsRecordWithPreference rec(0, buf); foundDnsRecordsWithPreference.push_back(rec); } break; } case DNS_TYPE_AAAA: { SOCKADDR_IN6 addr; memset(&addr, 0, sizeof addr); addr.sin6_family = AF_INET6; addr.sin6_addr = *((in_addr6*)&(pDnsRecord->Data.AAAA.Ip6Address)); char buf[128]; DWORD bufSize = sizeof(buf); if (WSAAddressToStringA((sockaddr*)&addr, sizeof addr, NULL, buf, &bufSize) == 0) { DnsRecordWithPreference rec(0, buf); foundDnsRecordsWithPreference.push_back(rec); } break; } case DNS_TYPE_CNAME: { String sDomainName = pDnsRecord->Data.CNAME.pNameHost; if (!Resolve_(sDomainName, vecFoundNames, wType, iRecursion+1)) return false; break; } case DNS_TYPE_MX: { String sName = pDnsRecord->pName; bool bNameMatches = (sName.CompareNoCase(sSearchFor) == 0); if (pDnsRecord->Flags.S.Section == DNSREC_ANSWER && bNameMatches) { DnsRecordWithPreference rec(pDnsRecord->Data.MX.wPreference, pDnsRecord->Data.MX.pNameExchange); foundDnsRecordsWithPreference.push_back(rec); } break; } case DNS_TYPE_TEXT: { AnsiString retVal; for (u_int i = 0; i < pDnsRecord->Data.TXT.dwStringCount; i++) retVal += pDnsRecord->Data.TXT.pStringArray[i]; DnsRecordWithPreference rec (0, retVal); foundDnsRecordsWithPreference.push_back(rec); break; } case DNS_TYPE_PTR: { AnsiString retVal; retVal = pDnsRecord->Data.PTR.pNameHost; DnsRecordWithPreference rec(0, retVal); foundDnsRecordsWithPreference.push_back(rec); break; } default: { ErrorManager::Instance()->ReportError(ErrorManager::Medium, 5036, "DNSResolver::Resolve_", Formatter::Format("Queried for {0} but received type {1}", wType, pDnsRecord->wType)); break; } } pDnsRecord = pDnsRecord->pNext; } while (pDnsRecord != nullptr); std::sort(foundDnsRecordsWithPreference.begin(), foundDnsRecordsWithPreference.end(), SortDnsRecordWithPreference); for (DnsRecordWithPreference item : foundDnsRecordsWithPreference) { vecFoundNames.push_back(item.Value); } _FreeDNSRecord(pDnsRecordsToDelete); pDnsRecordsToDelete = 0; return true; }
bool DNSResolver::GetEmailServers(const String &sDomainName, std::vector<HostNameAndIpAddress> &saFoundNames ) { String message = Formatter::Format("DNS MX lookup: {0}", sDomainName); LOG_TCPIP(message); std::vector<String> vecFoundMXRecords; if (!Resolve_(sDomainName, vecFoundMXRecords, DNS_TYPE_MX, 0)) { String logMessage; logMessage.Format(_T("Failed to resolve email servers (MX lookup). Domain name: %s."), sDomainName.c_str()); LOG_DEBUG(logMessage); return false; } if (vecFoundMXRecords.empty()) { /* RFC 2821: If no MX records are found, but an A RR is found, the A RR is treated as if it was associated with an implicit MX RR, with a preference of 0, pointing to that host. If one or more MX RRs are found for a given name, SMTP systems MUST NOT utilize any A RRs associated with that name unless they are located using the MX RRs; (implemented here) */ std::vector<String> a_records; if (!GetIpAddresses(sDomainName, a_records)) { String logMessage; logMessage.Format(_T("Failed to resolve email servers (A lookup). Domain name: %s."), sDomainName.c_str()); LOG_DEBUG(logMessage); return false; } for(String record : a_records) { HostNameAndIpAddress hostAndAddress; hostAndAddress.SetHostName(sDomainName); hostAndAddress.SetIpAddress(record); saFoundNames.push_back(hostAndAddress); } } else { // We've been able to find host names in the MX records. We should // now translate them to IP addresses. Some host names may result // in several IP addreses. auto iterDomain = vecFoundMXRecords.begin(); bool dnsSuccess = false; for(String domain : vecFoundMXRecords) { // Resolve to domain name to IP address and put it in the list. size_t iCountBefore = saFoundNames.size(); std::vector<String> a_records; if (!GetIpAddresses(domain, a_records)) continue; dnsSuccess = true; if (saFoundNames.size() == iCountBefore) { // No mx records were found for this host name. Check if the host // name is actually an IP address? It shouldn't be but.... if (StringParser::IsValidIPAddress(domain)) { // Okay, this is an invalid MX record. The MX record should always contain // a host name but in this case it appears an IP address. We'll be kind to // the domain owner and still deliver the email to him. a_records.push_back(domain); } } for(String record : a_records) { HostNameAndIpAddress hostAndAddress; hostAndAddress.SetHostName(domain); hostAndAddress.SetIpAddress(record); saFoundNames.push_back(hostAndAddress); } } if (!dnsSuccess) { // All dns queries failed. String logMessage; logMessage.Format(_T("Failed to resolve email servers (A lookup). Domain name: %s."), sDomainName.c_str()); LOG_DEBUG(logMessage); return false; } } String sLogMsg; sLogMsg.Format(_T("DNS - MX Result: %d IP addresses were found."), saFoundNames.size()); LOG_TCPIP(sLogMsg); // Remove duplicate names. auto iter = saFoundNames.begin(); std::set<String> duplicateCheck; while (iter != saFoundNames.end()) { String name = (*iter).GetIpAddress(); if (duplicateCheck.find(name) != duplicateCheck.end()) { // We found a duplicate. Remove it. iter = saFoundNames.erase(iter); } else { // This is not a duplicate. Move to next. iter++; duplicateCheck.insert(name); } } return true; }