/* This file is part of GNUnet. Copyright (C) 2013 GNUnet e.V. GNUnet 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 3, or (at your option) any later version. GNUnet 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 GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** * @file src/setup/gnunet-setup-exit-services.c * @brief code for the dialog to configure EXIT records * @author Christian Grothoff */ #include "gnunet-setup.h" #include #include /** * Columns in the hosted service model. */ enum ServiceModelColumns { /** * A gchararray */ SERVICE_MC_SERVICE_NAME = 0, /** * A gboolean */ SERVICE_MC_ISUDP = 1, /** * A guint */ SERVICE_MC_VISIBLE_PORT = 2, /** * Destination adddress, a gchararray */ SERVICE_MC_DESTINATION_ADDRESS = 3 }; /** * Check if the section represents a hosted service and then update * the GtkListStore accordingly. * * @param cls the list store to modify * @param section name of the section */ static void add_name_entry_to_list_store (void *cls, const char *section) { GtkListStore *ls = cls; GtkTreeIter iter; char *sld; char *destination; char *redirect; char *cpy; gboolean udp; guint local_port; if (NULL == section) { gtk_list_store_insert_with_values (ls, &iter, G_MAXINT, SERVICE_MC_SERVICE_NAME, "", SERVICE_MC_ISUDP, FALSE, SERVICE_MC_VISIBLE_PORT, (guint) 80, SERVICE_MC_DESTINATION_ADDRESS, "169.254.86.1:8080", -1); return; } if ((8 > strlen (section)) || (0 != strcmp (".gnunet.", section + ((strlen (section) - 8))))) return; sld = GNUNET_strdup (section); sld[strlen (section) - 8] = '\0'; udp = FALSE; do { if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, section, (udp) ? "UDP_REDIRECTS" : "TCP_REDIRECTS", &cpy)) { for (redirect = strtok (cpy, " "); NULL != redirect; redirect = strtok (NULL, " ")) { if (NULL == (destination = strstr (redirect, ":"))) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Option `%s' is not formatted correctly!\n"), redirect); continue; } destination[0] = '\0'; destination++; local_port = atoi (redirect); if (! ((local_port > 0) && (local_port < 65536))) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("`%s' is not a valid port number!\n"), redirect); continue; } gtk_list_store_insert_with_values (ls, &iter, 0, SERVICE_MC_SERVICE_NAME, sld, SERVICE_MC_ISUDP, udp, SERVICE_MC_VISIBLE_PORT, local_port, SERVICE_MC_DESTINATION_ADDRESS, destination, -1); } GNUNET_free (cpy); } udp = ! udp; } while (udp); GNUNET_free (sld); } /** * Initialize the GtkListModel with the hosted service specification. * * @param cls NULL * @param section section with the value (NULL) * @param option option name (NULL) * @param value value as a string (NULL) * @param widget widget to initialize (the GtkTreeView) * @param cfg configuration handle * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem */ int load_hosted_service_configuration ( const void *cls, const char *section, const char *option, const char *value, GObject *widget, const struct GNUNET_CONFIGURATION_Handle *cfg) { GtkTreeView *tv; GtkListStore *ls; tv = GTK_TREE_VIEW (widget); if (NULL == tv) { GNUNET_break (0); return GNUNET_SYSERR; } ls = GTK_LIST_STORE (gtk_tree_view_get_model (tv)); GNUNET_CONFIGURATION_iterate_sections (cfg, &add_name_entry_to_list_store, ls); /* finally, add empty entry */ add_name_entry_to_list_store (ls, NULL); return GNUNET_OK; } /** * Records we use to build DNS information lists. */ struct HostedServiceInfo { /** * We keep these in a singly-linked list. */ struct HostedServiceInfo *next; /** * Name of the section in the configuration (must end in ".gnunet."). */ char *section; /** * String describing all TCP redirects. */ char *tcpred; /** * String describing all UDP redirects. */ char *udpred; }; /** * Function called for each section in the configuration. * Gather existing ttl, section names and altnames. * * @param cls 'struct HostedServiceInfo**' to create * @param section name of a section in the configuration */ static void collect_hosted_service_sections (void *cls, const char *section) { struct HostedServiceInfo **base = cls; struct HostedServiceInfo *pos; if ((8 > strlen (section)) || (0 != strcmp (".gnunet.", section + ((strlen (section) - 8))))) return; pos = GNUNET_new (struct HostedServiceInfo); pos->section = GNUNET_strdup (section); pos->tcpred = GNUNET_strdup (""); pos->udpred = GNUNET_strdup (""); pos->next = *base; *base = pos; } /** * Function called for each section in the configuration. * Removes those ending in '.gnunet.'. * * @param cls unused * @param section name of a section in the configuration */ static void remove_hosted_service_sections (void *cls, const char *section) { if ((8 > strlen (section)) || (0 != strcmp (".gnunet.", section + ((strlen (section) - 8))))) return; GNUNET_CONFIGURATION_remove_section (cfg, section); } /** * Given the list store and the data in it, update the * configuration file accordingly. * * @param tm model to use */ static void update_hosted_service_configuration (GtkTreeModel *tm) { GtkTreeIter iter; gchar *name; guint srcport; gchar *targetaddress; gboolean is_udp; char *tmp; struct HostedServiceInfo *head; struct HostedServiceInfo *pos; head = NULL; GNUNET_CONFIGURATION_iterate_sections (cfg, &collect_hosted_service_sections, &head); if (gtk_tree_model_get_iter_first (tm, &iter)) do { gtk_tree_model_get (tm, &iter, SERVICE_MC_SERVICE_NAME, &name, SERVICE_MC_ISUDP, &is_udp, SERVICE_MC_VISIBLE_PORT, &srcport, SERVICE_MC_DESTINATION_ADDRESS, &targetaddress, -1); if (0 != strlen (name)) { pos = head; GNUNET_asprintf (&tmp, "%s.gnunet.", name); while ((NULL != pos) && (0 != strcasecmp (tmp, pos->section))) pos = pos->next; if (NULL == pos) { pos = GNUNET_new (struct HostedServiceInfo); pos->section = tmp; pos->tcpred = GNUNET_strdup (""); pos->udpred = GNUNET_strdup (""); pos->next = head; head = pos; } else { GNUNET_free (tmp); } GNUNET_asprintf (&tmp, "%u:%s %s", srcport, targetaddress, (is_udp) ? pos->udpred : pos->tcpred); if (is_udp) { GNUNET_free (pos->udpred); pos->udpred = tmp; } else { GNUNET_free (pos->tcpred); pos->tcpred = tmp; } } g_free (name); g_free (targetaddress); } while (TRUE == gtk_tree_model_iter_next (tm, &iter)); GNUNET_CONFIGURATION_iterate_sections (cfg, &remove_hosted_service_sections, NULL); while (NULL != head) { pos = head; head = pos->next; if (strlen (pos->udpred) > 0) GNUNET_CONFIGURATION_set_value_string (cfg, pos->section, "UDP_REDIRECTS", pos->udpred); if (strlen (pos->tcpred) > 0) GNUNET_CONFIGURATION_set_value_string (cfg, pos->section, "TCP_REDIRECTS", pos->tcpred); GNUNET_free (pos->tcpred); GNUNET_free (pos->udpred); GNUNET_free (pos->section); GNUNET_free (pos); } } /** * The user has edited the DNS name of a service we're offering. * Update the GtkTreeModel (at the given path) and update the * respective service entry in the configuration file. Finally, * if the edited path is for a "fresh" entry, create another empty * one at the bottom. If the hostname was set to empty, remove * the entire entry from the configuration and the model. * * @param renderer GtkCellRendererText that changed * @param path GtkTreePath identifying where in the Model the change is * @param new_text the new text that was stored in the line * @param user_data NULL */ static void save_hosted_service_name (GtkCellRendererText *renderer, gchar *path, gchar *new_text, gpointer user_data) { GtkTreeModel *tm; GtkListStore *ls; GtkTreeIter iter; gchar *old; tm = GTK_TREE_MODEL ( GNUNET_SETUP_get_object ("GNUNET_setup_hosted_service_liststore")); if (NULL == tm) { GNUNET_break (0); return; } ls = GTK_LIST_STORE (tm); if (NULL == ls) { GNUNET_break (0); return; } if (TRUE != gtk_tree_model_get_iter_from_string (tm, &iter, path)) { GNUNET_break (0); return; } gtk_tree_model_get (tm, &iter, SERVICE_MC_SERVICE_NAME, &old, -1); if ((0 != strlen (old)) && (0 == strlen (new_text))) { /* deletion */ gtk_list_store_remove (ls, &iter); g_free (old); /* update configuration */ update_hosted_service_configuration (tm); return; } /* update model */ gtk_list_store_set (ls, &iter, SERVICE_MC_SERVICE_NAME, new_text, -1); /* update configuration */ update_hosted_service_configuration (tm); if ((0 == strlen (old)) && (0 != strlen (new_text))) { /* need another empty entry at the end for future expansion */ add_name_entry_to_list_store (GTK_LIST_STORE (tm), NULL); } g_free (old); } /** * Initialize the GtkListModel with the VPN's DNS service specification. * * @param cls NULL * @param section section with the value (NULL) * @param option option name (NULL) * @param widget widget to initialize (the GtkTreeView) * @param cfg configuration handle * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem */ int hosted_service_name_install_edited_handler ( const void *cls, const char *section, const char *option, GObject *widget, struct GNUNET_CONFIGURATION_Handle *cfg) { static int once; GtkCellRendererText *rt; rt = GTK_CELL_RENDERER_TEXT (widget); if (NULL == rt) return GNUNET_SYSERR; if (0 != once++) return GNUNET_OK; g_signal_connect (rt, "edited", G_CALLBACK (&save_hosted_service_name), NULL); return GNUNET_OK; } /** * The user has edited the DNS name of a service we're offering. * Update the GtkTreeModel (at the given path) and update the * respective service entry in the configuration file. Finally, * if the edited path is for a "fresh" entry, create another empty * one at the bottom. If the hostname was set to empty, remove * the entire entry from the configuration and the model. * * @param renderer GtkCellRendererToggle that changed * @param path GtkTreePath identifying where in the Model the change is * @param user_data NULL */ static void save_hosted_service_is_udp (GtkCellRendererToggle *renderer, gchar *path, gpointer user_data) { GtkTreeModel *tm; GtkListStore *ls; GtkTreeIter iter; gboolean is_udp; tm = GTK_TREE_MODEL ( GNUNET_SETUP_get_object ("GNUNET_setup_hosted_service_liststore")); if (NULL == tm) { GNUNET_break (0); return; } ls = GTK_LIST_STORE (tm); if (NULL == ls) { GNUNET_break (0); return; } if (TRUE != gtk_tree_model_get_iter_from_string (tm, &iter, path)) { GNUNET_break (0); return; } /* update model */ gtk_tree_model_get (tm, &iter, SERVICE_MC_ISUDP, &is_udp, -1); gtk_list_store_set (ls, &iter, SERVICE_MC_ISUDP, ! is_udp, -1); /* update configuration */ update_hosted_service_configuration (tm); } /** * Initialize the GtkListModel with the VPN's DNS service specification. * * @param cls NULL * @param section section with the value (NULL) * @param option option name (NULL) * @param widget widget to initialize (the GtkTreeView) * @param cfg configuration handle * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem */ int hosted_service_is_udp_install_toggled_handler ( const void *cls, const char *section, const char *option, GObject *widget, struct GNUNET_CONFIGURATION_Handle *cfg) { static int once; GtkCellRendererToggle *rt; rt = GTK_CELL_RENDERER_TOGGLE (widget); if (NULL == rt) return GNUNET_SYSERR; if (0 != once++) return GNUNET_OK; g_signal_connect (rt, "toggled", G_CALLBACK (&save_hosted_service_is_udp), NULL); return GNUNET_OK; } /** * The user has edited the DNS name of a service we're offering. * Update the GtkTreeModel (at the given path) and update the * respective service entry in the configuration file. Finally, * if the edited path is for a "fresh" entry, create another empty * one at the bottom. If the hostname was set to empty, remove * the entire entry from the configuration and the model. * * @param renderer GtkCellRendererText that changed * @param path GtkTreePath identifying where in the Model the change is * @param new_text the new text that was stored in the line * @param user_data NULL */ static void save_hosted_service_visible_port (GtkCellRendererText *renderer, gchar *path, gchar *new_text, gpointer user_data) { GtkTreeModel *tm; GtkListStore *ls; GtkTreeIter iter; int port; if ((1 != sscanf (new_text, "%d", &port)) || (port < 1) || (port > UINT16_MAX)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Illegal value `%s' for port\n"), new_text); return; } tm = GTK_TREE_MODEL ( GNUNET_SETUP_get_object ("GNUNET_setup_hosted_service_liststore")); if (NULL == tm) { GNUNET_break (0); return; } ls = GTK_LIST_STORE (tm); if (NULL == ls) { GNUNET_break (0); return; } if (TRUE != gtk_tree_model_get_iter_from_string (tm, &iter, path)) { GNUNET_break (0); return; } /* update model */ gtk_list_store_set (ls, &iter, SERVICE_MC_VISIBLE_PORT, port, -1); /* update configuration */ update_hosted_service_configuration (tm); } /** * Initialize the GtkListModel with the VPN's DNS service specification. * * @param cls NULL * @param section section with the value (NULL) * @param option option name (NULL) * @param widget widget to initialize (the GtkTreeView) * @param cfg configuration handle * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem */ int hosted_service_visible_port_install_edited_handler ( const void *cls, const char *section, const char *option, GObject *widget, struct GNUNET_CONFIGURATION_Handle *cfg) { static int once; GtkCellRendererText *rt; rt = GTK_CELL_RENDERER_TEXT (widget); if (NULL == rt) return GNUNET_SYSERR; if (0 != once++) return GNUNET_OK; g_signal_connect (rt, "edited", G_CALLBACK (&save_hosted_service_visible_port), NULL); return GNUNET_OK; } /** * The user has edited the DNS name of a service we're offering. * Update the GtkTreeModel (at the given path) and update the * respective service entry in the configuration file. Finally, * if the edited path is for a "fresh" entry, create another empty * one at the bottom. If the hostname was set to empty, remove * the entire entry from the configuration and the model. * * @param renderer GtkCellRendererText that changed * @param path GtkTreePath identifying where in the Model the change is * @param new_text the new text that was stored in the line * @param user_data NULL */ static void save_hosted_service_destination (GtkCellRendererText *renderer, gchar *path, gchar *new_text, gpointer user_data) { GtkTreeModel *tm; GtkListStore *ls; GtkTreeIter iter; struct sockaddr_in v4; struct sockaddr_in6 v6; if (('[' != new_text[0]) && (GNUNET_OK != GNUNET_STRINGS_to_address_ipv4 (new_text, strlen (new_text), &v4))) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Illegal IPv4 destination address `%s'\n"), new_text); return; } if (('[' == new_text[0]) && (GNUNET_OK != GNUNET_STRINGS_to_address_ipv6 (new_text, strlen (new_text), &v6))) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Illegal IPv6 destination address `%s'\n"), new_text); return; } tm = GTK_TREE_MODEL ( GNUNET_SETUP_get_object ("GNUNET_setup_hosted_service_liststore")); if (NULL == tm) { GNUNET_break (0); return; } ls = GTK_LIST_STORE (tm); if (NULL == ls) { GNUNET_break (0); return; } if (TRUE != gtk_tree_model_get_iter_from_string (tm, &iter, path)) { GNUNET_break (0); return; } /* update model */ gtk_list_store_set (ls, &iter, SERVICE_MC_DESTINATION_ADDRESS, new_text, -1); /* update configuration */ update_hosted_service_configuration (tm); } /** * Initialize the GtkListModel with the VPN's DNS service specification. * * @param cls NULL * @param section section with the value (NULL) * @param option option name (NULL) * @param widget widget to initialize (the GtkTreeView) * @param cfg configuration handle * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem */ int hosted_service_destination_install_edited_handler ( const void *cls, const char *section, const char *option, GObject *widget, struct GNUNET_CONFIGURATION_Handle *cfg) { static int once; GtkCellRendererText *rt; rt = GTK_CELL_RENDERER_TEXT (widget); if (NULL == rt) return GNUNET_SYSERR; if (0 != once++) return GNUNET_OK; g_signal_connect (rt, "edited", G_CALLBACK (&save_hosted_service_destination), NULL); return GNUNET_OK; } /* end of gnunet-setup-dns-exit.c */