From 95d48266151b1e28edd8d0aabf4f8e44254be594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Arreola=20Rodr=C3=ADguez?= Date: Fri, 21 Jul 2023 22:58:05 -0600 Subject: [PATCH] Agrego primeras pruebas para procesar el resolv.conf --- .gitignore | 1 + src/Makefile.am | 14 +- src/common.h | 17 +- src/dhcp_client.c | 16 + src/glist.c | 1258 ++++++++++++++++++++++++++++++++++ src/glist.h | 152 ++++ src/main.c | 4 + src/manager.c | 203 ++++++ src/network-inador-manager.h | 2 + src/ni-dhcp-iface-helper.c | 2 +- src/resolv_conf_defs.h | 69 ++ src/resolv_conf_helper.c | 258 +++++++ src/resolv_conf_parser.c | 180 +++++ src/resolv_conf_parser.h | 39 ++ src/resolv_manager.c | 137 ++++ src/resolv_manager.h | 33 + src/struct_addr_union.h | 35 + src/utils.c | 53 ++ src/utils.h | 36 + 19 files changed, 2502 insertions(+), 7 deletions(-) create mode 100644 src/glist.c create mode 100644 src/glist.h create mode 100644 src/resolv_conf_defs.h create mode 100644 src/resolv_conf_helper.c create mode 100644 src/resolv_conf_parser.c create mode 100644 src/resolv_conf_parser.h create mode 100644 src/resolv_manager.c create mode 100644 src/resolv_manager.h create mode 100644 src/struct_addr_union.h create mode 100644 src/utils.c create mode 100644 src/utils.h diff --git a/.gitignore b/.gitignore index f117102..063e57f 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,4 @@ client-gtk/ni-marshal.h client-gtk/core src/ni-dhcp-helper +src/resolvconf diff --git a/src/Makefile.am b/src/Makefile.am index 91ccb71..de70994 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,6 +3,7 @@ bin_PROGRAMS = network-inador network_inador_SOURCES = main.c \ common.h link-types.h \ + struct_addr_union.h resolv_conf_defs.h \ netlink-events.c netlink-events.h \ interfaces.c interfaces.h \ ip-address.c ip-address.h \ @@ -10,8 +11,19 @@ network_inador_SOURCES = main.c \ manager.c manager.h \ dhcp_client.c dhcp_client.h \ routes.c routes.h \ + resolv_manager.c resolv_manager.h \ + resolv_conf_parser.c resolv_conf_parser.h \ wireless_if.c wireless_if.h \ - wireless_bss.c wireless_bss.h + wireless_bss.c wireless_bss.h \ + utils.c utils.h + +sbin_PROGRAMS = resolvconf +resolvconf_SOURCES = resolv_conf_helper.c \ + struct_addr_union.h resolv_conf_defs.h \ + resolv_conf_parser.c resolv_conf_parser.h \ + utils.c utils.h \ + glist.c glist.h +resolvconf_CFLAGS = -DSTAND_ALONE_RESOLV_CONF_HELPER_BUILD libexec_PROGRAMS = ni-dhcp-helper diff --git a/src/common.h b/src/common.h index 6da7de6..410fbe9 100644 --- a/src/common.h +++ b/src/common.h @@ -29,10 +29,15 @@ #include #include #include +#include #include #include +#include "struct_addr_union.h" +#include "resolv_conf_defs.h" + + #ifndef FALSE #define FALSE 0 #endif @@ -53,11 +58,6 @@ typedef struct _NetworkInadorHandle NetworkInadorHandle; typedef struct _NetworkInadorManager NetworkInadorManager; typedef struct _Interface Interface; -typedef union { - struct in_addr v4; - struct in6_addr v6; -} struct_addr; - typedef struct _IPAddr { sa_family_t family; @@ -255,12 +255,19 @@ typedef struct _RouteTable { int for_delete, was_new; } RouteTable; +/* La definición principal que engloba todo */ struct _NetworkInadorHandle { GList *interfaces; GList *route_v4_tables; GList *route_v6_tables; GList *route_tables_names; + /* Entradas para el resolv conf */ + GList *resolver_entries; + int resolver_inotify_fd; + int resolver_inotify_watch; + + /* El manager */ NetworkInadorManager *manager; /* Estos sockets ejecutan comandos */ diff --git a/src/dhcp_client.c b/src/dhcp_client.c index bdfd32b..7233528 100644 --- a/src/dhcp_client.c +++ b/src/dhcp_client.c @@ -257,6 +257,8 @@ void interfaces_dhcp_client_internal_feed_from_client (NetworkInadorHandle *hand Interface *iface; struct in_addr empty_v4; int g; + struct_addr old_dns[7]; + int old_dns_c; iface = _interfaces_locate_by_index (handle->interfaces, index); @@ -298,6 +300,13 @@ void interfaces_dhcp_client_internal_feed_from_client (NetworkInadorHandle *hand printf ("----> DHCP status: %i\n", iface->dhcpc.dhcp_state); if (status == NET_INADOR_DHCP_STATUS_BOUND || status == NET_INADOR_DHCP_STATUS_RENEWED) { + /* Copiar la información de DNS para saber si hubo cambios */ + old_dns_c = iface->dhcpc.dns_c; + if (old_dns_c != dns_count) { + /* Copiamos los dns viejos solo si la cantidad es diferente */ + memcpy (old_dns, iface->dhcpc.dns, old_dns_c * (sizeof (iface->dhcpc.dns[0]))); + } + /* Copiar las variables de estado */ memset (&empty_v4, 0, sizeof (empty_v4)); @@ -318,8 +327,15 @@ void interfaces_dhcp_client_internal_feed_from_client (NetworkInadorHandle *hand memcpy (&iface->dhcpc.dns[g], &dns_list[g], 4); } + if (iface->dhcpc.dns_c != old_dns_c) { + /* TODO: Tenemos cambio en los DNS, actualizar el resolv.conf */ + } else if (memcmp (iface->dhcpc.dns, old_dns, dns_count * (sizeof (iface->dhcpc.dns[0]))) != 0) { + /* TODO: Tenemos cambio en los DNS, actualizar el resolv.conf */ + } + strncpy (iface->dhcpc.domain_name, domain_name, sizeof (iface->dhcpc.domain_name)); + /* Esto solo es para impresión */ char buf_a[256], buf_b[256], buf_c[256], buf_d[256]; inet_ntop (AF_INET, &iface->dhcpc.ip, buf_a, sizeof (buf_a)); diff --git a/src/glist.c b/src/glist.c new file mode 100644 index 0000000..f4d6eea --- /dev/null +++ b/src/glist.c @@ -0,0 +1,1258 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +/* + * MT safe + */ + +#include +#include + +#include "glist.h" + +/** + * SECTION:linked_lists_double + * @title: Doubly-Linked Lists + * @short_description: linked lists that can be iterated over in both directions + * + * The #GList structure and its associated functions provide a standard + * doubly-linked list data structure. + * + * Each element in the list contains a piece of data, together with + * pointers which link to the previous and next elements in the list. + * Using these pointers it is possible to move through the list in both + * directions (unlike the singly-linked [GSList][glib-Singly-Linked-Lists], + * which only allows movement through the list in the forward direction). + * + * The double linked list does not keep track of the number of items + * and does not keep track of both the start and end of the list. If + * you want fast access to both the start and the end of the list, + * and/or the number of items in the list, use a + * [GQueue][glib-Double-ended-Queues] instead. + * + * The data contained in each element can be either integer values, by + * using one of the [Type Conversion Macros][glib-Type-Conversion-Macros], + * or simply pointers to any type of data. + * + * List elements are allocated from the [slice allocator][glib-Memory-Slices], + * which is more efficient than allocating elements individually. + * + * Note that most of the #GList functions expect to be passed a pointer + * to the first element in the list. The functions which insert + * elements return the new start of the list, which may have changed. + * + * There is no function to create a #GList. %NULL is considered to be + * a valid, empty list so you simply set a #GList* to %NULL to initialize + * it. + * + * To add elements, use g_list_append(), g_list_prepend(), + * g_list_insert() and g_list_insert_sorted(). + * + * To visit all elements in the list, use a loop over the list: + * |[ + * GList *l; + * for (l = list; l != NULL; l = l->next) + * { + * // do something with l->data + * } + * ]| + * + * To call a function for each element in the list, use g_list_foreach(). + * + * To loop over the list and modify it (e.g. remove a certain element) + * a while loop is more appropriate, for example: + * |[ + * GList *l = list; + * while (l != NULL) + * { + * GList *next = l->next; + * if (should_be_removed (l)) + * { + * // possibly free l->data + * list = g_list_delete_link (list, l); + * } + * l = next; + * } + * ]| + * + * To remove elements, use g_list_remove(). + * + * To navigate in a list, use g_list_first(), g_list_last(), + * g_list_next(), g_list_previous(). + * + * To find elements in the list use g_list_nth(), g_list_nth_data(), + * g_list_find() and g_list_find_custom(). + * + * To find the index of an element use g_list_position() and + * g_list_index(). + * + * To free the entire list, use g_list_free() or g_list_free_full(). + */ + +/** + * GList: + * @data: holds the element's data, which can be a pointer to any kind + * of data, or any integer value using the + * [Type Conversion Macros][glib-Type-Conversion-Macros] + * @next: contains the link to the next element in the list + * @prev: contains the link to the previous element in the list + * + * The #GList struct is used for each element in a doubly-linked list. + **/ + +/** + * g_list_previous: + * @list: an element in a #GList + * + * A convenience macro to get the previous element in a #GList. + * Note that it is considered perfectly acceptable to access + * @list->previous directly. + * + * Returns: the previous element, or %NULL if there are no previous + * elements + **/ + +/** + * g_list_next: + * @list: an element in a #GList + * + * A convenience macro to get the next element in a #GList. + * Note that it is considered perfectly acceptable to access + * @list->next directly. + * + * Returns: the next element, or %NULL if there are no more elements + **/ + +#define _g_list_alloc() ((GList *) malloc (sizeof (GList))) +#define _g_list_alloc0() ((GList *) calloc (1, sizeof (GList))) +#define _g_list_free1(list) free (list) + +/** + * g_list_alloc: + * + * Allocates space for one #GList element. It is called by + * g_list_append(), g_list_prepend(), g_list_insert() and + * g_list_insert_sorted() and so is rarely used on its own. + * + * Returns: a pointer to the newly-allocated #GList element + **/ +GList * +g_list_alloc (void) +{ + return _g_list_alloc0 (); +} + +/** + * g_list_free: + * @list: a #GList + * + * Frees all of the memory used by a #GList. + * The freed elements are returned to the slice allocator. + * + * If list elements contain dynamically-allocated memory, you should + * either use g_list_free_full() or free them manually first. + */ +void +g_list_free (GList *list) +{ + GList *n; + + while (list != NULL) { + n = list->next; + + free (list); + list = n; + } +} + +/** + * g_list_free_1: + * @list: a #GList element + * + * Frees one #GList element, but does not update links from the next and + * previous elements in the list, so you should not call this function on an + * element that is currently part of a list. + * + * It is usually used after g_list_remove_link(). + */ +/** + * g_list_free1: + * + * Another name for g_list_free_1(). + **/ +void +g_list_free_1 (GList *list) +{ + _g_list_free1 (list); +} + +/** + * g_list_free_full: + * @list: a pointer to a #GList + * @free_func: the function to be called to free each element's data + * + * Convenience method, which frees all the memory used by a #GList, + * and calls @free_func on every element's data. + * + * Since: 2.28 + */ +void +g_list_free_full (GList *list, + GDestroyNotify free_func) +{ + g_list_foreach (list, (GFunc) free_func, NULL); + g_list_free (list); +} + +/** + * g_list_append: + * @list: a pointer to a #GList + * @data: the data for the new element + * + * Adds a new element on to the end of the list. + * + * Note that the return value is the new start of the list, + * if @list was empty; make sure you store the new value. + * + * g_list_append() has to traverse the entire list to find the end, + * which is inefficient when adding multiple elements. A common idiom + * to avoid the inefficiency is to use g_list_prepend() and reverse + * the list with g_list_reverse() when all elements have been added. + * + * |[ + * // Notice that these are initialized to the empty list. + * GList *string_list = NULL, *number_list = NULL; + * + * // This is a list of strings. + * string_list = g_list_append (string_list, "first"); + * string_list = g_list_append (string_list, "second"); + * + * // This is a list of integers. + * number_list = g_list_append (number_list, int_TO_POINTER (27)); + * number_list = g_list_append (number_list, int_TO_POINTER (14)); + * ]| + * + * Returns: either @list or the new start of the #GList if @list was %NULL + */ +GList * +g_list_append (GList *list, + gpointer data) +{ + GList *new_list; + GList *last; + + new_list = _g_list_alloc (); + new_list->data = data; + new_list->next = NULL; + + if (list) + { + last = g_list_last (list); + /* g_assert (last != NULL); */ + last->next = new_list; + new_list->prev = last; + + return list; + } + else + { + new_list->prev = NULL; + return new_list; + } +} + +/** + * g_list_prepend: + * @list: a pointer to a #GList, this must point to the top of the list + * @data: the data for the new element + * + * Prepends a new element on to the start of the list. + * + * Note that the return value is the new start of the list, + * which will have changed, so make sure you store the new value. + * + * |[ + * // Notice that it is initialized to the empty list. + * GList *list = NULL; + * + * list = g_list_prepend (list, "last"); + * list = g_list_prepend (list, "first"); + * ]| + * + * Do not use this function to prepend a new element to a different + * element than the start of the list. Use g_list_insert_before() instead. + * + * Returns: a pointer to the newly prepended element, which is the new + * start of the #GList + */ +GList * +g_list_prepend (GList *list, + gpointer data) +{ + GList *new_list; + + new_list = _g_list_alloc (); + new_list->data = data; + new_list->next = list; + + if (list) + { + new_list->prev = list->prev; + if (list->prev) + list->prev->next = new_list; + list->prev = new_list; + } + else + new_list->prev = NULL; + + return new_list; +} + +/** + * g_list_insert: + * @list: a pointer to a #GList, this must point to the top of the list + * @data: the data for the new element + * @position: the position to insert the element. If this is + * negative, or is larger than the number of elements in the + * list, the new element is added on to the end of the list. + * + * Inserts a new element into the list at the given position. + * + * Returns: the (possibly changed) start of the #GList + */ +GList * +g_list_insert (GList *list, + gpointer data, + int position) +{ + GList *new_list; + GList *tmp_list; + + if (position < 0) + return g_list_append (list, data); + else if (position == 0) + return g_list_prepend (list, data); + + tmp_list = g_list_nth (list, position); + if (!tmp_list) + return g_list_append (list, data); + + new_list = _g_list_alloc (); + new_list->data = data; + new_list->prev = tmp_list->prev; + tmp_list->prev->next = new_list; + new_list->next = tmp_list; + tmp_list->prev = new_list; + + return list; +} + +/** + * g_list_insert_before: + * @list: a pointer to a #GList, this must point to the top of the list + * @sibling: the list element before which the new element + * is inserted or %NULL to insert at the end of the list + * @data: the data for the new element + * + * Inserts a new element into the list before the given position. + * + * Returns: the (possibly changed) start of the #GList + */ +GList * +g_list_insert_before (GList *list, + GList *sibling, + gpointer data) +{ + if (!list) + { + list = g_list_alloc (); + list->data = data; + //if (sibling != NULL) return list; + //g_return_val_if_fail (sibling == NULL, list); + return list; + } + else if (sibling) + { + GList *node; + + node = _g_list_alloc (); + node->data = data; + node->prev = sibling->prev; + node->next = sibling; + sibling->prev = node; + if (node->prev) + { + node->prev->next = node; + return list; + } + else + { + //g_return_val_if_fail (sibling == list, node); + return node; + } + } + else + { + GList *last; + + last = list; + while (last->next) + last = last->next; + + last->next = _g_list_alloc (); + last->next->data = data; + last->next->prev = last; + last->next->next = NULL; + + return list; + } +} + +/** + * g_list_concat: + * @list1: a #GList, this must point to the top of the list + * @list2: the #GList to add to the end of the first #GList, + * this must point to the top of the list + * + * Adds the second #GList onto the end of the first #GList. + * Note that the elements of the second #GList are not copied. + * They are used directly. + * + * This function is for example used to move an element in the list. + * The following example moves an element to the top of the list: + * |[ + * list = g_list_remove_link (list, llink); + * list = g_list_concat (llink, list); + * ]| + * + * Returns: the start of the new #GList, which equals @list1 if not %NULL + */ +GList * +g_list_concat (GList *list1, + GList *list2) +{ + GList *tmp_list; + + if (list2) + { + tmp_list = g_list_last (list1); + if (tmp_list) + tmp_list->next = list2; + else + list1 = list2; + list2->prev = tmp_list; + } + + return list1; +} + +static inline GList * +_g_list_remove_link (GList *list, + GList *link) +{ + if (link == NULL) + return list; + + if (link->prev) + { + if (link->prev->next == link) + link->prev->next = link->next; + else + printf ("corrupted double-linked list detected"); + } + if (link->next) + { + if (link->next->prev == link) + link->next->prev = link->prev; + else + printf ("corrupted double-linked list detected"); + } + + if (link == list) + list = list->next; + + link->next = NULL; + link->prev = NULL; + + return list; +} + +/** + * g_list_remove: + * @list: a #GList, this must point to the top of the list + * @data: the data of the element to remove + * + * Removes an element from a #GList. + * If two elements contain the same data, only the first is removed. + * If none of the elements contain the data, the #GList is unchanged. + * + * Returns: the (possibly changed) start of the #GList + */ +GList * +g_list_remove (GList *list, + gconstpointer data) +{ + GList *tmp; + + tmp = list; + while (tmp) + { + if (tmp->data != data) + tmp = tmp->next; + else + { + list = _g_list_remove_link (list, tmp); + _g_list_free1 (tmp); + + break; + } + } + return list; +} + +/** + * g_list_remove_all: + * @list: a #GList, this must point to the top of the list + * @data: data to remove + * + * Removes all list nodes with data equal to @data. + * Returns the new head of the list. Contrast with + * g_list_remove() which removes only the first node + * matching the given data. + * + * Returns: the (possibly changed) start of the #GList + */ +GList * +g_list_remove_all (GList *list, + gconstpointer data) +{ + GList *tmp = list; + + while (tmp) + { + if (tmp->data != data) + tmp = tmp->next; + else + { + GList *next = tmp->next; + + if (tmp->prev) + tmp->prev->next = next; + else + list = next; + if (next) + next->prev = tmp->prev; + + _g_list_free1 (tmp); + tmp = next; + } + } + return list; +} + +/** + * g_list_remove_link: + * @list: a #GList, this must point to the top of the list + * @llink: an element in the #GList + * + * Removes an element from a #GList, without freeing the element. + * The removed element's prev and next links are set to %NULL, so + * that it becomes a self-contained list with one element. + * + * This function is for example used to move an element in the list + * (see the example for g_list_concat()) or to remove an element in + * the list before freeing its data: + * |[ + * list = g_list_remove_link (list, llink); + * free_some_data_that_may_access_the_list_again (llink->data); + * g_list_free (llink); + * ]| + * + * Returns: the (possibly changed) start of the #GList + */ +GList * +g_list_remove_link (GList *list, + GList *llink) +{ + return _g_list_remove_link (list, llink); +} + +/** + * g_list_delete_link: + * @list: a #GList, this must point to the top of the list + * @link_: node to delete from @list + * + * Removes the node link_ from the list and frees it. + * Compare this to g_list_remove_link() which removes the node + * without freeing it. + * + * Returns: the (possibly changed) start of the #GList + */ +GList * +g_list_delete_link (GList *list, + GList *link_) +{ + list = _g_list_remove_link (list, link_); + _g_list_free1 (link_); + + return list; +} + +/** + * g_list_copy: + * @list: a #GList, this must point to the top of the list + * + * Copies a #GList. + * + * Note that this is a "shallow" copy. If the list elements + * consist of pointers to data, the pointers are copied but + * the actual data is not. See g_list_copy_deep() if you need + * to copy the data as well. + * + * Returns: the start of the new list that holds the same data as @list + */ +GList * +g_list_copy (GList *list) +{ + return g_list_copy_deep (list, NULL, NULL); +} + +/** + * g_list_copy_deep: + * @list: a #GList, this must point to the top of the list + * @func: a copy function used to copy every element in the list + * @user_data: user data passed to the copy function @func, or %NULL + * + * Makes a full (deep) copy of a #GList. + * + * In contrast with g_list_copy(), this function uses @func to make + * a copy of each list element, in addition to copying the list + * container itself. + * + * @func, as a #GCopyFunc, takes two arguments, the data to be copied + * and a @user_data pointer. It's safe to pass %NULL as user_data, + * if the copy function takes only one argument. + * + * For instance, if @list holds a list of GObjects, you can do: + * |[ + * another_list = g_list_copy_deep (list, (GCopyFunc) g_object_ref, NULL); + * ]| + * + * And, to entirely free the new list, you could do: + * |[ + * g_list_free_full (another_list, g_object_unref); + * ]| + * + * Returns: the start of the new list that holds a full copy of @list, + * use g_list_free_full() to free it + * + * Since: 2.34 + */ +GList * +g_list_copy_deep (GList *list, + GCopyFunc func, + gpointer user_data) +{ + GList *new_list = NULL; + + if (list) + { + GList *last; + + new_list = _g_list_alloc (); + if (func) + new_list->data = func (list->data, user_data); + else + new_list->data = list->data; + new_list->prev = NULL; + last = new_list; + list = list->next; + while (list) + { + last->next = _g_list_alloc (); + last->next->prev = last; + last = last->next; + if (func) + last->data = func (list->data, user_data); + else + last->data = list->data; + list = list->next; + } + last->next = NULL; + } + + return new_list; +} + +/** + * g_list_reverse: + * @list: a #GList, this must point to the top of the list + * + * Reverses a #GList. + * It simply switches the next and prev pointers of each element. + * + * Returns: the start of the reversed #GList + */ +GList * +g_list_reverse (GList *list) +{ + GList *last; + + last = NULL; + while (list) + { + last = list; + list = last->next; + last->next = last->prev; + last->prev = list; + } + + return last; +} + +/** + * g_list_nth: + * @list: a #GList, this must point to the top of the list + * @n: the position of the element, counting from 0 + * + * Gets the element at the given position in a #GList. + * + * This iterates over the list until it reaches the @n-th position. If you + * intend to iterate over every element, it is better to use a for-loop as + * described in the #GList introduction. + * + * Returns: the element, or %NULL if the position is off + * the end of the #GList + */ +GList * +g_list_nth (GList *list, + unsigned int n) +{ + while ((n-- > 0) && list) + list = list->next; + + return list; +} + +/** + * g_list_nth_prev: + * @list: a #GList + * @n: the position of the element, counting from 0 + * + * Gets the element @n places before @list. + * + * Returns: the element, or %NULL if the position is + * off the end of the #GList + */ +GList * +g_list_nth_prev (GList *list, + unsigned int n) +{ + while ((n-- > 0) && list) + list = list->prev; + + return list; +} + +/** + * g_list_nth_data: + * @list: a #GList, this must point to the top of the list + * @n: the position of the element + * + * Gets the data of the element at the given position. + * + * This iterates over the list until it reaches the @n-th position. If you + * intend to iterate over every element, it is better to use a for-loop as + * described in the #GList introduction. + * + * Returns: the element's data, or %NULL if the position + * is off the end of the #GList + */ +gpointer +g_list_nth_data (GList *list, + unsigned int n) +{ + while ((n-- > 0) && list) + list = list->next; + + return list ? list->data : NULL; +} + +/** + * g_list_find: + * @list: a #GList, this must point to the top of the list + * @data: the element data to find + * + * Finds the element in a #GList which contains the given data. + * + * Returns: the found #GList element, or %NULL if it is not found + */ +GList * +g_list_find (GList *list, + gconstpointer data) +{ + while (list) + { + if (list->data == data) + break; + list = list->next; + } + + return list; +} + +/** + * g_list_find_custom: + * @list: a #GList, this must point to the top of the list + * @data: user data passed to the function + * @func: the function to call for each element. + * It should return 0 when the desired element is found + * + * Finds an element in a #GList, using a supplied function to + * find the desired element. It iterates over the list, calling + * the given function which should return 0 when the desired + * element is found. The function takes two #gconstpointer arguments, + * the #GList element's data as the first argument and the + * given user data. + * + * Returns: the found #GList element, or %NULL if it is not found + */ +GList * +g_list_find_custom (GList *list, + gconstpointer data, + GCompareFunc func) +{ + if (func == NULL) return list; + //g_return_val_if_fail (func != NULL, list); + + while (list) + { + if (! func (list->data, data)) + return list; + list = list->next; + } + + return NULL; +} + +/** + * g_list_position: + * @list: a #GList, this must point to the top of the list + * @llink: an element in the #GList + * + * Gets the position of the given element + * in the #GList (starting from 0). + * + * Returns: the position of the element in the #GList, + * or -1 if the element is not found + */ +int +g_list_position (GList *list, + GList *llink) +{ + int i; + + i = 0; + while (list) + { + if (list == llink) + return i; + i++; + list = list->next; + } + + return -1; +} + +/** + * g_list_index: + * @list: a #GList, this must point to the top of the list + * @data: the data to find + * + * Gets the position of the element containing + * the given data (starting from 0). + * + * Returns: the index of the element containing the data, + * or -1 if the data is not found + */ +int +g_list_index (GList *list, + gconstpointer data) +{ + int i; + + i = 0; + while (list) + { + if (list->data == data) + return i; + i++; + list = list->next; + } + + return -1; +} + +/** + * g_list_last: + * @list: any #GList element + * + * Gets the last element in a #GList. + * + * Returns: the last element in the #GList, + * or %NULL if the #GList has no elements + */ +GList * +g_list_last (GList *list) +{ + if (list) + { + while (list->next) + list = list->next; + } + + return list; +} + +/** + * g_list_first: + * @list: any #GList element + * + * Gets the first element in a #GList. + * + * Returns: the first element in the #GList, + * or %NULL if the #GList has no elements + */ +GList * +g_list_first (GList *list) +{ + if (list) + { + while (list->prev) + list = list->prev; + } + + return list; +} + +/** + * g_list_length: + * @list: a #GList, this must point to the top of the list + * + * Gets the number of elements in a #GList. + * + * This function iterates over the whole list to count its elements. + * Use a #GQueue instead of a GList if you regularly need the number + * of items. To check whether the list is non-empty, it is faster to check + * @list against %NULL. + * + * Returns: the number of elements in the #GList + */ +unsigned int +g_list_length (GList *list) +{ + unsigned int length; + + length = 0; + while (list) + { + length++; + list = list->next; + } + + return length; +} + +/** + * g_list_foreach: + * @list: a #GList, this must point to the top of the list + * @func: the function to call with each element's data + * @user_data: user data to pass to the function + * + * Calls a function for each element of a #GList. + */ +/** + * GFunc: + * @data: the element's data + * @user_data: user data passed to g_list_foreach() or g_slist_foreach() + * + * Specifies the type of functions passed to g_list_foreach() and + * g_slist_foreach(). + */ +void +g_list_foreach (GList *list, + GFunc func, + gpointer user_data) +{ + while (list) + { + GList *next = list->next; + (*func) (list->data, user_data); + list = next; + } +} + +static GList* +g_list_insert_sorted_real (GList *list, + gpointer data, + GFunc func, + gpointer user_data) +{ + GList *tmp_list = list; + GList *new_list; + int cmp; + + if (func == NULL) return list; + //g_return_val_if_fail (func != NULL, list); + + if (!list) + { + new_list = _g_list_alloc0 (); + new_list->data = data; + return new_list; + } + + cmp = ((GCompareDataFunc) func) (data, tmp_list->data, user_data); + + while ((tmp_list->next) && (cmp > 0)) + { + tmp_list = tmp_list->next; + + cmp = ((GCompareDataFunc) func) (data, tmp_list->data, user_data); + } + + new_list = _g_list_alloc0 (); + new_list->data = data; + + if ((!tmp_list->next) && (cmp > 0)) + { + tmp_list->next = new_list; + new_list->prev = tmp_list; + return list; + } + + if (tmp_list->prev) + { + tmp_list->prev->next = new_list; + new_list->prev = tmp_list->prev; + } + new_list->next = tmp_list; + tmp_list->prev = new_list; + + if (tmp_list == list) + return new_list; + else + return list; +} + +/** + * g_list_insert_sorted: + * @list: a pointer to a #GList, this must point to the top of the + * already sorted list + * @data: the data for the new element + * @func: the function to compare elements in the list. It should + * return a number > 0 if the first parameter comes after the + * second parameter in the sort order. + * + * Inserts a new element into the list, using the given comparison + * function to determine its position. + * + * If you are adding many new elements to a list, and the number of + * new elements is much larger than the length of the list, use + * g_list_prepend() to add the new items and sort the list afterwards + * with g_list_sort(). + * + * Returns: the (possibly changed) start of the #GList + */ +GList * +g_list_insert_sorted (GList *list, + gpointer data, + GCompareFunc func) +{ + return g_list_insert_sorted_real (list, data, (GFunc) func, NULL); +} + +/** + * g_list_insert_sorted_with_data: + * @list: a pointer to a #GList, this must point to the top of the + * already sorted list + * @data: the data for the new element + * @func: the function to compare elements in the list. It should + * return a number > 0 if the first parameter comes after the + * second parameter in the sort order. + * @user_data: user data to pass to comparison function + * + * Inserts a new element into the list, using the given comparison + * function to determine its position. + * + * If you are adding many new elements to a list, and the number of + * new elements is much larger than the length of the list, use + * g_list_prepend() to add the new items and sort the list afterwards + * with g_list_sort(). + * + * Returns: the (possibly changed) start of the #GList + * + * Since: 2.10 + */ +GList * +g_list_insert_sorted_with_data (GList *list, + gpointer data, + GCompareDataFunc func, + gpointer user_data) +{ + return g_list_insert_sorted_real (list, data, (GFunc) func, user_data); +} + +static GList * +g_list_sort_merge (GList *l1, + GList *l2, + GFunc compare_func, + gpointer user_data) +{ + GList list, *l, *lprev; + int cmp; + + l = &list; + lprev = NULL; + + while (l1 && l2) + { + cmp = ((GCompareDataFunc) compare_func) (l1->data, l2->data, user_data); + + if (cmp <= 0) + { + l->next = l1; + l1 = l1->next; + } + else + { + l->next = l2; + l2 = l2->next; + } + l = l->next; + l->prev = lprev; + lprev = l; + } + l->next = l1 ? l1 : l2; + l->next->prev = l; + + return list.next; +} + +static GList * +g_list_sort_real (GList *list, + GFunc compare_func, + gpointer user_data) +{ + GList *l1, *l2; + + if (!list) + return NULL; + if (!list->next) + return list; + + l1 = list; + l2 = list->next; + + while ((l2 = l2->next) != NULL) + { + if ((l2 = l2->next) == NULL) + break; + l1 = l1->next; + } + l2 = l1->next; + l1->next = NULL; + + return g_list_sort_merge (g_list_sort_real (list, compare_func, user_data), + g_list_sort_real (l2, compare_func, user_data), + compare_func, + user_data); +} + +/** + * g_list_sort: + * @list: a #GList, this must point to the top of the list + * @compare_func: the comparison function used to sort the #GList. + * This function is passed the data from 2 elements of the #GList + * and should return 0 if they are equal, a negative value if the + * first element comes before the second, or a positive value if + * the first element comes after the second. + * + * Sorts a #GList using the given comparison function. The algorithm + * used is a stable sort. + * + * Returns: the (possibly changed) start of the #GList + */ +/** + * GCompareFunc: + * @a: a value + * @b: a value to compare with + * + * Specifies the type of a comparison function used to compare two + * values. The function should return a negative integer if the first + * value comes before the second, 0 if they are equal, or a positive + * integer if the first value comes after the second. + * + * Returns: negative value if @a < @b; zero if @a = @b; positive + * value if @a > @b + */ +GList * +g_list_sort (GList *list, + GCompareFunc compare_func) +{ + return g_list_sort_real (list, (GFunc) compare_func, NULL); +} + +/** + * g_list_sort_with_data: + * @list: a #GList, this must point to the top of the list + * @compare_func: comparison function + * @user_data: user data to pass to comparison function + * + * Like g_list_sort(), but the comparison function accepts + * a user data argument. + * + * Returns: the (possibly changed) start of the #GList + */ +/** + * GCompareDataFunc: + * @a: a value + * @b: a value to compare with + * @user_data: user data + * + * Specifies the type of a comparison function used to compare two + * values. The function should return a negative integer if the first + * value comes before the second, 0 if they are equal, or a positive + * integer if the first value comes after the second. + * + * Returns: negative value if @a < @b; zero if @a = @b; positive + * value if @a > @b + */ +GList * +g_list_sort_with_data (GList *list, + GCompareDataFunc compare_func, + gpointer user_data) +{ + return g_list_sort_real (list, (GFunc) compare_func, user_data); +} diff --git a/src/glist.h b/src/glist.h new file mode 100644 index 0000000..7b08976 --- /dev/null +++ b/src/glist.h @@ -0,0 +1,152 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __G_LIST_H__ +#define __G_LIST_H__ + +#define G_GNUC_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) + +typedef void * gpointer; +typedef const void * gconstpointer; +typedef void (*GDestroyNotify) (gpointer data); +typedef void (*GFunc) (gpointer data, gpointer user_data); +typedef int (*GCompareDataFunc) (gconstpointer a, gconstpointer b, gpointer user_data); +typedef int (*GCompareFunc) (gconstpointer a, gconstpointer b); +typedef gpointer (*GCopyFunc) (gconstpointer src, gpointer data); + +typedef struct _GList GList; + +struct _GList +{ + gpointer data; + GList *next; + GList *prev; +}; + +/* Doubly linked lists + */ + +GList* g_list_alloc (void) G_GNUC_WARN_UNUSED_RESULT; + +void g_list_free (GList *list); + +void g_list_free_1 (GList *list); +#define g_list_free1 g_list_free_1 + +void g_list_free_full (GList *list, + GDestroyNotify free_func); + +GList* g_list_append (GList *list, + gpointer data) G_GNUC_WARN_UNUSED_RESULT; + +GList* g_list_prepend (GList *list, + gpointer data) G_GNUC_WARN_UNUSED_RESULT; + +GList* g_list_insert (GList *list, + gpointer data, + int position) G_GNUC_WARN_UNUSED_RESULT; + +GList* g_list_insert_sorted (GList *list, + gpointer data, + GCompareFunc func) G_GNUC_WARN_UNUSED_RESULT; + +GList* g_list_insert_sorted_with_data (GList *list, + gpointer data, + GCompareDataFunc func, + gpointer user_data) G_GNUC_WARN_UNUSED_RESULT; + +GList* g_list_insert_before (GList *list, + GList *sibling, + gpointer data) G_GNUC_WARN_UNUSED_RESULT; + +GList* g_list_concat (GList *list1, + GList *list2) G_GNUC_WARN_UNUSED_RESULT; + +GList* g_list_remove (GList *list, + gconstpointer data) G_GNUC_WARN_UNUSED_RESULT; + +GList* g_list_remove_all (GList *list, + gconstpointer data) G_GNUC_WARN_UNUSED_RESULT; + +GList* g_list_remove_link (GList *list, + GList *llink) G_GNUC_WARN_UNUSED_RESULT; + +GList* g_list_delete_link (GList *list, + GList *link_) G_GNUC_WARN_UNUSED_RESULT; + +GList* g_list_reverse (GList *list) G_GNUC_WARN_UNUSED_RESULT; + +GList* g_list_copy (GList *list) G_GNUC_WARN_UNUSED_RESULT; + + +GList* g_list_copy_deep (GList *list, + GCopyFunc func, + gpointer user_data) G_GNUC_WARN_UNUSED_RESULT; + + +GList* g_list_nth (GList *list, + unsigned int n); + +GList* g_list_nth_prev (GList *list, + unsigned int n); + +GList* g_list_find (GList *list, + gconstpointer data); + +GList* g_list_find_custom (GList *list, + gconstpointer data, + GCompareFunc func); + +int g_list_position (GList *list, + GList *llink); + +int g_list_index (GList *list, + gconstpointer data); + +GList* g_list_last (GList *list); + +GList* g_list_first (GList *list); + +unsigned int g_list_length (GList *list); + +void g_list_foreach (GList *list, + GFunc func, + gpointer user_data); + +GList* g_list_sort (GList *list, + GCompareFunc compare_func) G_GNUC_WARN_UNUSED_RESULT; + +GList* g_list_sort_with_data (GList *list, + GCompareDataFunc compare_func, + gpointer user_data) G_GNUC_WARN_UNUSED_RESULT; + +gpointer g_list_nth_data (GList *list, + unsigned int n); + + +#define g_list_previous(list) ((list) ? (((GList *)(list))->prev) : NULL) +#define g_list_next(list) ((list) ? (((GList *)(list))->next) : NULL) + + +#endif /* __G_LIST_H__ */ diff --git a/src/main.c b/src/main.c index a33ad67..27bda6c 100644 --- a/src/main.c +++ b/src/main.c @@ -46,6 +46,7 @@ #include "bridge.h" #include "dhcp_client.h" #include "routes.h" +#include "resolv_manager.h" /* Usados para salir en caso de una señal */ static int sigterm_pipe_fds[2] = { -1, -1 }; @@ -148,6 +149,9 @@ int main (int argc, char *argv[]) { /* Inicializar las rutas */ routes_init (&handle); + /* Inicializar el resolv.conf */ + resolv_manager_init (&handle); + manager_init (&handle); g_main_loop_run (loop); diff --git a/src/manager.c b/src/manager.c index af33c4c..06a6b28 100644 --- a/src/manager.c +++ b/src/manager.c @@ -46,6 +46,8 @@ #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" @@ -818,6 +820,201 @@ static void _manager_execute_dhcp_client_feed (ManagerClientInfo *manager_client _manager_send_executed (manager_client); } +static void _manager_execute_resolvconf_feed (ManagerClientInfo *manager_client, unsigned char *buffer, int buffer_len) { + 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; + + int ns_count; + int search_count; + int g, h, pos; + int family; + int order = 0; + struct_addr direccion; + int iface_index; + Interface *iface; + + if (buffer_len < 3) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_RESOLVCONF_FEED); + return; + } + + name_len = buffer[2]; + + if (buffer_len < 3 + name_len || name_len == 0) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_RESOLVCONF_FEED); + return; + } + + memcpy (iface_prog, &buffer[3], name_len); + iface_prog[name_len] = 0; + + if (iface_prog[0] == '.') { + /* No permitimos interfaces vacías */ + _manager_send_error (manager_client, NET_INADOR_ERROR_INVALID_VALUE, NET_INADOR_COMMAND_RESOLVCONF_FEED); + + return; + } + + point = strchr (iface_prog, '.'); + if (point == NULL) { + prog[0] = 0; + strncpy (iface_name, iface_prog, sizeof (iface_name)); + } else { + point[0] = 0; + strncpy (iface_name, iface_prog, sizeof (iface_name)); + strncpy (prog, &point[1], sizeof (prog)); + } + + iface = _interfaces_locate_by_name (manager_client->manager->handle->interfaces, iface_name); + if (iface != NULL) { + iface_index = iface->index; + } else { + iface_index = 0; + } + + pos = 3 + name_len; + if (buffer_len < pos) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_RESOLVCONF_FEED); + return; + } + + /* Revisar la cantidad de nameservers */ + ns_count = buffer[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 */ + if (buffer_len < pos + 1) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_RESOLVCONF_FEED); + return; + } + + family = buffer[pos]; + pos++; /* El byte de la familia */ + + if (family != AF_INET && family != AF_INET6) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INVALID_FAMILY, NET_INADOR_COMMAND_RESOLVCONF_FEED); + + return; + } + + if (family == AF_INET) { + wanted = 4; + } else if (family == AF_INET6) { + wanted = 16; + } + + if (buffer_len < pos + wanted) { + _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); + } + } + + /* Ahora crear y parsear */ + + /* OK */ + _manager_send_executed (manager_client); +} + +static void _manager_execute_resolvconf_remove (ManagerClientInfo *manager_client, unsigned char *buffer, int buffer_len) { + int name_len; + unsigned char iface_prog[256]; + unsigned char iface_name[256], prog[256]; + char *point; + Interface *iface; + int iface_index; + + name_len = buffer[2]; + + if (buffer_len < 3 + name_len || name_len == 0) { + _manager_send_error (manager_client, NET_INADOR_ERROR_INCOMPLETE_REQUEST, NET_INADOR_COMMAND_RESOLVCONF_REMOVE); + return; + } + + memcpy (iface_prog, &buffer[3], name_len); + iface_prog[name_len] = 0; + + if (iface_prog[0] == '.') { + /* No permitimos interfaces vacías */ + _manager_send_error (manager_client, NET_INADOR_ERROR_INVALID_VALUE, NET_INADOR_COMMAND_RESOLVCONF_REMOVE); + + return; + } + + point = strchr (iface_prog, '.'); + if (point == NULL) { + prog[0] = 0; + strncpy (iface_name, iface_prog, sizeof (iface_name)); + } else { + point[0] = 0; + strncpy (iface_name, iface_prog, sizeof (iface_name)); + strncpy (prog, &point[1], sizeof (prog)); + } + + iface = _interfaces_locate_by_name (manager_client->manager->handle->interfaces, iface_name); + if (iface != NULL) { + iface_index = iface->index; + } else { + iface_index = 0; + } + + resolv_manager_clear_entries_by_prog (manager_client->manager->handle, iface_index, prog); + + _manager_send_executed (manager_client); +} + static void _manager_execute_dhcp_run (ManagerClientInfo *manager_client, unsigned char *buffer, int buffer_len) { Interface *iface; int ret; @@ -1493,6 +1690,12 @@ static gboolean _manager_client_data (GIOChannel *source, GIOCondition condition case NET_INADOR_COMMAND_LIST_ROUTE_TABLES: _manager_send_list_route_tables (manager_client, buffer, bytes); break; + case NET_INADOR_COMMAND_RESOLVCONF_FEED: + _manager_execute_resolvconf_feed (manager_client, buffer, bytes); + break; + case NET_INADOR_COMMAND_RESOLVCONF_REMOVE: + _manager_execute_resolvconf_remove (manager_client, buffer, bytes); + break; default: _manager_send_error (manager_client, NET_INADOR_ERROR_WRONG_COMMAND, command); } diff --git a/src/network-inador-manager.h b/src/network-inador-manager.h index d57da0a..ffd4cb7 100644 --- a/src/network-inador-manager.h +++ b/src/network-inador-manager.h @@ -51,6 +51,8 @@ enum { /* Los siguientes comandos son para uso interno */ NET_INADOR_COMMAND_DHCP_CLIENT_FEED = 224, + NET_INADOR_COMMAND_RESOLVCONF_FEED = 225, + NET_INADOR_COMMAND_RESOLVCONF_REMOVE = 226, }; enum { diff --git a/src/ni-dhcp-iface-helper.c b/src/ni-dhcp-iface-helper.c index 49f92eb..3af5837 100644 --- a/src/ni-dhcp-iface-helper.c +++ b/src/ni-dhcp-iface-helper.c @@ -1,5 +1,5 @@ /* - * ni-iface-helper.c + * ni-dhcp-iface-helper.c * This file is part of Network Inador * * Copyright (C) 2021 - Félix Arreola Rodríguez diff --git a/src/resolv_conf_defs.h b/src/resolv_conf_defs.h new file mode 100644 index 0000000..8beeaaa --- /dev/null +++ b/src/resolv_conf_defs.h @@ -0,0 +1,69 @@ +/* + * resolv_conf_defs.h + * This file is part of NetworkInador + * + * Copyright (C) 2023 - Félix Arreola Rodríguez + * + * NetworkInador 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. + * + * NetworkInador 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 NetworkInador; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef __RESOLV_CONF_DEFS_H__ +#define __RESOLV_CONF_DEFS_H__ + +#include + +#include "struct_addr_union.h" + +/* Cosas del resolv.conf */ +enum { + RESOLV_TYPE_NAMESERVER, + RESOLV_TYPE_DOMAIN, + RESOLV_TYPE_SEARCH, + RESOLV_TYPE_SORTLIST, + RESOLV_TYPE_OPTIONS, + + NUM_RESOLV_TYPES +}; + +enum { + RESOLV_ORIGIN_FILE, + RESOLV_ORIGIN_DHCP, + RESOLV_ORIGIN_RESOLVCONF, + + RESOLV_ORIGIN_SLAAC_RDNSS, + + NUM_RESOLV_ORIGINS +}; + +typedef struct _ResolvConfEntry { + int resolv_type; + int origin; + + int file_order; + int is_on_file; + int priority; + + int ns_family; + struct_addr nameserver; + char value[2048]; + + //char owner_interface[IFNAMSIZ]; + int owner_interface_index; + char owner_prog[256]; +} ResolvConfEntry; + +#endif /* __RESOLV_CONF_DEFS_H__ */ + diff --git a/src/resolv_conf_helper.c b/src/resolv_conf_helper.c new file mode 100644 index 0000000..a3c00aa --- /dev/null +++ b/src/resolv_conf_helper.c @@ -0,0 +1,258 @@ +/* + * resolv_conf_helper.c + * This file is part of NetworkInador + * + * Copyright (C) 2023 - Félix Arreola Rodríguez + * + * NetworkInador 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. + * + * NetworkInador 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 NetworkInador; 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 +#include +#include + +#include "glist.h" + +#include "network-inador-manager.h" +#include "resolv_conf_defs.h" +#include "struct_addr_union.h" +#include "resolv_conf_parser.h" + +#define COMMAND_SOCKET_PATH "/tmp/network-inador.socket" + +static const struct option long_options[] = { + {"enable-updates", 0, NULL, 'z' }, + {"disable-updates", 0, NULL, 'y' }, + {"updates-are-enabled", 0, NULL, 'x' }, + {"create-runtime-directories", 0, NULL, 'w' }, + {"wipe-runtime-directories", 0, NULL, 'v' }, + {NULL, 0, NULL, 0} +}; + +const char *short_options = "a:d:u"; + +int wait_for_ack_or_error (int s) { + unsigned char buffer[128]; + int ret; + + ret = recv (s, buffer, sizeof (buffer), 0); + + if (ret < 3) { + return -1; + } + + if (buffer[0] == NET_INADOR_TYPE_RESPONSE_ERROR) { + return -1; + } + + if (buffer[0] == NET_INADOR_TYPE_RESPONSE && buffer[1] == NET_INADOR_RESPONSE_EXECUTED) { + return 0; + } + + return -1; +} + +void send_entries (int s, GList *entries, const char *iface_prog) { + char buffer[2048]; + int pos, len, pos_name_count; + GList *g; + int n; + ResolvConfEntry *entry; + + buffer[0] = NET_INADOR_TYPE_COMMAND; + buffer[1] = NET_INADOR_COMMAND_RESOLVCONF_FEED; + + /* El segundo byte es el nombre seguido del nombre en sí */ + len = strlen (iface_prog); + buffer[2] = len; + + memcpy (&buffer[3], iface_prog, len); + + pos_name_count = 3 + len; + + /* Contar los nameservers que tengo */ + g = entries; + n = 0; + pos = pos_name_count + 1; + while (g != NULL) { + entry = (ResolvConfEntry *) g->data; + + if (entry->resolv_type == RESOLV_TYPE_NAMESERVER) { + n++; + buffer[pos] = entry->ns_family; + pos++; + if (entry->ns_family == AF_INET) { + memcpy (&buffer[pos], &entry->nameserver, 4); + pos += 4; + } else if (entry->ns_family == AF_INET6) { + memcpy (&buffer[pos], &entry->nameserver, 16); + pos += 16; + } + } + + g = g->next; + } + + buffer[pos_name_count] = n; + + send (s, buffer, pos, 0); + + if (wait_for_ack_or_error (s) < 0) { + return; + } +} + +void send_remove_entries (int s, const char *iface_prog) { + char buffer[2048]; + int pos, len; + + buffer[0] = NET_INADOR_TYPE_COMMAND; + buffer[1] = NET_INADOR_COMMAND_RESOLVCONF_REMOVE; + + /* El segundo byte es el nombre seguido del nombre en sí */ + len = strlen (iface_prog); + buffer[2] = len; + + memcpy (&buffer[3], iface_prog, len); + + send (s, buffer, 3 + len, 0); + + if (wait_for_ack_or_error (s) < 0) { + return; + } +} + +int main (int argc, char *argv[]) { + int next_option; + const char *iface_prog = NULL; + int action = 0; + GList *entries; + int s, ret; + struct sockaddr_un path_dest; + + do { + next_option = getopt_long (argc, argv, short_options, long_options, NULL); + + switch (next_option) { + case 'z': + case 'y': + case 'x': + case 'w': + case 'v': + /* Ignorar */ + break; + case 'a': + action = 'a'; + iface_prog = optarg; + break; + case 'd': + action = 'd'; + iface_prog = optarg; + break; + case 'u': + break; + case '?': + /* Opción incorrecta */ + break; + case -1: + /* Fin de opciones */ + break; + } + } while (next_option != -1); + + if (action == 0) { + return 0; + } + + /* Validar el nombre del argumento del -a o del -d */ + if (iface_prog == NULL || iface_prog[0] == 0) { + fprintf (stderr, "interface record name empty\n"); + return -1; + } + + if (strchr (iface_prog, '/') != NULL) { + /* Diagonal no permitida */ + fprintf (stderr, "Slash not allowed in interface record name\n"); + return -1; + } + + if (strchr (iface_prog, ' ') != NULL) { + fprintf (stderr, "Space not allowed in interface record name\n"); + return -1; + } + + if (iface_prog[0] == '.') { + fprintf (stderr, "Space not allowed in interface record name\n"); + return -1; + } + + if (iface_prog[0] == '-') { + fprintf (stderr, "Initial hyphen not allowed in interface record name\n"); + return -1; + } + + if (iface_prog[0] == '~') { + fprintf (stderr, "Initial tilde not allowed in interface record name\n"); + return -1; + } + + if (strlen (iface_prog) > 255) { + fprintf (stderr, "Interface too long\n"); + return -1; + } + + /* Intentar abrir el socket al network inador */ + s = socket (AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); + + if (s < 0) { + return 1; + } + + path_dest.sun_family = AF_UNIX; + strncpy (path_dest.sun_path, COMMAND_SOCKET_PATH, sizeof (path_dest.sun_path)); + ret = connect (s, (struct sockaddr *) &path_dest, sizeof (path_dest)); + + if (ret < 0) { + perror ("Connect"); + close (s); + return 1; + } + + if (action == 'a') { + /* Parsear el archivo primero */ + entries = resolv_parser_parse_file (NULL, stdin, RESOLV_ORIGIN_RESOLVCONF); + + /* Preparar un paquete de datos para enviar via el socket */ + send_entries (s, entries, iface_prog); + } else if (action == 'd') { + send_remove_entries (s, iface_prog); + } + + close (s); + + return 0; +} + diff --git a/src/resolv_conf_parser.c b/src/resolv_conf_parser.c new file mode 100644 index 0000000..0efcae2 --- /dev/null +++ b/src/resolv_conf_parser.c @@ -0,0 +1,180 @@ +/* + * resolv_conf_parser.c + * This file is part of NetworkInador + * + * Copyright (C) 2023 - Félix Arreola Rodríguez + * + * NetworkInador 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. + * + * NetworkInador 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 NetworkInador; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +#include "resolv_conf_defs.h" +#include "resolv_conf_parser.h" +#include "utils.h" +#ifdef STAND_ALONE_RESOLV_CONF_HELPER_BUILD +#include "glist.h" +#else +#include "common.h" +#endif + +static const char *resolv_conf_keywords[NUM_RESOLV_TYPES] = { + "nameserver", + "domain", + "search", + "sortlist", + "options" +}; + +int resolv_conf_parse_line (const char *line, int *ns_family, struct_addr *nameserver, char *value, const size_t value_size) { + int g, len; + int af; + struct_addr temp_addr; + int ret; + + /* Recorrer cada palabra clave posible de los resolv.conf */ + for (g = 0; g < NUM_RESOLV_TYPES; g++) { + len = strlen (resolv_conf_keywords[g]); + if (strncmp (line, resolv_conf_keywords[g], len) == 0 && line[len] == ' ') { + if (g == RESOLV_TYPE_NAMESERVER) { + /* Los nameserver solo pueden ser direcciones IP de IPv4 o IPv6 */ + af = AF_INET; + ret = inet_pton (AF_INET, &line[len + 1], &temp_addr); + if (ret == 0) { + af = AF_INET6; + ret = inet_pton (AF_INET6, &line[len + 1], &temp_addr); + } + + if (ret == 0) { + /* No es una dirección IP, ignorar */ + break; + } + } else { + /* Revisar por posibles restricciones en otros tipos */ + } + + strncpy (value, &line[len + 1], value_size); + value[value_size - 1] = 0; + + if (g == RESOLV_TYPE_NAMESERVER) { + *ns_family = af; + memcpy (nameserver, &temp_addr, sizeof (temp_addr)); + } else { + *ns_family = AF_UNSPEC; + memset (nameserver, 0, sizeof (temp_addr)); + } + + return g; + } + } + + /* No es alguna linea válida */ + 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 *g; + ResolvConfEntry *entry; + + g = all_entries; + while (g != NULL) { + entry = (ResolvConfEntry *) g->data; + + if (resolv_type != entry->resolv_type) { + /* Pasar a la siguiente entrada, no es del mismo tipo */ + g = g->next; + continue; + } + + /* Comparar dependiendo del tipo */ + if (resolv_type == RESOLV_TYPE_NAMESERVER) { + if (ns_family == AF_INET && entry->ns_family == AF_INET) { + if (memcmp (nameserver, &entry->nameserver, sizeof (struct in_addr)) == 0) { + /* Es la entrada que andamos buscando */ + return g; + } + } else if (ns_family == AF_INET6 && entry->ns_family == AF_INET6) { + if (memcmp (nameserver, &entry->nameserver, sizeof (struct in6_addr)) == 0) { + return g; + } + } + } else { + /* TODO: Revisar si las otras entradas requieren otro tipo de comparación */ + if (strcmp (value, entry->value) == 0) { + return g; + } + } + + g = g->next; + } + + return NULL; +} + +GList * resolv_parser_parse_file (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; + } + /* 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); + + if (pos != NULL) { + /* Ya existe, refrescar que está en el archivo */ + entry = (ResolvConfEntry *) pos->data; + } else { + /* Como no existe, crear y 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->is_on_file = 1; + entry->file_order = order++; + } + + return all_entries; +} + diff --git a/src/resolv_conf_parser.h b/src/resolv_conf_parser.h new file mode 100644 index 0000000..e01b43a --- /dev/null +++ b/src/resolv_conf_parser.h @@ -0,0 +1,39 @@ +/* + * resolv_conf_parser.h + * This file is part of NetworkInador + * + * Copyright (C) 2023 - Félix Arreola Rodríguez + * + * NetworkInador 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. + * + * NetworkInador 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 NetworkInador; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef __RESOLV_CONF_PARSER_H__ +#define __RESOLV_CONF_PARSER_H__ + +#include +#include + +#ifdef STAND_ALONE_RESOLV_CONF_HELPER_BUILD +#include "glist.h" +#else +#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); + +#endif /* __RESOLV_CONF_PARSER_H__ */ + diff --git a/src/resolv_manager.c b/src/resolv_manager.c new file mode 100644 index 0000000..54ba3fe --- /dev/null +++ b/src/resolv_manager.c @@ -0,0 +1,137 @@ +/* + * 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 "common.h" +#include "utils.h" +#include "resolv_conf_parser.h" + +void resolv_manager_clear_entries_by_prog (NetworkInadorHandle *handle, const int iface_index, const char *prog) { + GList *g, *next; + ResolvConfEntry *entry; + + 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; + continue; + } + + if (entry->owner_interface_index != iface_index) { + 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 = g_list_delete_link (handle->resolver_entries, g); + } + + g = next; + } +} + +void resolv_manager_read_local_etc_resolv (NetworkInadorHandle *handle) { + FILE *fd; + GList *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 */ + /* 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; + } + + handle->resolver_entries = resolv_parser_parse_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->is_on_file == 0) { + /* Esta entrada se va, la eliminaron */ + printf ("/// La entrada %s del resolv.conf fué eliminada del archivo. Purgando nosotros.\n", entry->value); + + 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; + } +} + +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); + + 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 new file mode 100644 index 0000000..35a2cf8 --- /dev/null +++ b/src/resolv_manager.h @@ -0,0 +1,33 @@ +/* + * resolv_manager.h + * This file is part of Network-inador + * + * Copyright (C) 2023 - 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 + */ + +#ifndef __RESOLV_MANAGER_H__ +#define __RESOLV_MANAGER_H__ + +#include "struct_addr_union.h" +#include "resolv_conf_parser.h" + +void resolv_manager_init (NetworkInadorHandle *handle); +void resolv_manager_clear_entries_by_prog (NetworkInadorHandle *handle, const int iface_index, const char *prog); + +#endif /* __RESOLV_MANAGER_H__ */ + diff --git a/src/struct_addr_union.h b/src/struct_addr_union.h new file mode 100644 index 0000000..80ba8eb --- /dev/null +++ b/src/struct_addr_union.h @@ -0,0 +1,35 @@ +/* + * struct_addr_union.h + * This file is part of NetworkInador + * + * Copyright (C) 2023 - Félix Arreola Rodríguez + * + * NetworkInador 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. + * + * NetworkInador 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 NetworkInador; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef __STRUCT_ADDR_UNION_H__ +#define __STRUCT_ADDR_UNION_H__ + +#include +#include + +typedef union { + struct in_addr v4; + struct in6_addr v6; +} struct_addr; + +#endif /* __STRUCT_ADDR_UNION_H__ */ + diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..7676f22 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,53 @@ +/* + * utils.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 "utils.h" + +void utils_trim (char * s) { + char * p = s; + int l = strlen(p); + + while(isspace(p[l - 1])) p[--l] = 0; + while(* p && isspace(* p)) ++p, --l; + + memmove(s, p, l + 1); +} + +void utils_trim_comment (char *s) { + char *p = s; + int l = strlen (p); + + while (*p != 0 && *p != '#') { + ++p; + } + + if (*p == '#') { + *p = 0; + } +} + diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..00a52a4 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,36 @@ +/* + * utils.h + * 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 + */ + +#ifndef __UTILS_H__ +#define __UTILS_H__ + +#include +#include +#include + +#include + +void utils_trim (char * s); +void utils_trim_comment (char *s); + +#endif /* __UTILS_H__ */ +