Add a new IPV4 direct DNS lookup function. Allows us to use the configured DNS directly.

This commit is contained in:
Henrik Rydgård committed 2025-01-10 21:37:36 +01:00
1 parent fbd548dde5
commit 293c087577
3 files changed
+176 -1

No files matched your search

+167 -1
View File
@@ -231,6 +231,7 @@ bool GetIPList(std::vector<std::string> &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
+3
View File
@@ -23,4 +23,7 @@ bool GetIPList(std::vector<std::string>& 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
+6
View File
@@ -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)