602 lines
12 KiB
C
602 lines
12 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "constants.h"
|
|
#include "packet.h"
|
|
#include "question.h"
|
|
#include "rr.h"
|
|
|
|
void dns_rr_pack_data (DNSRR *rr, DNSPacket *packet);
|
|
DNSRR * dns_rr_unpack_data (DNSPacket *packet);
|
|
|
|
DNSPacket *dns_packet_create_request (const char *name, int type, int klass) {
|
|
DNSPacket *req;
|
|
DNSQuestion *q;
|
|
|
|
req = (DNSPacket *) malloc (sizeof (DNSPacket));
|
|
|
|
if (req == NULL) return NULL;
|
|
|
|
dns_header_init (&req->header);
|
|
|
|
q = dns_question_create (name, type, klass);
|
|
|
|
if (q == NULL) {
|
|
free (req);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
req->questions = g_list_append (NULL, q);
|
|
|
|
req->authority = NULL;
|
|
req->answers = NULL;
|
|
req->additional = NULL;
|
|
req->rdlength = 0;
|
|
|
|
req->labels = NULL;
|
|
|
|
return req;
|
|
}
|
|
|
|
DNSPacket *dns_packet_create_response (int type, unsigned char *data, int size_data) {
|
|
DNSPacket *res;
|
|
DNSRR *rr;
|
|
|
|
int g;
|
|
|
|
res = (DNSPacket *) malloc (sizeof (DNSPacket));
|
|
|
|
if (res == NULL) return NULL;
|
|
|
|
memset (res, 0, sizeof (DNSPacket));
|
|
|
|
res->answer_socket_type = type;
|
|
|
|
memcpy (res->rdata, data, size_data);
|
|
res->rdlength = size_data;
|
|
res->rdread = 0;
|
|
|
|
if (dns_header_init_from_packet (&res->header, res) < 0) {
|
|
dns_packet_free_full (res);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* TODO: Revisar el bit de truncamiento */
|
|
|
|
/* Parsear las preguntas */
|
|
for (g = 0; g < res->header.qdcount; g++) {
|
|
if (dns_question_parse_question_from_packet (res) < 0) {
|
|
dns_packet_free_full (res);
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Parsear los answers */
|
|
for (g = 0; g < res->header.ancount; g++) {
|
|
rr = dns_rr_unpack_data (res);
|
|
|
|
if (rr == NULL) {
|
|
dns_packet_free_full (res);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
res->answers = g_list_append (res->answers, rr);
|
|
}
|
|
|
|
/* Parsear los authority */
|
|
for (g = 0; g < res->header.nscount; g++) {
|
|
rr = dns_rr_unpack_data (res);
|
|
|
|
if (rr == NULL) {
|
|
dns_packet_free_full (res);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
res->authority = g_list_append (res->authority, rr);
|
|
}
|
|
|
|
/* Parsear los aditional */
|
|
for (g = 0; g < res->header.arcount; g++) {
|
|
rr = dns_rr_unpack_data (res);
|
|
|
|
if (rr == NULL) {
|
|
dns_packet_free_full (res);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
res->additional = g_list_append (res->additional, rr);
|
|
}
|
|
|
|
dns_packet_recount_items (res);
|
|
|
|
return res;
|
|
}
|
|
|
|
int dns_packet_get_data (DNSPacket *packet, unsigned char **data) {
|
|
if (data != NULL) {
|
|
*data = packet->rdata;
|
|
}
|
|
|
|
return packet->rdlength;
|
|
}
|
|
|
|
void dns_packet_pack_data (DNSPacket *packet) {
|
|
GList *g;
|
|
DNSQuestion *qq;
|
|
DNSRR *rr;
|
|
|
|
/* Reiniciar desde 0 */
|
|
packet->rdlength = 0;
|
|
|
|
dns_header_pack_data (&packet->header, packet);
|
|
|
|
g = packet->questions;
|
|
while (g != NULL) {
|
|
qq = (DNSQuestion *) g->data;
|
|
|
|
dns_question_pack_data (qq, packet);
|
|
|
|
g = g->next;
|
|
}
|
|
|
|
g = packet->answers;
|
|
while (g != NULL) {
|
|
rr = (DNSRR *) g->data;
|
|
|
|
dns_rr_pack_data (rr, packet);
|
|
|
|
g = g->next;
|
|
}
|
|
|
|
g = packet->authority;
|
|
while (g != NULL) {
|
|
rr = (DNSRR *) g->data;
|
|
|
|
dns_rr_pack_data (rr, packet);
|
|
|
|
g = g->next;
|
|
}
|
|
|
|
g = packet->additional;
|
|
while (g != NULL) {
|
|
rr = (DNSRR *) g->data;
|
|
|
|
dns_rr_pack_data (rr, packet);
|
|
|
|
g = g->next;
|
|
}
|
|
}
|
|
|
|
static int dns_packet_search_label (gconstpointer left, gconstpointer right) {
|
|
PacketLabel *a = (PacketLabel *) left;
|
|
PacketLabel *b = (PacketLabel *) right;
|
|
|
|
return strcmp (a->name, b->name);
|
|
}
|
|
|
|
static void dns_packet_append_word (DNSPacket *packet, const char *name) {
|
|
const char *ending;
|
|
PacketLabel *new_label;
|
|
uint8_t t8;
|
|
|
|
ending = strchr (name, '.');
|
|
if (ending == NULL) {
|
|
ending = name + strlen (name);
|
|
}
|
|
|
|
new_label = (PacketLabel *) malloc (sizeof (PacketLabel));
|
|
|
|
if (new_label == NULL) {
|
|
/* Oops, error */
|
|
return;
|
|
}
|
|
|
|
strncpy (new_label->name, name, sizeof (new_label->name));
|
|
new_label->offset = packet->rdlength;
|
|
|
|
packet->labels = g_list_append (packet->labels, new_label);
|
|
|
|
/* Copiar solo la palabra */
|
|
t8 = (ending - name);
|
|
packet->rdata[packet->rdlength] = t8;
|
|
memcpy (&packet->rdata[packet->rdlength + 1], name, t8);
|
|
|
|
packet->rdlength += t8 + 1;
|
|
}
|
|
|
|
void dns_packet_pack_name (DNSPacket *packet, const char *name) {
|
|
char * dot_pos;
|
|
|
|
while (dot_pos = strchr (name, '.'), dot_pos != NULL) {
|
|
/* Buscar esta palabra en nuestro diccionario de palabras */
|
|
name = dot_pos + 1;
|
|
|
|
/* Agregar esta palabra al diccionario de etiquetas */
|
|
dns_packet_append_word (packet, name);
|
|
}
|
|
|
|
/* La palabra restante, copiarla */
|
|
if (name[0] != 0) {
|
|
dns_packet_append_word (packet, name);
|
|
}
|
|
|
|
packet->rdata[packet->rdlength] = 0;
|
|
packet->rdlength += 1;
|
|
}
|
|
|
|
void dns_packet_compress_name (DNSPacket *packet, char *name) {
|
|
PacketLabel temp, *found;
|
|
char * dot_pos;
|
|
GList *search;
|
|
uint16_t t16;
|
|
|
|
while (dot_pos = strchr (name, '.'), dot_pos != NULL) {
|
|
/* Buscar esta palabra en nuestro diccionario de palabras */
|
|
|
|
strncpy (temp.name, name, sizeof (temp.name));
|
|
|
|
search = g_list_find_custom (packet->labels, &temp, dns_packet_search_label);
|
|
|
|
if (search != NULL) {
|
|
found = (PacketLabel *) search->data;
|
|
|
|
t16 = htons (0xc000 | found->offset);
|
|
|
|
memcpy (&packet->rdata[packet->rdlength], &t16, 2);
|
|
packet->rdlength += 2;
|
|
|
|
return;
|
|
}
|
|
|
|
/* Agregar esta palabra al direccionario de etiquetas */
|
|
dns_packet_append_word (packet, name);
|
|
name = dot_pos + 1;
|
|
}
|
|
|
|
/* La palabra restante, copiarla */
|
|
if (name[0] != 0) {
|
|
dns_packet_append_word (packet, name);
|
|
}
|
|
|
|
packet->rdata[packet->rdlength] = 0;
|
|
packet->rdlength += 1;
|
|
}
|
|
|
|
DNSPacket *dns_packet_clone_simple (DNSPacket *packet) {
|
|
DNSPacket *clone;
|
|
|
|
clone = (DNSPacket *) malloc (sizeof (DNSPacket));
|
|
|
|
if (clone == NULL) return NULL;
|
|
|
|
memcpy (&clone->header, &packet->header, sizeof (DNSHeader));
|
|
|
|
clone->questions = g_list_copy (packet->questions);
|
|
clone->answers = g_list_copy (packet->answers);
|
|
clone->authority = g_list_copy (packet->authority);
|
|
clone->additional = g_list_copy (packet->additional);
|
|
|
|
clone->labels = NULL;
|
|
|
|
clone->rdlength = 0;
|
|
|
|
/* NOTA: No se copian las labels */
|
|
|
|
return clone;
|
|
}
|
|
|
|
void dns_packet_free_simple (DNSPacket *packet) {
|
|
/* Liberar las estructuras, excepto los datos que no duplicamos */
|
|
g_list_free_full (packet->labels, (GDestroyNotify) free);
|
|
|
|
g_list_free (packet->questions);
|
|
g_list_free (packet->answers);
|
|
g_list_free (packet->authority);
|
|
g_list_free (packet->additional);
|
|
|
|
free (packet);
|
|
}
|
|
|
|
void dns_packet_free_full (DNSPacket *packet) {
|
|
g_list_free_full (packet->labels, (GDestroyNotify) free);
|
|
|
|
g_list_free_full (packet->questions, (GDestroyNotify) dns_question_free);
|
|
g_list_free_full (packet->answers, (GDestroyNotify) dns2_rr_free);
|
|
g_list_free_full (packet->authority, (GDestroyNotify) dns2_rr_free);
|
|
g_list_free_full (packet->additional, (GDestroyNotify) dns2_rr_free);
|
|
|
|
free (packet);
|
|
}
|
|
|
|
void dns_packet_reset (DNSPacket *packet) {
|
|
g_list_free_full (packet->labels, (GDestroyNotify) free);
|
|
packet->labels = NULL;
|
|
|
|
packet->rdlength = 0;
|
|
}
|
|
|
|
int dns_packet_expand_name (unsigned char *name, DNSPacket *packet, int *offset) {
|
|
int name_len = 0;
|
|
uint8_t t8;
|
|
uint16_t t16;
|
|
int local_offset;
|
|
int ptr, ptr_inicio;
|
|
int g;
|
|
|
|
while (1) {
|
|
if (name_len >= 256) {
|
|
return -1;
|
|
}
|
|
|
|
if (packet->rdlength < (*offset) + 1) {
|
|
return -1;
|
|
}
|
|
|
|
t8 = packet->rdata[*offset];
|
|
|
|
if (t8 == 0) {
|
|
name[name_len] = 0;
|
|
|
|
(*offset)++;
|
|
|
|
return 0;
|
|
} else if ((t8 & 0xc0) == 0xc0) {
|
|
/* Tenemos un apuntador */
|
|
if (packet->rdlength < (*offset) + 2) {
|
|
return -1;
|
|
}
|
|
|
|
memcpy (&t16, &packet->rdata[*offset], sizeof (t16));
|
|
t16 = ntohs (t16);
|
|
|
|
ptr = t16 & 0x3fff;
|
|
ptr_inicio = ptr;
|
|
if (dns_packet_expand_name (&name[name_len], packet, &ptr) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* Sumar a la longitud de nombres lo recibido del apuntador */
|
|
name_len = name_len + (ptr - ptr_inicio);
|
|
(*offset) += 2;
|
|
|
|
break;
|
|
} else {
|
|
(*offset)++;
|
|
|
|
if (packet->rdlength < (*offset) + t8) {
|
|
return -1;
|
|
}
|
|
|
|
/* Una palabra */
|
|
for (g = *offset; g < t8 + *offset; g++) {
|
|
name[name_len] = packet->rdata[g];
|
|
name_len++;
|
|
}
|
|
|
|
name[name_len] = '.';
|
|
name_len++;
|
|
|
|
(*offset) += t8;
|
|
}
|
|
}
|
|
|
|
/* Limpiar el último punto */
|
|
if (name[name_len - 1] == '.') {
|
|
name[name_len - 1] = 0;
|
|
name_len--;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int dns_packet_expand_name_simple (unsigned char *name, unsigned char *buffer, int buffer_size) {
|
|
int name_len = 0;
|
|
uint8_t t8;
|
|
int g;
|
|
int offset = 0;
|
|
|
|
while (1) {
|
|
if (name_len >= 256) {
|
|
return -1;
|
|
}
|
|
|
|
if (buffer_size < offset + 1) {
|
|
return -1;
|
|
}
|
|
|
|
t8 = buffer[offset];
|
|
offset++;
|
|
|
|
if (t8 == 0) {
|
|
/* Fin del nombre */
|
|
name[name_len] = 0;
|
|
|
|
return offset;
|
|
} else {
|
|
if (buffer_size < offset + t8) {
|
|
return -1;
|
|
}
|
|
|
|
/* Una palabra */
|
|
for (g = offset; g < t8 + offset; g++) {
|
|
name[name_len] = buffer[g];
|
|
name_len++;
|
|
}
|
|
|
|
name[name_len] = '.';
|
|
name_len++;
|
|
|
|
offset += t8;
|
|
}
|
|
}
|
|
|
|
/* Limpiar el último punto */
|
|
if (name[name_len - 1] == '.') {
|
|
name[name_len - 1] = 0;
|
|
name_len--;
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
void dns_header_pack_data (DNSHeader *header, DNSPacket *packet) {
|
|
unsigned char *buf;
|
|
uint16_t t16;
|
|
uint8_t t8;
|
|
|
|
buf = &packet->rdata[packet->rdlength];
|
|
|
|
/* Copiar el ID */
|
|
t16 = htons (header->id);
|
|
memcpy (&buf[0], &t16, sizeof (t16));
|
|
|
|
t8 = (header->qr << 7) | (header->opcode << 3) | (header->aa << 2) | (header->tc << 1) | (header->rd);
|
|
buf[2] = t8;
|
|
|
|
t8 = (header->ra << 7) | (header->ad << 5) | (header->cd << 4) | header->rcode;
|
|
buf[3] = t8;
|
|
|
|
t16 = htons (header->qdcount);
|
|
memcpy (&buf[4], &t16, sizeof (t16));
|
|
|
|
t16 = htons (header->ancount);
|
|
memcpy (&buf[6], &t16, sizeof (t16));
|
|
|
|
t16 = htons (header->nscount);
|
|
memcpy (&buf[8], &t16, sizeof (t16));
|
|
|
|
t16 = htons (header->arcount);
|
|
memcpy (&buf[10], &t16, sizeof (t16));
|
|
|
|
packet->rdlength += DNS_HEADER_SIZE;
|
|
}
|
|
|
|
int dns_header_init_from_packet (DNSHeader *header, DNSPacket *packet) {
|
|
uint16_t t16;
|
|
uint8_t t8;
|
|
unsigned char *buf;
|
|
|
|
buf = &packet->rdata[packet->rdread];
|
|
|
|
if (packet->rdlength < packet->rdread + DNS_HEADER_SIZE) {
|
|
return -1;
|
|
}
|
|
|
|
memcpy (&t16, &buf[0], sizeof (t16));
|
|
header->id = ntohs (t16);
|
|
|
|
t8 = buf[2];
|
|
|
|
header->qr = (t8 >> 7) & 0x1;
|
|
header->opcode = (t8 >> 3) & 0xf;
|
|
header->aa = (t8 >> 2) & 0x1;
|
|
header->tc = (t8 >> 1) & 0x1;
|
|
header->rd = t8 & 0x1;
|
|
|
|
t8 = buf[3];
|
|
header->ra = (t8 >> 7) & 0x1;
|
|
header->z = (t8 >> 6) & 0x1;
|
|
header->ad = (t8 >> 5) & 0x1;
|
|
header->cd = (t8 >> 4) & 0x1;
|
|
header->rcode = t8 & 0xf;
|
|
|
|
memcpy (&t16, &buf[4], sizeof (t16));
|
|
header->qdcount = ntohs (t16);
|
|
|
|
memcpy (&t16, &buf[6], sizeof (t16));
|
|
header->ancount = ntohs (t16);
|
|
|
|
memcpy (&t16, &buf[8], sizeof (t16));
|
|
header->nscount = ntohs (t16);
|
|
|
|
memcpy (&t16, &buf[10], sizeof (t16));
|
|
header->arcount = ntohs (t16);
|
|
|
|
packet->rdread += DNS_HEADER_SIZE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void dns_packet_recount_items (DNSPacket *packet) {
|
|
if (packet == NULL) return;
|
|
|
|
packet->header.qdcount = g_list_length (packet->questions);
|
|
packet->header.ancount = g_list_length (packet->answers);
|
|
packet->header.nscount = g_list_length (packet->authority);
|
|
packet->header.arcount = g_list_length (packet->additional);
|
|
}
|
|
|
|
DNSRR **dns_packet_get_answers (DNSPacket *packet) {
|
|
DNSRR **array;
|
|
GList *g;
|
|
int n;
|
|
|
|
dns_packet_recount_items (packet);
|
|
|
|
if (packet->header.ancount == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
array = (DNSRR **) malloc ((packet->header.ancount + 1) * sizeof (DNSRR *));
|
|
|
|
for (n = 0, g = packet->answers; g != NULL; g = g->next, n++) {
|
|
array[n] = (DNSRR *) g->data;
|
|
}
|
|
|
|
array[n] = NULL;
|
|
|
|
return array;
|
|
}
|
|
|
|
DNSRR **dns_packet_get_authority (DNSPacket *packet) {
|
|
DNSRR **array;
|
|
GList *g;
|
|
int n;
|
|
|
|
dns_packet_recount_items (packet);
|
|
|
|
if (packet->header.nscount == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
array = (DNSRR **) malloc ((packet->header.nscount + 1) * sizeof (DNSRR *));
|
|
|
|
for (n = 0, g = packet->authority; g != NULL; g = g->next, n++) {
|
|
array[n] = (DNSRR *) g->data;
|
|
}
|
|
|
|
array[n] = NULL;
|
|
|
|
return array;
|
|
}
|
|
|
|
DNSRR **dns_packet_get_additional (DNSPacket *packet) {
|
|
DNSRR **array;
|
|
GList *g;
|
|
int n;
|
|
|
|
dns_packet_recount_items (packet);
|
|
|
|
if (packet->header.arcount == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
array = (DNSRR **) malloc ((packet->header.arcount + 1) * sizeof (DNSRR *));
|
|
|
|
for (n = 0, g = packet->additional; g != NULL; g = g->next, n++) {
|
|
array[n] = (DNSRR *) g->data;
|
|
}
|
|
|
|
array[n] = NULL;
|
|
|
|
return array;
|
|
}
|
|
|