487 lines
16 KiB
C
487 lines
16 KiB
C
|
/*
|
||
|
* resolv_manager.c
|
||
|
* This file is part of Network-inador
|
||
|
*
|
||
|
* Copyright (C) 2022 - Félix Arreola Rodríguez
|
||
|
*
|
||
|
* Network-inador is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* Network-inador is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with Network-inador; if not, write to the Free Software
|
||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||
|
* Boston, MA 02110-1301 USA
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <ctype.h>
|
||
|
#include <sys/inotify.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <arpa/inet.h>
|
||
|
|
||
|
#include "resolv_manager.h"
|
||
|
#include "network-inador-private.h"
|
||
|
#include "utils.h"
|
||
|
#include "resolv_conf_parser.h"
|
||
|
#include "interfaces.h"
|
||
|
|
||
|
void resolv_manager_write (NetworkInadorHandle *handle);
|
||
|
|
||
|
int resolv_manager_sort_entries (const void *a, const void *b, void * data) {
|
||
|
const ResolvConfEntry *aa = (ResolvConfEntry *) a;
|
||
|
const ResolvConfEntry *bb = (ResolvConfEntry *) b;
|
||
|
|
||
|
/* Primero, las purgadas van al final */
|
||
|
if (aa->for_purge == 1 && bb->for_purge == 0) {
|
||
|
return 1;
|
||
|
} else if (aa->for_purge == 0 && bb->for_purge == 1) {
|
||
|
return -1;
|
||
|
} else if (aa->for_purge == 1) {
|
||
|
return 0;
|
||
|
}
|
||
|
if (aa->tagged == 1 && bb->tagged == 0) {
|
||
|
/* TODO: Revisar este orden por prioridades */
|
||
|
return -1;
|
||
|
} else if (aa->tagged == 0 && bb->tagged == 1) {
|
||
|
return 1;
|
||
|
} else if (aa->tagged == 1) {
|
||
|
/* Como los dos están en el archivo, comparar por el file_order */
|
||
|
return (aa->file_order - bb->file_order);
|
||
|
}
|
||
|
|
||
|
/* TODO: Estas entradas necesitan resolverse por prioridad de configuración */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void resolv_manager_clear_entries_by_prog (NetworkInadorHandle *handle, const int iface_index, const char *prog) {
|
||
|
FList *g;
|
||
|
ResolvConfEntry *entry;
|
||
|
int do_write = 0;
|
||
|
|
||
|
g = handle->resolver_entries;
|
||
|
while (g != NULL) {
|
||
|
entry = (ResolvConfEntry *) g->data;
|
||
|
|
||
|
if (entry->origin != RESOLV_ORIGIN_RESOLVCONF || entry->owner_interface_index != iface_index) {
|
||
|
/* Solo borramos las entradas creadas via resolvconf y que pertenezcan a esta interfaz */
|
||
|
g = g->next;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (prog == NULL || strcmp (entry->owner_prog, prog) == 0) {
|
||
|
/* Hay que eliminar esta entrada, coincide por la interfaz o nombre de programa */
|
||
|
printf ("/// Eliminando entrada %s por via resolvconf. Prog = «%s»\n", entry->value, prog);
|
||
|
/*free (entry);
|
||
|
|
||
|
handle->resolver_entries = f_list_delete_link (handle->resolver_entries, g);*/
|
||
|
entry->for_purge = 1;
|
||
|
do_write = 1;
|
||
|
}
|
||
|
|
||
|
g = g->next;
|
||
|
}
|
||
|
|
||
|
/* Solicitar una escritura del archivo, via el resolvconf fuimos modificados */
|
||
|
if (do_write == 1) {
|
||
|
resolv_manager_write (handle);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void resolv_manager_clear_tag_on_all (NetworkInadorHandle *handle) {
|
||
|
FList *pos;
|
||
|
ResolvConfEntry *entry;
|
||
|
|
||
|
/* Borrar el atributo "tagged" */
|
||
|
pos = handle->resolver_entries;
|
||
|
while (pos != NULL) {
|
||
|
entry = (ResolvConfEntry *) pos->data;
|
||
|
|
||
|
entry->tagged = 0;
|
||
|
pos = pos->next;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void resolv_manager_process_resolvconf_entries (NetworkInadorHandle *handle, ResolvConfEntry *entries, int num_entries) {
|
||
|
int g;
|
||
|
ResolvConfEntry *entry;
|
||
|
FList *pos_entry;
|
||
|
int do_write = 0;
|
||
|
|
||
|
resolv_manager_clear_tag_on_all (handle);
|
||
|
|
||
|
for (g = 0; g < num_entries; g++) {
|
||
|
entry = NULL;
|
||
|
while (entry == NULL) {
|
||
|
pos_entry = resolv_parser_search_entry (handle->resolver_entries, entries[g].resolv_type, entries[g].ns_family, &entries[g].nameserver, entries[g].value, 1);
|
||
|
if (pos_entry == NULL) break;
|
||
|
|
||
|
/* De ser posible, hacer conciliación */
|
||
|
entry = (ResolvConfEntry *) pos_entry->data;
|
||
|
|
||
|
if (entry->origin == RESOLV_ORIGIN_FILE) {
|
||
|
/* Hacer esta entrada nuestra */
|
||
|
entry->origin = RESOLV_ORIGIN_RESOLVCONF;
|
||
|
entry->owner_interface_index = entries[g].owner_interface_index;
|
||
|
entry->tagged = 1;
|
||
|
strncpy (entry->owner_prog, entries[g].owner_prog, sizeof (entry->owner_prog));
|
||
|
printf ("/// Asociando una entrada %s existente del archivo al: %s\n", entry->value, entry->owner_prog);
|
||
|
#if 0
|
||
|
} else if (entry->origin == RESOLV_ORIGIN_DHCP) {
|
||
|
/* Una entrada que coincide con la actual solo se puede re-asociar al resolvconf si pertenece a la misma interfaz */
|
||
|
if (entry->owner_interface_index != entries[g].owner_interface_index) {
|
||
|
entry->tagged = 1;
|
||
|
entry = NULL;
|
||
|
continue;
|
||
|
}
|
||
|
#endif
|
||
|
} else {
|
||
|
/* Existe la entrada, pero no la podemos tomar como nuestra, crear otra */
|
||
|
entry->tagged = 1;
|
||
|
entry = NULL;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Si no existe, crearla */
|
||
|
if (entry == NULL) {
|
||
|
entry = (ResolvConfEntry *) malloc (sizeof (ResolvConfEntry));
|
||
|
|
||
|
memcpy (entry, &entries[g], sizeof (ResolvConfEntry));
|
||
|
entry->file_order = g; /* TODO: Revisar esto del file order */
|
||
|
entry->tagged = 0;
|
||
|
entry->priority = 0;
|
||
|
entry->for_purge = 0;
|
||
|
|
||
|
/* Anexar al final de la lista */
|
||
|
handle->resolver_entries = f_list_append (handle->resolver_entries, entry);
|
||
|
|
||
|
/* TODO: Como creamos una entrada via el resolvconf, regenerar el archivo */
|
||
|
printf ("/// Nueva entrada %s via resolvconf, programando regeneración.\n", entry->value);
|
||
|
do_write = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Si hubo actualización cambios, disparar ahora */
|
||
|
if (do_write == 1) {
|
||
|
resolv_manager_write (handle);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void resolv_manager_process_dhcp_nameservers (NetworkInadorHandle *handle, Interface *iface) {
|
||
|
FList *pos_entry;
|
||
|
int h;
|
||
|
ResolvConfEntry *entry;
|
||
|
int do_write = 0;
|
||
|
|
||
|
/* Recorrer todas las entradas que sean tipo DHCP y marcar para eliminación */
|
||
|
resolv_manager_clear_tag_on_all (handle);
|
||
|
|
||
|
pos_entry = handle->resolver_entries;
|
||
|
while (pos_entry != NULL) {
|
||
|
entry = (ResolvConfEntry *) pos_entry->data;
|
||
|
|
||
|
if (entry->origin == RESOLV_ORIGIN_DHCP && entry->owner_interface_index == iface->index) {
|
||
|
/* Nos interesa para eliminación */
|
||
|
entry->for_purge = 1;
|
||
|
}
|
||
|
|
||
|
pos_entry = pos_entry->next;
|
||
|
}
|
||
|
|
||
|
/* Ahora, procesar la información de DHCP buscando entradas (viejas o de archivo) para reciclar.
|
||
|
* Si no, crear */
|
||
|
for (h = 0; h < iface->dhcpc.dns_c; h++) {
|
||
|
entry = NULL;
|
||
|
while (entry == NULL) {
|
||
|
pos_entry = resolv_parser_search_entry (handle->resolver_entries, RESOLV_TYPE_NAMESERVER, AF_INET, &iface->dhcpc.dns[h], "", 1);
|
||
|
if (pos_entry == NULL) break;
|
||
|
|
||
|
/* De ser posible, hacer conciliación */
|
||
|
entry = (ResolvConfEntry *) pos_entry->data;
|
||
|
|
||
|
if (entry->origin == RESOLV_ORIGIN_FILE) {
|
||
|
/* Hacer esta entrada nuestra */
|
||
|
entry->origin = RESOLV_ORIGIN_DHCP;
|
||
|
entry->owner_interface_index = iface->index;
|
||
|
entry->tagged = 1;
|
||
|
entry->owner_prog[0] = 0;
|
||
|
printf ("/// Asociando una entrada %s existente del archivo al DHCP (%s)\n", entry->value, iface->name);
|
||
|
} else if (entry->origin == RESOLV_ORIGIN_DHCP && entry->owner_interface_index == iface->index) {
|
||
|
/* Una entrada vieja coincide, reciclar */
|
||
|
entry->tagged = 1;
|
||
|
entry->for_purge = 0;
|
||
|
} else {
|
||
|
/* Existe la entrada, pero no la podemos tomar como nuestra, buscar otra */
|
||
|
entry->tagged = 1;
|
||
|
entry = NULL;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Si la entrada no se pudo conciliar, crear una */
|
||
|
if (entry == NULL) {
|
||
|
entry = (ResolvConfEntry *) malloc (sizeof (ResolvConfEntry));
|
||
|
|
||
|
entry->resolv_type = RESOLV_TYPE_NAMESERVER;
|
||
|
entry->origin = RESOLV_ORIGIN_DHCP;
|
||
|
entry->owner_interface_index = iface->index;
|
||
|
entry->owner_prog[0] = 0;
|
||
|
entry->file_order = h;
|
||
|
entry->tagged = 0;
|
||
|
entry->priority = 0;
|
||
|
entry->for_purge = 0;
|
||
|
entry->ns_family = AF_INET;
|
||
|
memcpy (&entry->nameserver, &iface->dhcpc.dns[h], 4);
|
||
|
inet_ntop (AF_INET, &entry->nameserver, entry->value, sizeof (entry->value));
|
||
|
|
||
|
/* Anexar al final de la lista */
|
||
|
handle->resolver_entries = f_list_append (handle->resolver_entries, entry);
|
||
|
|
||
|
/* Como creamos una entrada via el dhcp, regenerar el archivo */
|
||
|
printf ("/// Nueva entrada %s via DHCP (%s), programando regeneración.\n", entry->value, iface->name);
|
||
|
do_write = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Si tiene un nombre de dominio, agregar como entrada tipo search */
|
||
|
if (iface->dhcpc.domain_name[0] != 0) {
|
||
|
entry = NULL;
|
||
|
while (entry == NULL) {
|
||
|
pos_entry = resolv_parser_search_entry (handle->resolver_entries, RESOLV_TYPE_SEARCH, AF_INET, NULL, iface->dhcpc.domain_name, 1);
|
||
|
if (pos_entry == NULL) break;
|
||
|
|
||
|
/* De ser posible, hacer conciliación */
|
||
|
entry = (ResolvConfEntry *) pos_entry->data;
|
||
|
|
||
|
if (entry->origin == RESOLV_ORIGIN_FILE) {
|
||
|
/* Hacer esta entrada nuestra */
|
||
|
entry->origin = RESOLV_ORIGIN_DHCP;
|
||
|
entry->owner_interface_index = iface->index;
|
||
|
entry->tagged = 1;
|
||
|
entry->owner_prog[0] = 0;
|
||
|
printf ("/// Asociando una entrada search %s existente del archivo al DHCP (%s)\n", entry->value, iface->name);
|
||
|
} else if (entry->origin == RESOLV_ORIGIN_DHCP && entry->owner_interface_index == iface->index) {
|
||
|
/* Una entrada vieja coincide, reciclar */
|
||
|
entry->tagged = 1;
|
||
|
entry->for_purge = 0;
|
||
|
} else {
|
||
|
/* Existe la entrada, pero no la podemos tomar como nuestra, buscar otra */
|
||
|
entry->tagged = 1;
|
||
|
entry = NULL;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Si la entrada no se pudo conciliar, crear una */
|
||
|
if (entry == NULL) {
|
||
|
entry = (ResolvConfEntry *) malloc (sizeof (ResolvConfEntry));
|
||
|
|
||
|
entry->resolv_type = RESOLV_TYPE_SEARCH;
|
||
|
entry->origin = RESOLV_ORIGIN_DHCP;
|
||
|
entry->owner_interface_index = iface->index;
|
||
|
entry->owner_prog[0] = 0;
|
||
|
entry->file_order = h;
|
||
|
entry->tagged = 0;
|
||
|
entry->priority = 0;
|
||
|
entry->for_purge = 0;
|
||
|
entry->ns_family = AF_INET;
|
||
|
memset (&entry->nameserver, 0, sizeof (entry->nameserver));
|
||
|
strncpy (entry->value, iface->dhcpc.domain_name, sizeof (entry->value));
|
||
|
|
||
|
/* Anexar al final de la lista */
|
||
|
handle->resolver_entries = f_list_append (handle->resolver_entries, entry);
|
||
|
|
||
|
/* Como creamos una entrada via el dhcp, regenerar el archivo */
|
||
|
printf ("/// Nueva entrada search %s via DHCP (%s), programando regeneración.\n", entry->value, iface->name);
|
||
|
do_write = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Después de recorrer las entradas, revisar si quedaron entradas por purgar.
|
||
|
* Si hay entradas por purgar, programar una reescritura de archivo */
|
||
|
pos_entry = handle->resolver_entries;
|
||
|
while (pos_entry != NULL) {
|
||
|
entry = (ResolvConfEntry *) pos_entry->data;
|
||
|
|
||
|
if (entry->for_purge == 1) {
|
||
|
do_write = 1;
|
||
|
printf ("/// Entrada %s eliminada via DHCP (%s), programando regeneración.\n", entry->value, iface->name);
|
||
|
//break;
|
||
|
}
|
||
|
pos_entry = pos_entry->next;
|
||
|
}
|
||
|
|
||
|
if (do_write) {
|
||
|
resolv_manager_write (handle);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void resolv_manager_clear_dhcp_nameservers (NetworkInadorHandle *handle, Interface *iface) {
|
||
|
FList *pos_entry;
|
||
|
int h;
|
||
|
ResolvConfEntry *entry;
|
||
|
int do_write = 0;
|
||
|
|
||
|
/* Recorrer todas las entradas que sean tipo DHCP y marcar para eliminación */
|
||
|
resolv_manager_clear_tag_on_all (handle);
|
||
|
|
||
|
pos_entry = handle->resolver_entries;
|
||
|
while (pos_entry != NULL) {
|
||
|
entry = (ResolvConfEntry *) pos_entry->data;
|
||
|
|
||
|
if (entry->origin == RESOLV_ORIGIN_DHCP && entry->owner_interface_index == iface->index) {
|
||
|
/* Nos interesa para eliminación */
|
||
|
printf ("/// Entrada %s eliminada via DHCP (%s), programando regeneración.\n", entry->value, iface->name);
|
||
|
entry->for_purge = 1;
|
||
|
do_write = 1;
|
||
|
}
|
||
|
|
||
|
pos_entry = pos_entry->next;
|
||
|
}
|
||
|
|
||
|
if (do_write) {
|
||
|
resolv_manager_write (handle);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void resolv_manager_read_local_etc_resolv (NetworkInadorHandle *handle) {
|
||
|
FILE *fd;
|
||
|
FList *pos, *next;
|
||
|
ResolvConfEntry *entry;
|
||
|
|
||
|
fd = fopen ("/etc/resolv.conf", "r");
|
||
|
if (fd == NULL) return;
|
||
|
|
||
|
printf ("/// Ejecutando lectura de resolv.conf\n");
|
||
|
/* Etiquetar las entradas con origen del archivo como "ya no en el archivo", para que cuando se lea el archivo, nos queden las que vamos a eliminar */
|
||
|
resolv_manager_clear_tag_on_all (handle);
|
||
|
|
||
|
handle->resolver_entries = resolv_parser_parse_local_file (handle->resolver_entries, fd, RESOLV_ORIGIN_FILE);
|
||
|
|
||
|
fclose (fd);
|
||
|
|
||
|
/* A la salida, borrar las que ya no siguen en el archivo */
|
||
|
pos = handle->resolver_entries;
|
||
|
while (pos != NULL) {
|
||
|
//next = pos->next;
|
||
|
entry = (ResolvConfEntry *) pos->data;
|
||
|
|
||
|
if (entry->origin == RESOLV_ORIGIN_FILE && entry->tagged == 0) {
|
||
|
/* Esta entrada se va, la eliminaron */
|
||
|
printf ("/// La entrada %s del resolv.conf fué eliminada del archivo. Purgando nosotros.\n", entry->value);
|
||
|
|
||
|
entry->for_purge = 1;
|
||
|
/*handle->resolver_entries = f_list_delete_link (handle->resolver_entries, pos);
|
||
|
free (entry);
|
||
|
entry = NULL;*/
|
||
|
} else {
|
||
|
printf ("/// La entrada %s del resolv.conf sigue en el archivo.\n", entry->value);
|
||
|
}
|
||
|
pos = pos->next;
|
||
|
}
|
||
|
|
||
|
/* Reordenar las entradas. TODO: Falta leer los ficheros de configuración del resolv.conf */
|
||
|
handle->resolver_entries = f_list_sort_with_data (handle->resolver_entries, resolv_manager_sort_entries, NULL);
|
||
|
}
|
||
|
|
||
|
void resolv_manager_write (NetworkInadorHandle *handle) {
|
||
|
FILE *fd;
|
||
|
FList *pos, *next;
|
||
|
ResolvConfEntry *entry;
|
||
|
Interface *iface;
|
||
|
|
||
|
/* Antes de hacer la escritura, siempre hacemos una lectura */
|
||
|
resolv_manager_read_local_etc_resolv (handle);
|
||
|
|
||
|
/* Intentar la escritura */
|
||
|
fd = fopen ("/etc/resolv.conf", "w");
|
||
|
if (fd != NULL) {
|
||
|
fprintf (fd, "# Autogenered by NetworkInador, you can change this file, changes are preserved");
|
||
|
fprintf (fd, "\n\n");
|
||
|
|
||
|
pos = handle->resolver_entries;
|
||
|
while (pos != NULL) {
|
||
|
entry = (ResolvConfEntry *) pos->data;
|
||
|
|
||
|
if (entry->for_purge == 1) {
|
||
|
/* Omitir */
|
||
|
pos = pos->next;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
iface = _interfaces_locate_by_index (handle->interfaces, entry->owner_interface_index);
|
||
|
/* Escribir esta entrada "buena" al resolv.conf */
|
||
|
if (entry->origin == RESOLV_ORIGIN_FILE) {
|
||
|
fprintf (fd, "# This entry was previously on file, preserving");
|
||
|
} else if (entry->origin == RESOLV_ORIGIN_DHCP) {
|
||
|
fprintf (fd, "# This entry was added via DHCP (on %s) by NetworkInador", (iface != NULL ? iface->name : "unknown"));
|
||
|
} else if (entry->origin == RESOLV_ORIGIN_RESOLVCONF) {
|
||
|
fprintf (fd, "# This entry was added via resolvconf (%s.%s)", (iface != NULL ? iface->name : "unknown"), entry->owner_prog);
|
||
|
} else if (entry->origin == RESOLV_ORIGIN_SLAAC_RDNSS) {
|
||
|
fprintf (fd, "# This entry was added via RDNSS (IPv6)");
|
||
|
}
|
||
|
fprintf (fd, "\n");
|
||
|
|
||
|
if (entry->resolv_type == RESOLV_TYPE_NAMESERVER) {
|
||
|
fprintf (fd, "nameserver %s\n", entry->value);
|
||
|
} else if (entry->resolv_type == RESOLV_TYPE_DOMAIN) {
|
||
|
fprintf (fd, "domain %s\n", entry->value);
|
||
|
} else if (entry->resolv_type == RESOLV_TYPE_SEARCH) {
|
||
|
fprintf (fd, "search %s\n", entry->value);
|
||
|
} else if (entry->resolv_type == RESOLV_TYPE_SORTLIST) {
|
||
|
fprintf (fd, "sortlist %s\n", entry->value);
|
||
|
} else if (entry->resolv_type == RESOLV_TYPE_OPTIONS) {
|
||
|
fprintf (fd, "options %s\n", entry->value);
|
||
|
}
|
||
|
pos = pos->next;
|
||
|
}
|
||
|
|
||
|
fclose (fd);
|
||
|
}
|
||
|
|
||
|
/* Purgar las entradas marcadas para purge */
|
||
|
pos = handle->resolver_entries;
|
||
|
while (pos != NULL) {
|
||
|
next = pos->next;
|
||
|
entry = (ResolvConfEntry *) pos->data;
|
||
|
|
||
|
if (entry->for_purge == 1) {
|
||
|
handle->resolver_entries = f_list_delete_link (handle->resolver_entries, pos);
|
||
|
free (entry);
|
||
|
entry = NULL;
|
||
|
}
|
||
|
pos = next;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void resolv_manager_init (NetworkInadorHandle *handle) {
|
||
|
|
||
|
//handle->resolver_inotify_fd = inotify_init1 (IN_NONBLOCK | IN_CLOEXEC);
|
||
|
|
||
|
if (handle->resolver_inotify_fd >= 0) {
|
||
|
/* Debemos primero instalar el manejador de eventos de escritura sobre el resolv.conf */
|
||
|
/*
|
||
|
IN_CLOSE_WRITE Releer archivo.
|
||
|
IN_DELETE_SELF Dejar de hacer watch
|
||
|
IN_MOVE_SELF Dejar de hacer watch
|
||
|
*/
|
||
|
|
||
|
//handle->resolver_inotify_watch = inotify_add_watch (handle->resolver_inotify_fd, "/etc/resolv.conf",
|
||
|
}
|
||
|
|
||
|
handle->resolver_entries = NULL;
|
||
|
/* Luego, leer el resolv.conf */
|
||
|
resolv_manager_read_local_etc_resolv (handle);
|
||
|
}
|
||
|
|