diff --git a/src/manager.c b/src/manager.c index 06a6b28..cd6910e 100644 --- a/src/manager.c +++ b/src/manager.c @@ -46,7 +46,6 @@ #include "bridge.h" #include "network-inador-manager.h" #include "dhcp_client.h" -#include "struct_addr_union.h" #include "resolv_manager.h" #define COMMAND_SOCKET_PATH "/tmp/network-inador.socket" @@ -824,20 +823,19 @@ static void _manager_execute_resolvconf_feed (ManagerClientInfo *manager_client, int name_len, wanted; unsigned char iface_prog[256]; unsigned char iface_name[256], prog[256]; - char value[2048]; char *point; - ResolvConfEntry *entry; - GList *pos_entry; + ResolvConfEntry *resolv_entries; + int entries_count; int ns_count; int search_count; - int g, h, pos; + int g, h, pos, ns_pos; int family; - int order = 0; - struct_addr direccion; int iface_index; Interface *iface; + int do_write = 0; + if (buffer_len < 3) { _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_RESOLVCONF_FEED); return; @@ -874,7 +872,8 @@ static void _manager_execute_resolvconf_feed (ManagerClientInfo *manager_client, if (iface != NULL) { iface_index = iface->index; } else { - iface_index = 0; + _manager_send_error (manager_client, NET_INADOR_ERROR_INVALID_VALUE, NET_INADOR_COMMAND_RESOLVCONF_FEED); + return; } pos = 3 + name_len; @@ -887,6 +886,10 @@ static void _manager_execute_resolvconf_feed (ManagerClientInfo *manager_client, ns_count = buffer[pos]; pos++; + /* TODO: Sumar las otras entradas */ + entries_count = ns_count; + + ns_pos = pos; /* Los nameserver vienen con un byte de familia y luego los bytes correspondientes a la IP */ for (g = 0; g < ns_count; g++) { /* Revisar que en la longitud venga el byte de la familia */ @@ -914,58 +917,40 @@ static void _manager_execute_resolvconf_feed (ManagerClientInfo *manager_client, _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_RESOLVCONF_FEED); return; } - - /* Tengo un Nameserver completo, buscar y dependiendo del caso, actualizar o crear */ - memcpy (&direccion, &buffer[pos], wanted); - inet_ntop (family, &direccion, value, sizeof (value)); - - pos_entry = resolv_parser_search_entry (manager_client->manager->handle->resolver_entries, RESOLV_TYPE_NAMESERVER, family, &direccion, value); - if (pos_entry != NULL) { - /* 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 = iface_index; - strncpy (entry->owner_prog, prog, sizeof (entry->owner_prog)); - printf ("/// Asociando una entrada %s existente del archivo al: %s\n", entry->value, entry->owner_prog); - } else { - /* Existe la entrada, pero no la podemos tomar como nuestra, crear otra */ - entry = NULL; - } - } else { - entry = NULL; - } - - /* Si no existe, crearla */ - if (entry == NULL) { - entry = (ResolvConfEntry *) malloc (sizeof (ResolvConfEntry)); - - entry->origin = RESOLV_ORIGIN_RESOLVCONF; - entry->resolv_type = RESOLV_TYPE_NAMESERVER; - entry->file_order = 0; - entry->is_on_file = 0; - entry->priority = 0; - - entry->ns_family = family; - memcpy (&entry->nameserver, &direccion, wanted); - strncpy (entry->value, value, sizeof (entry->value)); - entry->owner_interface_index = iface_index; - strncpy (entry->owner_prog, prog, sizeof (entry->owner_prog)); - - /* Anexar al final de la lista */ - manager_client->manager->handle->resolver_entries = g_list_append (manager_client->manager->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); - } + pos += wanted; } - /* Ahora crear y parsear */ + /* TODO: Validar otro tipo de entradas */ - /* OK */ + /* Notificar al cliente */ _manager_send_executed (manager_client); + + + /* Re-agrupar los datos para enviarlos al resolv_manager */ + resolv_entries = (ResolvConfEntry *) malloc (sizeof (ResolvConfEntry) * entries_count); + h = 0; + + pos = ns_pos; + for (g = 0; g < ns_count; g++) { + family = buffer[pos]; + resolv_entries[h].ns_family = family; + pos++; /* El byte de la familia */ + + wanted = (family == AF_INET) ? 4 : 16; + + /* Tengo un Nameserver completo, buscar y dependiendo del caso, actualizar o crear */ + memcpy (&resolv_entries[h].nameserver, &buffer[pos], wanted); + inet_ntop (family, &resolv_entries[h].nameserver, resolv_entries[h].value, sizeof (resolv_entries[h].value)); + + pos += wanted; + resolv_entries[h].resolv_type = RESOLV_TYPE_NAMESERVER; + resolv_entries[h].origin = RESOLV_ORIGIN_RESOLVCONF; + resolv_entries[h].owner_interface_index = iface_index; + strncpy (resolv_entries[h].owner_prog, prog, sizeof (resolv_entries[h].owner_prog)); + h++; + } + + resolv_manager_process_resolvconf_entries (manager_client->manager->handle, resolv_entries, entries_count); } static void _manager_execute_resolvconf_remove (ManagerClientInfo *manager_client, unsigned char *buffer, int buffer_len) { diff --git a/src/resolv_conf_defs.h b/src/resolv_conf_defs.h index 8beeaaa..f7d89d8 100644 --- a/src/resolv_conf_defs.h +++ b/src/resolv_conf_defs.h @@ -53,7 +53,8 @@ typedef struct _ResolvConfEntry { int origin; int file_order; - int is_on_file; + int tagged; + int for_purge; int priority; int ns_family; diff --git a/src/resolv_conf_helper.c b/src/resolv_conf_helper.c index a3c00aa..05fe7d5 100644 --- a/src/resolv_conf_helper.c +++ b/src/resolv_conf_helper.c @@ -40,6 +40,7 @@ #include "resolv_conf_defs.h" #include "struct_addr_union.h" #include "resolv_conf_parser.h" +#include "utils.h" #define COMMAND_SOCKET_PATH "/tmp/network-inador.socket" @@ -75,6 +76,50 @@ int wait_for_ack_or_error (int s) { return -1; } +GList * helper_parser_parse_input (GList *all_entries, FILE *fd, int origin) { + char buffer[8192], value[2048]; + ResolvConfEntry *entry = NULL; + GList *pos, *next; + int g, len; + struct_addr direccion; + int type; + int family; + int order; + + order = 0; + while (fgets (buffer, sizeof (buffer), fd), feof (fd) == 0) { + utils_trim (buffer); + utils_trim_comment (buffer); + if (buffer[0] == 0) continue; + + type = resolv_conf_parse_line (buffer, &family, &direccion, value, sizeof (value)); + + if (type == -1) { + /* Linea no válida de acuerdo al parser, ignorar */ + continue; + } + + /* Desde el resolvconf helper, siempre anexar al final de la lista */ + entry = (ResolvConfEntry *) malloc (sizeof (ResolvConfEntry)); + + entry->resolv_type = type; + entry->origin = origin; + + entry->ns_family = family; + memcpy (&entry->nameserver, &direccion, sizeof (direccion)); + strncpy (entry->value, value, sizeof (entry->value)); + + entry->owner_interface_index = 0; + entry->owner_prog[0] = 0; + + all_entries = g_list_append (all_entries, entry); + entry->tagged = 1; + entry->file_order = order++; + } + + return all_entries; +} + void send_entries (int s, GList *entries, const char *iface_prog) { char buffer[2048]; int pos, len, pos_name_count; @@ -243,7 +288,7 @@ int main (int argc, char *argv[]) { if (action == 'a') { /* Parsear el archivo primero */ - entries = resolv_parser_parse_file (NULL, stdin, RESOLV_ORIGIN_RESOLVCONF); + entries = helper_parser_parse_input (NULL, stdin, RESOLV_ORIGIN_RESOLVCONF); /* Preparar un paquete de datos para enviar via el socket */ send_entries (s, entries, iface_prog); diff --git a/src/resolv_conf_parser.c b/src/resolv_conf_parser.c index 0efcae2..5261e23 100644 --- a/src/resolv_conf_parser.c +++ b/src/resolv_conf_parser.c @@ -88,7 +88,7 @@ int resolv_conf_parse_line (const char *line, int *ns_family, struct_addr *names return -1; } -GList * resolv_parser_search_entry (GList *all_entries, const int resolv_type, const int ns_family, const struct_addr *nameserver, const char *value) { +GList * resolv_parser_search_entry (GList *all_entries, const int resolv_type, const int ns_family, const struct_addr *nameserver, const char *value, int skip_tagged) { GList *g; ResolvConfEntry *entry; @@ -102,6 +102,12 @@ GList * resolv_parser_search_entry (GList *all_entries, const int resolv_type, c continue; } + if (skip_tagged == 1 && entry->tagged == 1) { + /* Pasar a la siguiente entrada, nos pidieron omitir las marcadas en el archivo */ + g = g->next; + continue; + } + /* Comparar dependiendo del tipo */ if (resolv_type == RESOLV_TYPE_NAMESERVER) { if (ns_family == AF_INET && entry->ns_family == AF_INET) { @@ -127,7 +133,7 @@ GList * resolv_parser_search_entry (GList *all_entries, const int resolv_type, c return NULL; } -GList * resolv_parser_parse_file (GList *all_entries, FILE *fd, int origin) { +GList * resolv_parser_parse_local_file (GList *all_entries, FILE *fd, int origin) { char buffer[8192], value[2048]; ResolvConfEntry *entry = NULL; GList *pos, *next; @@ -137,7 +143,7 @@ GList * resolv_parser_parse_file (GList *all_entries, FILE *fd, int origin) { int family; int order; - order = 0; + order = 1; while (fgets (buffer, sizeof (buffer), fd), feof (fd) == 0) { utils_trim (buffer); utils_trim_comment (buffer); @@ -150,7 +156,7 @@ GList * resolv_parser_parse_file (GList *all_entries, FILE *fd, int origin) { continue; } /* Tenemos una linea buena, buscar si esta linea ya existe en nuestra lista de entradas */ - pos = resolv_parser_search_entry (all_entries, type, family, &direccion, value); + pos = resolv_parser_search_entry (all_entries, type, family, &direccion, value, 1); if (pos != NULL) { /* Ya existe, refrescar que está en el archivo */ @@ -161,6 +167,7 @@ GList * resolv_parser_parse_file (GList *all_entries, FILE *fd, int origin) { entry->resolv_type = type; entry->origin = origin; + entry->for_purge = 0; entry->ns_family = family; memcpy (&entry->nameserver, &direccion, sizeof (direccion)); @@ -171,7 +178,7 @@ GList * resolv_parser_parse_file (GList *all_entries, FILE *fd, int origin) { all_entries = g_list_append (all_entries, entry); } - entry->is_on_file = 1; + entry->tagged = 1; entry->file_order = order++; } diff --git a/src/resolv_conf_parser.h b/src/resolv_conf_parser.h index e01b43a..fea694f 100644 --- a/src/resolv_conf_parser.h +++ b/src/resolv_conf_parser.h @@ -32,8 +32,9 @@ #include "common.h" #endif -GList * resolv_parser_search_entry (GList *all_entries, const int resolv_type, const int ns_family, const struct_addr *nameserver, const char *value); -GList * resolv_parser_parse_file (GList *all_entries, FILE *fd, int origin); +int resolv_conf_parse_line (const char *line, int *ns_family, struct_addr *nameserver, char *value, const size_t value_size); +GList * resolv_parser_search_entry (GList *all_entries, const int resolv_type, const int ns_family, const struct_addr *nameserver, const char *value, int skip_on_file); +GList * resolv_parser_parse_local_file (GList *all_entries, FILE *fd, int origin); #endif /* __RESOLV_CONF_PARSER_H__ */ diff --git a/src/resolv_manager.c b/src/resolv_manager.c index 54ba3fe..007c68a 100644 --- a/src/resolv_manager.c +++ b/src/resolv_manager.c @@ -27,40 +27,154 @@ #include #include +#include "resolv_manager.h" #include "common.h" #include "utils.h" #include "resolv_conf_parser.h" +#include "interfaces.h" + +void resolv_manager_write (NetworkInadorHandle *handle); + +gint resolv_manager_sort_entries (const void *a, const void *b, gpointer 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) { - GList *g, *next; + GList *g; ResolvConfEntry *entry; + int do_write = 0; g = handle->resolver_entries; while (g != NULL) { - next = g->next; - entry = (ResolvConfEntry *) g->data; if (entry->origin != RESOLV_ORIGIN_RESOLVCONF) { /* Solo borramos las entradas creadas via resolvconf */ - g = next; + g = g->next; continue; } if (entry->owner_interface_index != iface_index) { - g = next; + 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); + /*free (entry); - handle->resolver_entries = g_list_delete_link (handle->resolver_entries, g); + handle->resolver_entries = g_list_delete_link (handle->resolver_entries, g);*/ + entry->for_purge = 1; + do_write = 1; } - g = next; + 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) { + GList *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; + GList *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); + } 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; + } + } 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 = g_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); } } @@ -74,34 +188,101 @@ void resolv_manager_read_local_etc_resolv (NetworkInadorHandle *handle) { 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 */ - /* Borrar cuáles están en el archivo */ - pos = handle->resolver_entries; - while (pos != NULL) { - entry = (ResolvConfEntry *) pos->data; - - entry->is_on_file = 0; - pos = pos->next; - } + resolv_manager_clear_tag_on_all (handle); - handle->resolver_entries = resolv_parser_parse_file (handle->resolver_entries, fd, RESOLV_ORIGIN_FILE); + 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; + //next = pos->next; entry = (ResolvConfEntry *) pos->data; - if (entry->origin == RESOLV_ORIGIN_FILE && entry->is_on_file == 0) { + 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 = g_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 = g_list_sort_with_data (handle->resolver_entries, resolv_manager_sort_entries, NULL); +} + +void resolv_manager_write (NetworkInadorHandle *handle) { + FILE *fd; + GList *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) via 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 = g_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 = next; } @@ -125,13 +306,5 @@ void resolv_manager_init (NetworkInadorHandle *handle) { handle->resolver_entries = NULL; /* Luego, leer el resolv.conf */ resolv_manager_read_local_etc_resolv (handle); - - resolv_manager_read_local_etc_resolv (handle); - - resolv_manager_read_local_etc_resolv (handle); - - resolv_manager_read_local_etc_resolv (handle); - - resolv_manager_read_local_etc_resolv (handle); } diff --git a/src/resolv_manager.h b/src/resolv_manager.h index 35a2cf8..4e3b8d9 100644 --- a/src/resolv_manager.h +++ b/src/resolv_manager.h @@ -23,11 +23,15 @@ #ifndef __RESOLV_MANAGER_H__ #define __RESOLV_MANAGER_H__ +#include "common.h" #include "struct_addr_union.h" -#include "resolv_conf_parser.h" +#include "resolv_conf_defs.h" void resolv_manager_init (NetworkInadorHandle *handle); void resolv_manager_clear_entries_by_prog (NetworkInadorHandle *handle, const int iface_index, const char *prog); +void resolv_manager_process_resolvconf_entries (NetworkInadorHandle *handle, ResolvConfEntry *entries, int num_entries); +//void resolv_manager_clear_tag_on_all (NetworkInadorHandle *handle); +//void resolv_manager_write (NetworkInadorHandle *handle); #endif /* __RESOLV_MANAGER_H__ */