Arreglo el procesamiento de la lista de servidores fallidos.

master
Félix Arreola Rodríguez 2022-08-28 12:06:36 -05:00
parent 82fe2dd0c6
commit 670c3c5bd9
1 changed files with 114 additions and 12 deletions

View File

@ -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,19 +355,40 @@ 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 */
/* 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;
obj->state = DNS_STATE_FREE;
obj->direction = DNS_POLL_WANT_NONE;
return DNS_COMMAND_ERROR;
}
}
int _dns2_internal_udp_send (DNS2 *obj) {
@ -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;