//Get signature data from server via UDP request bool DNSCurve_SignatureRequest_UDP( const uint16_t Protocol, const bool IsAlternate) { //Initialization const auto SendBuffer = std::make_unique<uint8_t[]>(PACKET_NORMAL_MAXSIZE + MEMORY_RESERVED_BYTES); const auto RecvBuffer = std::make_unique<uint8_t[]>(PACKET_NORMAL_MAXSIZE + MEMORY_RESERVED_BYTES); memset(SendBuffer.get(), 0, PACKET_NORMAL_MAXSIZE + MEMORY_RESERVED_BYTES); memset(RecvBuffer.get(), 0, PACKET_NORMAL_MAXSIZE + MEMORY_RESERVED_BYTES); std::vector<SOCKET_DATA> UDPSocketDataList(1U); memset(&UDPSocketDataList.front(), 0, sizeof(UDPSocketDataList.front())); UDPSocketDataList.front().Socket = INVALID_SOCKET; //Make packet data(Part 1). const auto DNS_Header = reinterpret_cast<dns_hdr *>(SendBuffer.get()); auto DataLength = sizeof(dns_hdr); #if defined(ENABLE_PCAP) DNS_Header->ID = Parameter.DomainTest_ID; #else GenerateRandomBuffer(&DNS_Header->ID, sizeof(DNS_Header->ID), nullptr, 0, 0); #endif DNS_Header->Flags = hton16(DNS_FLAG_REQUEST_STANDARD); DNS_Header->Question = hton16(UINT16_NUM_ONE); if (Protocol == AF_INET6) { if (IsAlternate) DataLength += StringToPacketQuery(DNSCurveParameter.DNSCurve_Target_Server_Alternate_IPv6.ProviderName, SendBuffer.get() + DataLength, PACKET_NORMAL_MAXSIZE - DataLength); else //Main DataLength += StringToPacketQuery(DNSCurveParameter.DNSCurve_Target_Server_Main_IPv6.ProviderName, SendBuffer.get() + DataLength, PACKET_NORMAL_MAXSIZE - DataLength); } else if (Protocol == AF_INET) { if (IsAlternate) DataLength += StringToPacketQuery(DNSCurveParameter.DNSCurve_Target_Server_Alternate_IPv4.ProviderName, SendBuffer.get() + DataLength, PACKET_NORMAL_MAXSIZE - DataLength); else //Main DataLength += StringToPacketQuery(DNSCurveParameter.DNSCurve_Target_Server_Main_IPv4.ProviderName, SendBuffer.get() + DataLength, PACKET_NORMAL_MAXSIZE - DataLength); } else { return false; } reinterpret_cast<dns_qry *>(SendBuffer.get() + DataLength)->Type = hton16(DNS_TYPE_TEXT); reinterpret_cast<dns_qry *>(SendBuffer.get() + DataLength)->Classes = hton16(DNS_CLASS_INTERNET); DataLength += sizeof(dns_qry); //EDNS Label DataLength = Add_EDNS_LabelToPacket(SendBuffer.get(), DataLength, PACKET_NORMAL_MAXSIZE, nullptr); //Socket initialization(Part 1) std::wstring Message; size_t TotalSleepTime = 0; ssize_t RecvLen = 0; auto FileModifiedTime = GlobalRunningStatus.ConfigFileModifiedTime; auto ServerType = DNSCURVE_SERVER_TYPE::NONE; const auto PacketTarget = DNSCurve_SelectSignatureTargetSocket(Protocol, IsAlternate, ServerType, UDPSocketDataList); if (PacketTarget == nullptr) { SocketSetting(UDPSocketDataList.front().Socket, SOCKET_SETTING_TYPE::CLOSE, false, nullptr); PrintError(LOG_LEVEL_TYPE::LEVEL_2, LOG_ERROR_TYPE::SYSTEM, L"DNSCurve UDP Signature Request module Monitor terminated", 0, nullptr, 0); return false; } //Send request. while (!GlobalRunningStatus.IsNeedExit) { //Sleep time controller if (TotalSleepTime > 0) { //Configuration files have been changed. if (FileModifiedTime != GlobalRunningStatus.ConfigFileModifiedTime) { FileModifiedTime = GlobalRunningStatus.ConfigFileModifiedTime; TotalSleepTime = 0; } //Interval time is not enough. else if (TotalSleepTime < DNSCurveParameter.KeyRecheckTime) { TotalSleepTime += Parameter.FileRefreshTime; //Next loop Sleep(Parameter.FileRefreshTime); continue; } //Interval time is enough, next recheck time. else { TotalSleepTime = 0; } } //Socket initialization(Part 2) if (Protocol == AF_INET6) UDPSocketDataList.front().Socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); else if (Protocol == AF_INET) UDPSocketDataList.front().Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); else goto JumpTo_Restart; if (!SocketSetting(UDPSocketDataList.front().Socket, SOCKET_SETTING_TYPE::INVALID_CHECK, true, nullptr) || !SocketSetting(UDPSocketDataList.front().Socket, SOCKET_SETTING_TYPE::NON_BLOCKING_MODE, true, nullptr) || (Protocol == AF_INET6 && !SocketSetting(UDPSocketDataList.front().Socket, SOCKET_SETTING_TYPE::HOP_LIMITS_IPV6, true, nullptr)) || (Protocol == AF_INET && (!SocketSetting(UDPSocketDataList.front().Socket, SOCKET_SETTING_TYPE::HOP_LIMITS_IPV4, true, nullptr) || !SocketSetting(UDPSocketDataList.front().Socket, SOCKET_SETTING_TYPE::DO_NOT_FRAGMENT, true, nullptr)))) goto JumpTo_Restart; //Socket selecting RecvLen = SocketSelectingOnce(REQUEST_PROCESS_TYPE::DNSCURVE_SIGN, IPPROTO_UDP, UDPSocketDataList, nullptr, SendBuffer.get(), DataLength, RecvBuffer.get(), PACKET_NORMAL_MAXSIZE, nullptr, nullptr); if (RecvLen < static_cast<const ssize_t>(DNS_PACKET_MINSIZE)) { goto JumpTo_Restart; } else { //Check signature. if (!DNSCruve_GetSignatureData(RecvBuffer.get() + DNS_PACKET_RR_LOCATE(RecvBuffer.get(), PACKET_NORMAL_MAXSIZE), ServerType) || CheckEmptyBuffer(PacketTarget->ServerFingerprint, crypto_box_PUBLICKEYBYTES) || CheckEmptyBuffer(PacketTarget->SendMagicNumber, DNSCURVE_MAGIC_QUERY_LEN)) goto JumpTo_Restart; } //Wait for sending again. TotalSleepTime += Parameter.FileRefreshTime; continue; //Jump here to restart. JumpTo_Restart: SocketSetting(UDPSocketDataList.front().Socket, SOCKET_SETTING_TYPE::CLOSE, false, nullptr); //Print error log. PrintLog_DNSCurve(ServerType, Message); if (!Message.empty()) { Message.append(L"UDP get signature data error"); PrintError(LOG_LEVEL_TYPE::LEVEL_3, LOG_ERROR_TYPE::DNSCURVE, Message.c_str(), 0, nullptr, 0); } else { PrintError(LOG_LEVEL_TYPE::LEVEL_3, LOG_ERROR_TYPE::DNSCURVE, L"UDP get signature data error", 0, nullptr, 0); } //Send request again. memset(RecvBuffer.get(), 0, PACKET_NORMAL_MAXSIZE + MEMORY_RESERVED_BYTES); if (!Parameter.AlternateMultipleRequest) { if (ServerType == DNSCURVE_SERVER_TYPE::MAIN_IPV6) ++AlternateSwapList.TimeoutTimes.at(ALTERNATE_SWAP_TYPE_DNSCURVE_UDP_IPV6); else if (ServerType == DNSCURVE_SERVER_TYPE::MAIN_IPV4) ++AlternateSwapList.TimeoutTimes.at(ALTERNATE_SWAP_TYPE_DNSCURVE_UDP_IPV4); } //Next loop Sleep(SENDING_INTERVAL_TIME); } //Loop terminated SocketSetting(UDPSocketDataList.front().Socket, SOCKET_SETTING_TYPE::CLOSE, false, nullptr); if (!GlobalRunningStatus.IsNeedExit) PrintError(LOG_LEVEL_TYPE::LEVEL_2, LOG_ERROR_TYPE::SYSTEM, L"DNSCurve UDP Signature Request module Monitor terminated", 0, nullptr, 0); return true; }
//Mark responses to domains Cache bool __fastcall MarkDomainCache(const char *Buffer, const size_t Length) { //Check conditions. auto DNS_Header = (pdns_hdr)Buffer; if ( //Not a response packet (ntohs(DNS_Header->Flags) & DNS_GET_BIT_RESPONSE) == 0 || //Question Resource Records must be one. DNS_Header->Questions != htons(U16_NUM_ONE) || //Not any Answer Resource Records DNS_Header->Answer == 0 /* && DNS_Header->Authority == 0 && DNS_Header->Additional == 0 */ || //OPCode must be set Query/0. (ntohs(DNS_Header->Flags) & DNS_GET_BIT_OPCODE) != DNS_OPCODE_QUERY || //Truncated bit must not be set. (ntohs(DNS_Header->Flags) & DNS_GET_BIT_TC) != 0 || //RCode must be set No Error or Non-Existent Domain. (ntohs(DNS_Header->Flags) & DNS_GET_BIT_RCODE) != DNS_RCODE_NOERROR && (ntohs(DNS_Header->Flags) & DNS_GET_BIT_RCODE) != DNS_RCODE_NXDOMAIN) return false; //Initialization(A part) DNSCACHE_DATA DNSCacheDataTemp; DNSCacheDataTemp.Length = 0; DNSCacheDataTemp.ClearCacheTime = 0; auto DNS_Query = (pdns_qry)(Buffer + DNS_PACKET_QUERY_LOCATE(Buffer)); DNSCacheDataTemp.RecordType = DNS_Query->Type; uint32_t ResponseTTL = 0; //Mark DNS A records and AAAA records only. if (DNSCacheDataTemp.RecordType == htons(DNS_RECORD_AAAA) || DNSCacheDataTemp.RecordType == htons(DNS_RECORD_A)) { size_t DataLength = DNS_PACKET_RR_LOCATE(Buffer), TTLCounts = 0; pdns_record_standard DNS_Record_Standard = nullptr; uint16_t DNS_Pointer = 0; //Scan all Answers Resource Records. for (size_t Index = 0;Index < (size_t)ntohs(DNS_Header->Answer);++Index) { //Pointer check if (DataLength + sizeof(uint16_t) < Length && (UCHAR)Buffer[DataLength] >= DNS_POINTER_BITS) { DNS_Pointer = ntohs(*(uint16_t *)(Buffer + DataLength)) & DNS_POINTER_BITS_GET_LOCATE; if (DNS_Pointer >= Length || DNS_Pointer < sizeof(dns_hdr) || DNS_Pointer == DataLength || DNS_Pointer == DataLength + 1U) return false; } //Resource Records Name(Domain Name) DataLength += CheckQueryNameLength(Buffer + DataLength) + 1U; if (DataLength + sizeof(dns_record_standard) > Length) break; //Standard Resource Records DNS_Record_Standard = (pdns_record_standard)(Buffer + DataLength); DataLength += sizeof(dns_record_standard); if (DataLength > Length || DNS_Record_Standard != nullptr && DataLength + ntohs(DNS_Record_Standard->Length) > Length) break; //Resource Records Data if (DNS_Record_Standard->Classes == htons(DNS_CLASS_IN) && DNS_Record_Standard->TTL > 0 && (DNS_Record_Standard->Type == htons(DNS_RECORD_AAAA) && DNS_Record_Standard->Length == htons(sizeof(in6_addr)) || DNS_Record_Standard->Type == htons(DNS_RECORD_A) && DNS_Record_Standard->Length == htons(sizeof(in_addr)))) { ResponseTTL += ntohl(DNS_Record_Standard->TTL); ++TTLCounts; } DataLength += ntohs(DNS_Record_Standard->Length); } //Calculate average TTL. if (TTLCounts > 0) ResponseTTL = ResponseTTL / (uint32_t)TTLCounts + ResponseTTL % (uint32_t)TTLCounts; } //Set cache TTL. if (ResponseTTL == 0) //Only mark A and AAAA records. { return false; } else { if (Parameter.CacheType == CACHE_TYPE_TIMER) { if (ResponseTTL * SECOND_TO_MILLISECOND < Parameter.CacheParameter) ResponseTTL = (uint32_t)(Parameter.CacheParameter / SECOND_TO_MILLISECOND - ResponseTTL + STANDARD_TIMEOUT / SECOND_TO_MILLISECOND); } else { //CACHE_TYPE_QUEUE if (ResponseTTL < Parameter.HostsDefaultTTL) ResponseTTL = Parameter.HostsDefaultTTL - ResponseTTL + STANDARD_TIMEOUT / SECOND_TO_MILLISECOND; } } //Initialization(B part) if (Length <= DOMAIN_MAXSIZE) { std::shared_ptr<char> DNSCacheDataBufferTemp(new char[DOMAIN_MAXSIZE]()); memset(DNSCacheDataBufferTemp.get(), 0, DOMAIN_MAXSIZE); DNSCacheDataTemp.Response.swap(DNSCacheDataBufferTemp); } else { std::shared_ptr<char> DNSCacheDataBufferTemp(new char[Length]()); memset(DNSCacheDataBufferTemp.get(), 0, Length); DNSCacheDataTemp.Response.swap(DNSCacheDataBufferTemp); } //Mark to global list. if (DNSQueryToChar(Buffer + sizeof(dns_hdr), DNSCacheDataTemp.Response.get()) > DOMAIN_MINSIZE) { //Domain Case Conversion CaseConvert(false, DNSCacheDataTemp.Response.get(), strnlen_s(DNSCacheDataTemp.Response.get(), DOMAIN_MAXSIZE)); DNSCacheDataTemp.Domain = DNSCacheDataTemp.Response.get(); memset(DNSCacheDataTemp.Response.get(), 0, DOMAIN_MAXSIZE); memcpy_s(DNSCacheDataTemp.Response.get(), PACKET_MAXSIZE, Buffer + sizeof(uint16_t), Length - sizeof(uint16_t)); DNSCacheDataTemp.Length = Length - sizeof(uint16_t); //Minimum supported system of GetTickCount64() is Windows Vista(Windows XP with SP3 support). #if (defined(PLATFORM_WIN32) && !defined(PLATFORM_WIN64)) if (Parameter.FunctionPTR_GetTickCount64 != nullptr) DNSCacheDataTemp.ClearCacheTime = (size_t)((*Parameter.FunctionPTR_GetTickCount64)() + ResponseTTL * SECOND_TO_MILLISECOND); else DNSCacheDataTemp.ClearCacheTime = GetTickCount() + ResponseTTL * SECOND_TO_MILLISECOND; #else DNSCacheDataTemp.ClearCacheTime = GetTickCount64() + ResponseTTL * SECOND_TO_MILLISECOND; #endif std::unique_lock<std::mutex> DNSCacheListMutex(DNSCacheListLock); //Check repeating items, delete duque rear and add new item to deque front. for (auto DNSCacheDataIter = DNSCacheList.begin();DNSCacheDataIter != DNSCacheList.end();++DNSCacheDataIter) { if (DNSCacheDataTemp.Domain == DNSCacheDataIter->Domain && DNSCacheDataTemp.RecordType == DNSCacheDataIter->RecordType) { DNSCacheList.erase(DNSCacheDataIter); break; } } if (Parameter.CacheType == CACHE_TYPE_QUEUE) { while (DNSCacheList.size() > Parameter.CacheParameter) DNSCacheList.pop_front(); } else { //CACHE_TYPE_TIMER //Minimum supported system of GetTickCount64() is Windows Vista(Windows XP with SP3 support). #if (defined(PLATFORM_WIN32) && !defined(PLATFORM_WIN64)) while (!DNSCacheList.empty() && (Parameter.FunctionPTR_GetTickCount64 != nullptr && (*Parameter.FunctionPTR_GetTickCount64)() >= DNSCacheList.front().ClearCacheTime || GetTickCount() >= DNSCacheList.front().ClearCacheTime)) #else while (!DNSCacheList.empty() && GetTickCount64() >= DNSCacheList.front().ClearCacheTime) #endif DNSCacheList.pop_front(); } DNSCacheList.push_back(DNSCacheDataTemp); DNSCacheList.shrink_to_fit(); return true; } return false; }