diff --git a/src/dns2.c b/src/dns2.c index 4274ccd..4aa1916 100644 --- a/src/dns2.c +++ b/src/dns2.c @@ -33,6 +33,7 @@ typedef struct { socklen_t addr_size; int socket; + int fatal_network_error; } NameServer; @@ -81,7 +82,6 @@ int dns2_async (DNS2 *obj) { 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; } @@ -212,6 +212,64 @@ int dns2_sync (DNS2 *obj) { return res; } +GList * _dns2_internal_get_last_usable_ns (DNS2 *obj) { + GList *next; + NameServer *ns; + + next = g_list_last (obj->nameservers); + while (next != NULL) { + ns = (NameServer *) next->data; + if (ns->fatal_network_error == 1) { + next = g_list_previous (next); + continue; + } + + return next; + } + + return NULL; +} + +GList * _dns2_internal_get_next_ns (DNS2 *obj, GList *start) { + /* Tenemos que sacar el siguiente de la lista en una forma circular, + * sin darle la vuelta, omitiendo los que ya están etiquetados con error */ + GList *next; + NameServer *ns; + + if (start == NULL) { + /* Empezar desde el inicio de la lista sin ciclarnos */ + next = obj->nameservers; + while (next != NULL) { + ns = (NameServer *) next->data; + if (ns->fatal_network_error == 1) { + next = g_list_next (next); + continue; + } + + break; + } + + return next; + } + + /* Si nos dan un inicio, dar la vuelta de la otra forma */ + next = start; + do { + next = g_list_next (next); + if (next == NULL) next = obj->nameservers; + + if (next == start) { + /* Como le dí la vuelta, se acabó */ + return NULL; + } + ns = (NameServer *) next->data; + if (ns->fatal_network_error == 1) continue; + break; + } while (1); + + return next; +} + int _dns2_internal_tcp_start_connect (DNS2 *obj) { NameServer *ns; int flags; @@ -297,18 +355,39 @@ int _dns2_internal_tcp_next_or_error (DNS2 *obj) { int _dns2_internal_udp_next_or_error (DNS2 *obj) { /* Los sockets UDP no se cierran, se dejan para usar después */ + GList *next; + NameServer *ns; - obj->current_ns = g_list_next (obj->current_ns); - if (obj->current_ns != NULL) { + /* Buscar un siguiente que no haya tenido un error fatal */ + next = _dns2_internal_get_next_ns (obj, obj->current_ns); + if (obj->current_ns->next == NULL) { + /* Como estoy al borde de la lista, + * pedir mejor timeout para que se ejecute el timeout largo, + * sin cambiar de ns */ + + obj->direction = DNS_POLL_WANT_READ; + return DNS_COMMAND_POLL; + } else if (next != NULL) { /* Aún hay siguiente servidor, intentarlo con éste */ + obj->current_ns = next; 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; + /* Si el actual no tiene marca de error de red, pedir timeout */ + ns = (NameServer *) obj->current_ns->data; + if (ns->fatal_network_error == 0) { + obj->direction = DNS_POLL_WANT_READ; + return DNS_COMMAND_POLL; + } + /* Como ya no hay mas en la lista, marcar error */ + dns2_clean_network (obj); + obj->current_ns = NULL; - return DNS_COMMAND_POLL; + obj->state = DNS_STATE_FREE; + obj->direction = DNS_POLL_WANT_NONE; + + return DNS_COMMAND_ERROR; } } @@ -333,10 +412,11 @@ int _dns2_internal_udp_send (DNS2 *obj) { return DNS_COMMAND_POLL; } + ns->fatal_network_error = 1; return _dns2_internal_udp_next_or_error (obj); } else if (res < data_length) { /* Envié la petición incompleta, manejar este error */ - + ns->fatal_network_error = 1; return _dns2_internal_udp_next_or_error (obj); } @@ -429,6 +509,7 @@ int _dns2_internal_udp_recv (DNS2 *obj) { if (obj->packet_response == NULL) { /* Como no es válida la respuesta, pasar al siguiente servidor */ + ns->fatal_network_error = 1; return _dns2_internal_udp_next_or_error (obj); } else { if (obj->packet_response->header.id != obj->packet->header.id) { @@ -611,6 +692,7 @@ void dns2_add_nserver (DNS2 *obj, const char *ip) { ns->addr_size = sizeof (v4); strncpy (ns->name, ip, sizeof (ns->name)); ns->socket = -1; + ns->fatal_network_error = 0; obj->nameservers = g_list_append (obj->nameservers, ns); @@ -645,6 +727,7 @@ void dns2_clean_network (DNS2 *obj) { ns = (NameServer *) g->data; if (ns->socket >= 0) { + ns->fatal_network_error = 0; close (ns->socket); ns->socket = -1; } @@ -652,7 +735,6 @@ void dns2_clean_network (DNS2 *obj) { } void dns2_free (DNS2 *obj) { - /* Cerrar el socket, si es que hay alguno abierto */ dns2_clean_network (obj); @@ -684,14 +766,20 @@ void dns2_free (DNS2 *obj) { int dns2_check_timeout (DNS2 *obj) { struct timespec now; double d; + GList *last_usable; + int pos_cur, pos_last; 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 */ + last_usable = _dns2_internal_get_last_usable_ns (obj); + + pos_cur = g_list_position (obj->nameservers, obj->current_ns); + pos_last = g_list_position (obj->nameservers, last_usable); + if (pos_cur >= pos_last) { + /* Si estamos al borde del último servidor usable, 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++; @@ -700,14 +788,27 @@ int dns2_check_timeout (DNS2 *obj) { dns2_clean_network (obj); obj->current_ns = NULL; + obj->state = DNS_STATE_FREE; + obj->direction = DNS_POLL_WANT_NONE; + return DNS_COMMAND_ERROR; } - obj->current_ns = obj->nameservers; + obj->current_ns = _dns2_internal_get_next_ns (obj, NULL); + if (obj->current_ns == NULL) { + /* No quedan servidores, bye bye */ + dns2_clean_network (obj); + obj->current_ns = NULL; + + obj->state = DNS_STATE_FREE; + obj->direction = DNS_POLL_WANT_NONE; + + return DNS_COMMAND_ERROR; + } return _dns2_internal_udp_create (obj); } } else if (d > 1.0) { - /* 1 segundo entre servidor y servidor */ + /* Como ocurrió 1 segundo de timeout en este servidor, pedir pasar al siguiente */ return _dns2_internal_udp_next_or_error (obj); } @@ -721,6 +822,7 @@ int dns2_get_socket (DNS2 *obj) { if (obj->state == DNS_STATE_FREE) return -1; + if (obj->current_ns == NULL) return -1; ns = (NameServer *) obj->current_ns->data; return ns->socket;