#include #include #include #include #include #include #include #include #include #include #include #include "dns2.h" #include "utils.h" #include "packet.h" #include "constants.h" #include "glist.h" #include "rr.h" #include "question.h" #include "dns2-private.h" typedef struct { char name[512]; struct sockaddr_storage server_addr; socklen_t addr_size; int socket; } NameServer; /* Prototipos de función */ int _dns2_internal_tcp_start_connect (DNS2 *obj); int _dns2_internal_udp_create (DNS2 *obj); int _dns2_internal_tcp_connect_continue (DNS2 *obj); int _dns2_internal_udp_send (DNS2 *obj); int _dns2_internal_tcp_send (DNS2 *obj); int _dns2_internal_udp_recv (DNS2 *obj); int _dns2_internal_tcp_recv (DNS2 *obj); int _dns2_internal_tcp_next_or_error (DNS2 *obj); void dns2_clean_network (DNS2 *obj); double _dns2_diff_timespec (const struct timespec *time1, const struct timespec *time0) { return (time1->tv_sec - time0->tv_sec) + (time1->tv_nsec - time0->tv_nsec) / 1000000000.0; } int dns2_async (DNS2 *obj) { int length; // checkServers DNSRR *signature; DNSQuestion *question; int max_udp_size; if (obj->state != DNS_STATE_FREE) { return DNS_COMMAND_ERROR; } if (obj->packet == NULL) { return DNS_COMMAND_ERROR; } /* Resetear el estado del paquete */ dns_packet_reset (obj->packet); obj->current_ns = g_list_first (obj->nameservers); obj->retry_count = 0; if (obj->current_ns == NULL) { return DNS_COMMAND_ERROR; } /* Actualizar el contador de paquetes */ dns_packet_recount_items (obj->packet); if (obj->type == DNS_UPDATER && (obj->packet->header.qdcount == 0 || obj->packet->header.nscount == 0)) { /* Error, nada que mandar en el update */ return DNS_COMMAND_ERROR; } max_udp_size = DNS_MAX_UDP_SIZE; /* Si tiene una firma, generar la firma */ if (obj->auth_signature != NULL) { if (obj->auth_signature->type == RR_TYPE_TSIG /*|| obj->auth_signature->tipo == RR_TYPE_SIG*/) { signature = dns2_rr_dup (obj->auth_signature); dns_rr_pack_generate_signature (signature, obj->packet); obj->packet->additional = g_list_append (obj->packet->additional, signature); obj->packet->header.arcount = g_list_length (obj->packet->additional); } max_udp_size = 4000; } dns_packet_pack_data (obj->packet); /* ------ Para depuración ------ unsigned char *data; length = dns_packet_get_data (obj->packet, &data); for (int g = 0; gpacket->questions->data; if (question->qtype == RR_TYPE_ANY || question->qtype == RR_TYPE_AXFR) { obj->force_use_tcp = 1; } length = dns_packet_get_data (obj->packet, NULL); if (length > max_udp_size || obj->force_use_tcp == 1) { obj->state = DNS_STATE_TCP_CONNECT; return _dns2_internal_tcp_start_connect (obj); } else { obj->state = DNS_STATE_UDP; return _dns2_internal_udp_create (obj); } } int dns2_async_continue (DNS2 *obj, int happen) { if (obj->state == DNS_STATE_FREE) { return DNS_COMMAND_ERROR; } if (obj->direction == DNS_POLL_WANT_READ) { if ((happen & DNS_POLL_WANT_READ) == 0) { return DNS_COMMAND_POLL; } switch (obj->state) { case DNS_STATE_UDP: return _dns2_internal_udp_recv (obj); case DNS_STATE_TCP: return _dns2_internal_tcp_recv (obj); } } else if (obj->direction == DNS_POLL_WANT_WRITE) { if ((happen & DNS_POLL_WANT_WRITE) == 0) { return DNS_COMMAND_POLL; } switch (obj->state) { case DNS_STATE_TCP_CONNECT: return _dns2_internal_tcp_connect_continue (obj); case DNS_STATE_UDP: return _dns2_internal_udp_send (obj); case DNS_STATE_TCP: return _dns2_internal_tcp_send (obj); } } return DNS_COMMAND_ERROR; } int dns2_sync (DNS2 *obj) { int res, res_poll; struct pollfd poller; int wanted_events; int events; res = dns2_async (obj); while (res == DNS_COMMAND_POLL) { poller.fd = dns2_get_socket (obj); poller.revents = 0; poller.events = 0; /* FIXME AQUI */ wanted_events = dns2_get_socket_poll_flags (obj); if (wanted_events == DNS_POLL_WANT_READ) { poller.events = POLLIN; } else if (wanted_events == DNS_POLL_WANT_WRITE) { poller.events = POLLOUT; } res_poll = poll (&poller, 1, 30); if (res_poll == 0) { /* Si no ocurrió evento, mandar a llamar por timeouts: */ res = dns2_check_timeout (obj); continue; } else if (res_poll < 0) { if (errno == EINTR) continue; /* Caso contrario, salir */ res = DNS_COMMAND_ERROR; break; } /* Ocurrió un evento, revisar cuál se quería y retornarlo para el continue */ if (poller.revents & POLLOUT) { events = DNS_POLL_WANT_WRITE; } else if (poller.revents & POLLIN) { events = DNS_POLL_WANT_READ; } res = dns2_async_continue (obj, events); } /* Leer el paquete respuesta y regresarlo */ return res; } int _dns2_internal_tcp_start_connect (DNS2 *obj) { NameServer *ns; int flags; int res; ns = (NameServer *) obj->current_ns->data; ns->socket = socket (ns->server_addr.ss_family, SOCK_STREAM, 0); /* Si no pude crear un socket, ni siquiera intentar con el siguiente nameserver */ if (ns->socket < 0) { obj->state = DNS_STATE_FREE; obj->direction = DNS_POLL_WANT_NONE; return DNS_COMMAND_ERROR; } /* Aplicar el no-bloqueante */ flags = fcntl (ns->socket, F_GETFL, 0); flags = flags | O_NONBLOCK; fcntl (ns->socket, F_SETFL, flags); /* Ejecutar el connect */ res = connect (ns->socket, (struct sockaddr *) &ns->server_addr, ns->addr_size); if (res < 0) { if (errno == EINPROGRESS) { obj->direction = DNS_POLL_WANT_WRITE; return DNS_COMMAND_POLL; } return _dns2_internal_tcp_next_or_error (obj); } /* Tengo conexión, pasar de estado */ obj->state = DNS_STATE_TCP; obj->direction = DNS_POLL_WANT_WRITE; return dns2_async_continue (obj, DNS_POLL_WANT_WRITE); } int _dns2_internal_tcp_connect_continue (DNS2 *obj) { int optval; socklen_t optlen; int res; NameServer *ns; ns = (NameServer *) obj->current_ns->data; optlen = sizeof (optval); res = getsockopt (ns->socket, SOL_SOCKET, SO_ERROR, &optval, &optlen); if (res < 0 || optval != 0) { return _dns2_internal_tcp_next_or_error (obj); } /* Pasar de estado */ obj->state = DNS_STATE_TCP; obj->direction = DNS_POLL_WANT_WRITE; return dns2_async_continue (obj, DNS_POLL_WANT_WRITE); } int _dns2_internal_tcp_next_or_error (DNS2 *obj) { NameServer *ns; ns = (NameServer *) obj->current_ns->data; close (ns->socket); ns->socket = -1; obj->current_ns = g_list_next (obj->current_ns); if (obj->current_ns != NULL) { obj->state = DNS_STATE_TCP_CONNECT; return _dns2_internal_tcp_start_connect (obj); } else { obj->state = DNS_STATE_FREE; obj->direction = DNS_POLL_WANT_NONE; /* Como ya no hay más nameservers en la lista, entregar error */ return DNS_COMMAND_ERROR; } } int _dns2_internal_udp_next_or_error (DNS2 *obj) { /* Los sockets UDP no se cierran, se dejan para usar después */ obj->current_ns = g_list_next (obj->current_ns); if (obj->current_ns != NULL) { /* Aún hay siguiente servidor, intentarlo con éste */ obj->state = DNS_STATE_UDP; return _dns2_internal_udp_create (obj); } else { /* Como ya no hay mas en la lista, esperar al timeout */ obj->direction = DNS_POLL_WANT_READ; return DNS_COMMAND_POLL; } } int _dns2_internal_udp_send (DNS2 *obj) { int res; void *data; int data_length; NameServer *ns; ns = (NameServer *) obj->current_ns->data; data_length = dns_packet_get_data (obj->packet, (unsigned char **) &data); clock_gettime (CLOCK_MONOTONIC, &obj->last_time_sent); res = sendto (ns->socket, data, data_length, 0, (struct sockaddr *) &ns->server_addr, ns->addr_size); if (res < 0) { if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR) { obj->direction = DNS_POLL_WANT_WRITE; return DNS_COMMAND_POLL; } return _dns2_internal_udp_next_or_error (obj); } else if (res < data_length) { /* Envié la petición incompleta, manejar este error */ return _dns2_internal_udp_next_or_error (obj); } /* Cambio de estado, esperar la lectura */ obj->direction = DNS_POLL_WANT_READ; return dns2_async_continue (obj, DNS_POLL_WANT_READ); } int _dns2_internal_tcp_send (DNS2 *obj) { int res; void *data; int data_length; unsigned char data_plus[8192 + 2]; uint16_t t16; NameServer *ns; ns = (NameServer *) obj->current_ns->data; data_length = dns_packet_get_data (obj->packet, (unsigned char **) &data); /* En TCP, agregar la longitud previa a los datos */ t16 = htons (data_length); memcpy (data_plus, &t16, 2); memcpy (&data_plus[2], data, data_length); res = write (ns->socket, data_plus, data_length + 2); if (res < 0) { if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR) { obj->direction = DNS_POLL_WANT_WRITE; return DNS_COMMAND_POLL; } return _dns2_internal_tcp_next_or_error (obj); } else if (res < data_length) { /* Envié la petición incompleta, manejar este error */ return _dns2_internal_tcp_next_or_error (obj); } /* Cambio de estado, esperar la lectura */ obj->direction = DNS_POLL_WANT_READ; return dns2_async_continue (obj, DNS_POLL_WANT_READ); } int _dns2_internal_udp_recv (DNS2 *obj) { int res; struct sockaddr_storage origen; socklen_t origen_size; unsigned char buffer[8192]; NameServer *ns; ns = (NameServer *) obj->current_ns->data; origen_size = sizeof (origen); res = recvfrom (ns->socket, buffer, sizeof (buffer), 0, (struct sockaddr *) &origen, &origen_size); if (res < 0) { if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR) { obj->direction = DNS_POLL_WANT_READ; return DNS_COMMAND_POLL; } /* ¿Otro error en un socket UDP? -> Mejor seguir esperando una lectura */ obj->direction = DNS_POLL_WANT_READ; return DNS_COMMAND_POLL; } /* Revisar que el paquete venga del mismo origen a donde envié la petición original */ else if (sockaddr_cmp ((struct sockaddr *) &origen, (struct sockaddr *) &ns->server_addr) == 0) { /* Por el momento, dumpear la información int g; printf ("Respuesta recibida:\n"); for (g = 0; g < res; g++) { printf ("%02x", ((unsigned int) buffer[g])); } printf ("\nEnd response\n");*/ if (obj->packet_response != NULL) { dns_packet_free_full (obj->packet_response); obj->packet_response = NULL; } obj->packet_response = dns_packet_create_response (SOCK_DGRAM, buffer, res); if (obj->packet_response == NULL) { /* Como no es válida la respuesta, pasar al siguiente servidor */ return _dns2_internal_udp_next_or_error (obj); } else { if (obj->packet_response->header.id != obj->packet->header.id) { /* Esta no es la respuesta que esperaba */ obj->direction = DNS_POLL_WANT_READ; return DNS_COMMAND_POLL; } /* Finalizar la petición */ dns2_clean_network (obj); obj->state = DNS_STATE_FREE; obj->direction = DNS_POLL_WANT_NONE; return DNS_COMMAND_SUCCESS; } } obj->direction = DNS_POLL_WANT_READ; return DNS_COMMAND_POLL; } int _dns2_internal_tcp_recv (DNS2 *obj) { int res; uint16_t t16; NameServer *ns; ns = (NameServer *) obj->current_ns->data; res = read (ns->socket, &obj->buffer_read[obj->buffer_read_pos], sizeof (obj->buffer_read) - obj->buffer_read_pos); if (res < 0) { if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR) { obj->direction = DNS_POLL_WANT_READ; return DNS_COMMAND_POLL; } /* Si obtuvimos un error, pasar al siguiente servidor */ return _dns2_internal_tcp_next_or_error (obj); } else if (res == 0) { /* ¿Socket cerrado? -> Pasar al siguiente servidor */ return _dns2_internal_tcp_next_or_error (obj); } else { /* Por el momento, dumpear la información int g; printf ("Bytes recibidos por TCP:\n"); for (g = obj->buffer_read_pos; g < obj->buffer_read_pos + res; g++) { printf ("%02x", ((unsigned int) obj->buffer_read[g])); } printf ("\nFin bytes\n");*/ /* Incrementar el buffer */ obj->buffer_read_pos = obj->buffer_read_pos + res; /* Validar la longitud de los datos TCP aquí */ memcpy (&t16, obj->buffer_read, 2); t16 = ntohs (t16); if (t16 + 2 < obj->buffer_read_pos) { /* Datos incompletos TCP, seguir haciendo POLL */ obj->direction = DNS_POLL_WANT_READ; return DNS_COMMAND_POLL; } if (obj->packet_response != NULL) { dns_packet_free_full (obj->packet_response); obj->packet_response = NULL; } obj->packet_response = dns_packet_create_response (SOCK_STREAM, &obj->buffer_read[2], res - 2); if (obj->packet_response == NULL) { /* Error de parseo */ return _dns2_internal_tcp_next_or_error (obj); } else { /* If packet == good */ //dns2_updater_packet_has_valid_tsig (obj, packet); /* Pasar de estado */ close (ns->socket); ns->socket = -1; obj->state = DNS_STATE_FREE; obj->direction = DNS_POLL_WANT_NONE; return DNS_COMMAND_SUCCESS; } } } int _dns2_internal_udp_create (DNS2 *obj) { NameServer *ns; int flags; obj->direction = DNS_POLL_WANT_WRITE; ns = (NameServer *) obj->current_ns->data; /* Crear el socket UDP de una buena vez */ if (ns->socket < 0) { ns->socket = socket (ns->server_addr.ss_family, SOCK_DGRAM, 0); /* Aplicar el no-bloqueante */ flags = fcntl (ns->socket, F_GETFL, 0); flags = flags | O_NONBLOCK; fcntl (ns->socket, F_SETFL, flags); } /* Si no pude crear un socket, tenemos un serio problema */ if (ns->socket < 0) { obj->state = DNS_STATE_FREE; obj->direction = DNS_POLL_WANT_NONE; return DNS_COMMAND_ERROR; } return dns2_async_continue (obj, DNS_POLL_WANT_WRITE); } void dns2_sign_tsig (DNS2 *obj, const char *keyname, const char *signature, const char *algorithm) { DNSRR *rr; /* TODO: Revisar si deberíamos liberar el auth_signature anterior */ rr = dns2_rr_create_tsig (keyname, signature, algorithm); if (rr != NULL) { obj->auth_signature = rr; } } /* Crear un objeto */ DNS2 *dns2_new (void) { DNS2 *obj; obj = (DNS2 *) malloc (sizeof (DNS2)); if (obj == NULL) { return NULL; } obj->packet = NULL; obj->state = DNS_STATE_FREE; obj->direction = DNS_POLL_WANT_NONE; obj->nameservers = NULL; obj->timeout = 5; obj->current_ns = NULL; obj->zone = NULL; obj->force_use_tcp = 0; obj->want_recursion = 0; obj->buffer_read_pos = 0; obj->auth_signature = NULL; obj->packet_response = NULL; return obj; } void dns2_add_nserver (DNS2 *obj, const char *ip) { int res; struct sockaddr_in v4; struct sockaddr_in6 v6; NameServer *ns; memset (&v4, 0, sizeof (v4)); res = inet_pton (AF_INET, ip, &v4.sin_addr); if (res > 0) { /* Es una IP de 4 */ ns = (NameServer *) malloc (sizeof (NameServer)); if (ns == NULL) return; v4.sin_family = AF_INET; v4.sin_port = htons (53); memcpy (&ns->server_addr, &v4, sizeof (v4)); ns->addr_size = sizeof (v4); strncpy (ns->name, ip, sizeof (ns->name)); ns->socket = -1; obj->nameservers = g_list_append (obj->nameservers, ns); return; } memset (&v6, 0, sizeof (v6)); res = inet_pton (AF_INET6, ip, &v6.sin6_addr); if (res > 0) { ns = (NameServer *) malloc (sizeof (NameServer)); if (ns == NULL) return; v6.sin6_family = AF_INET6; v6.sin6_port = htons (53); memcpy (&ns->server_addr, &v6, sizeof (v6)); ns->addr_size = sizeof (v6); strncpy (ns->name, ip, sizeof (ns->name)); obj->nameservers = g_list_append (obj->nameservers, ns); return; } } void dns2_clean_network (DNS2 *obj) { GList *g; NameServer *ns; for (g = obj->nameservers; g != NULL; g = g->next) { ns = (NameServer *) g->data; if (ns->socket >= 0) { close (ns->socket); ns->socket = -1; } } } void dns2_free (DNS2 *obj) { /* Cerrar el socket, si es que hay alguno abierto */ dns2_clean_network (obj); /* Liberar la lista de NS */ g_list_free_full (obj->nameservers, (GDestroyNotify) free); if (obj->zone != NULL) { free (obj->zone); } /* Ahora, buscar si el auth_signature ya está en la lista de additionals. Si lo está, no hay necesidad de liberarlo, se liberará cuando se libere el paquete */ if (obj->auth_signature != NULL) { /* Liberar la firma, porque está fuera del paquete */ dns2_rr_free (obj->auth_signature); obj->auth_signature = NULL; } if (obj->packet != NULL) { dns_packet_free_full (obj->packet); } if (obj->packet_response != NULL) { dns_packet_free_full (obj->packet_response); } free (obj); } int dns2_check_timeout (DNS2 *obj) { struct timespec now; double d; if (obj->state != DNS_STATE_UDP) return DNS_COMMAND_POLL; clock_gettime (CLOCK_MONOTONIC, &now); d = _dns2_diff_timespec (&now, &obj->last_time_sent); if (obj->current_ns->next == NULL) { /* Si estamos al borde del último servidor, necesitamos el timeout largo */ if (d > (double) obj->timeout) { /* Como cayó el timeout largo, incrementar los retries y regresar al primer servidor */ obj->retry_count++; if (obj->retry_count >= 3) { /* Ya agoté todos los reintentos y servidores, bye */ dns2_clean_network (obj); obj->current_ns = NULL; return DNS_COMMAND_ERROR; } obj->current_ns = obj->nameservers; return _dns2_internal_udp_create (obj); } } else if (d > 1.0) { /* 1 segundo entre servidor y servidor */ return _dns2_internal_udp_next_or_error (obj); } return DNS_COMMAND_POLL; } int dns2_get_socket (DNS2 *obj) { NameServer *ns; if (obj == NULL) return -1; if (obj->state == DNS_STATE_FREE) return -1; ns = (NameServer *) obj->current_ns->data; return ns->socket; } int dns2_get_socket_poll_flags (DNS2 *obj) { if (obj == NULL) return -1; if (obj->direction == DNS_POLL_WANT_READ) { return DNS_POLL_WANT_READ; } else if (obj->direction == DNS_POLL_WANT_WRITE) { return DNS_POLL_WANT_WRITE; } return 0; } void dns2_set_use_tcp (DNS2 *obj) { if (obj == NULL) return; obj->force_use_tcp = 1; } DNSRR **dns2_get_answers_from_response (DNS2 *obj) { if (obj == NULL) return NULL; if (obj->packet_response == NULL) return NULL; return dns_packet_get_answers (obj->packet_response); } DNSRR **dns2_get_authority_from_response (DNS2 *obj) { if (obj == NULL) return NULL; if (obj->packet_response == NULL) return NULL; return dns_packet_get_authority (obj->packet_response); } DNSRR **dns2_get_additional_from_response (DNS2 *obj) { if (obj == NULL) return NULL; if (obj->packet_response == NULL) return NULL; return dns_packet_get_additional (obj->packet_response); } int dns2_get_rcode (DNS2 *obj) { if (obj == NULL) return -1; if (obj->packet_response == NULL) return -1; return obj->packet_response->header.rcode; }