diff --git a/Common/Net/Resolve.cpp b/Common/Net/Resolve.cpp index ca4dfd0555..78535acd2b 100644 --- a/Common/Net/Resolve.cpp +++ b/Common/Net/Resolve.cpp @@ -231,6 +231,7 @@ bool GetIPList(std::vector &IP4s) { return false; } +// IP address parser int inet_pton(int af, const char* src, void* dst) { if (af == AF_INET) @@ -304,6 +305,171 @@ int inet_pton(int af, const char* src, void* dst) } // Structs for implementing DNS are available here: -// Based on https://web.archive.org/web/20201204080751/https://www.binarytides.com/dns-query-code-in-c-with-winsock/ +// https://web.archive.org/web/20201204080751/https://www.binarytides.com/dns-query-code-in-c-with-winsock/ + +#define DNS_PORT 53 +#define DNS_QUERY_TYPE_A 1 +#define DNS_QUERY_CLASS_IN 1 + +// DNS header structure +struct DNSHeader { + uint16_t id; // Identifier + uint16_t flags; // Flags + uint16_t q_count; // Number of questions + uint16_t ans_count; // Number of answers + uint16_t auth_count; // Number of authority records + uint16_t add_count; // Number of additional records +}; + +// Function to convert a domain name to DNS query format +static void encode_domain_name(const char *domain, unsigned char *encoded) { + const char *pos = domain; + unsigned char *ptr = encoded; + + while (*pos) { + const char *start = pos; + while (*pos && *pos != '.') pos++; + + *ptr++ = pos - start; + memcpy(ptr, start, pos - start); + ptr += pos - start; + + if (*pos == '.') pos++; + } + *ptr = 0; // End of domain name +} + +// Function to parse and print the DNS response +static bool parse_dns_response(unsigned char *buffer, size_t response_len, uint32_t *output) { + DNSHeader *dns = (DNSHeader *)buffer; + unsigned char *ptr = buffer + sizeof(struct DNSHeader); + + DEBUG_LOG(Log::sceNet, "DNS Response:"); + DEBUG_LOG(Log::sceNet, "ID: 0x%x", ntohs(dns->id)); + DEBUG_LOG(Log::sceNet, "Flags: 0x%x", ntohs(dns->flags)); + DEBUG_LOG(Log::sceNet, "Questions: %d", ntohs(dns->q_count)); + DEBUG_LOG(Log::sceNet, "Answers: %d", ntohs(dns->ans_count)); + DEBUG_LOG(Log::sceNet, "Authority Records: %d", ntohs(dns->auth_count)); + DEBUG_LOG(Log::sceNet, "Additional Records: %d", ntohs(dns->add_count)); + + // Skip over the question section + const int q_count = ntohs(dns->q_count); + for (int i = 0; i < q_count; i++) { + while (*ptr != 0) { + ptr += (*ptr) + 1; + } + ptr += 5; // Null byte + QTYPE (2 bytes) + QCLASS (2 bytes) + } + + *output = 0; + + // Parse the answer section + const int ans_count = ntohs(dns->ans_count); + for (int i = 0; i < ans_count; i++) { + DEBUG_LOG(Log::sceNet, "Answer %d:\n", i + 1); + + // Skip the name (can be a pointer or a sequence) + if ((*ptr & 0xC0) == 0xC0) { + ptr += 2; // Pointer (2 bytes) + } else { + while (*ptr != 0) ptr += (*ptr) + 1; + ptr++; + } + + // TODO: Use a struct or something. + uint16_t type = ntohs(*((uint16_t *)ptr)); + ptr += 2; + uint16_t clazz = ntohs(*((uint16_t *)ptr)); + ptr += 2; + uint32_t ttl = ntohl(*((uint32_t *)ptr)); + ptr += 4; + uint16_t data_len = ntohs(*((uint16_t *)ptr)); + ptr += 2; + + DEBUG_LOG(Log::sceNet, " Type: %d", type); + DEBUG_LOG(Log::sceNet, " Class: %d", clazz); + DEBUG_LOG(Log::sceNet, " TTL: %u", ttl); + DEBUG_LOG(Log::sceNet, " Data length: %n", data_len); + + if (type == DNS_QUERY_TYPE_A && data_len == 4) { + // IPv4 address + char ip[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, ptr, ip, sizeof(ip)); + DEBUG_LOG(Log::sceNet, " IPV4 Address: %s", ip); + memcpy(output, ptr, 4); + // Skipping further responses. + return true; + } + + ptr += data_len; + } + return false; +} + +// This was written by ChatGPT! (And then cleaned up...) +bool DirectDNSLookupIPV4(const char *dns_server_ip, const char *domain, uint32_t *ipv4_addr) { + if (!strlen(dns_server_ip)) { + WARN_LOG(Log::sceNet, "Direct lookup: DNS server not specified"); + return false; + } + + if (!strlen(domain)) { + ERROR_LOG(Log::sceNet, "Direct lookup: Can't look up an empty domain"); + return false; + } + + int sockfd; + // Create UDP socket + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + ERROR_LOG(Log::sceNet, "Socket creation for direct DNS failed"); + return 1; + } + + struct sockaddr_in server_addr {}; + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(DNS_PORT); + + if (net::inet_pton(AF_INET, dns_server_ip, &server_addr.sin_addr) <= 0) { + ERROR_LOG(Log::sceNet,"Invalid DNS server IP address %s", dns_server_ip); + close(sockfd); + return 1; + } + + // Build DNS query + unsigned char buffer[1024]{}; + struct DNSHeader *dns = (struct DNSHeader *)buffer; + dns->id = htons(0x1234); // Random ID + dns->flags = htons(0x0100); // Standard query + dns->q_count = htons(1); // One question + + unsigned char *qname = buffer + sizeof(DNSHeader); + encode_domain_name(domain, qname); + + unsigned char *qinfo = qname + strlen((const char *)qname) + 1; + *((uint16_t *)qinfo) = htons(DNS_QUERY_TYPE_A); // Query type: A + *((uint16_t *)(qinfo + 2)) = htons(DNS_QUERY_CLASS_IN); // Query class: IN + + // Send DNS query + size_t query_len = sizeof(DNSHeader) + (qinfo - buffer) + 4; + if (sendto(sockfd, (const char *)buffer, query_len, 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { + ERROR_LOG(Log::sceNet, "Failed to send DNS query"); + closesocket(sockfd); + return 1; + } + + // Receive DNS response + socklen_t server_len = sizeof(server_addr); + size_t response_len; + if ((response_len = recvfrom(sockfd, (char *)buffer, sizeof(buffer), 0, (struct sockaddr *)&server_addr, &server_len)) < 0) { + ERROR_LOG(Log::sceNet, "Failed to receive DNS response"); + closesocket(sockfd); + return 1; + } + // Close socket + closesocket(sockfd); + + // Done communicating, time to parse. + return parse_dns_response(buffer, response_len, ipv4_addr); +} } // namespace net diff --git a/Common/Net/Resolve.h b/Common/Net/Resolve.h index 63edecfbfe..cd50615262 100644 --- a/Common/Net/Resolve.h +++ b/Common/Net/Resolve.h @@ -23,4 +23,7 @@ bool GetIPList(std::vector& IP4s); int inet_pton(int af, const char* src, void* dst); +// Does a DNS lookup without involving the OS, so you can hit any DNS server. +bool DirectDNSLookupIPV4(const char *dnsServer, const char *host, uint32_t *ipv4_addr); + } // namespace net diff --git a/Core/HLE/sceNetResolver.cpp b/Core/HLE/sceNetResolver.cpp index ae7fc3e8ae..c9201a8340 100644 --- a/Core/HLE/sceNetResolver.cpp +++ b/Core/HLE/sceNetResolver.cpp @@ -100,6 +100,12 @@ int NetResolver_StartNtoA(u32 resolverId, u32 hostnamePtr, u32 inAddrPtr, int ti hostname = alias; } + /* + // Try the new resolver, that hits the configured primary and secondary DNSs. + uint32_t raw_ip = net::RawDNSLookupIPV4(g_Config.primaryDNSServer.c_str(), hostname.c_str()); + + Memory::Write_U32(raw_ip, inAddrPtr); + */ // Attempt to execute a DNS resolution if (!net::DNSResolve(hostname, "", &resolved, err)) { // TODO: Return an error based on the outputted "err" (unfortunately it's already converted to string)