/* * 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 #include #include #include #include #include #include #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); }