mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Add a new IPV4 direct DNS lookup function. Allows us to use the configured DNS directly.
This commit is contained in:
1 parent
fbd548dde5
commit
293c087577
3 files changed
+176
-1
No files matched your search
+167
-1
@@ -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
|
||||
Reference in new issue
Block a user