X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fconfig.c;h=56ca2b8b18f94a790f45d2afff0fd9b48238f48f;hb=515ec5f2286e75d8a607d88fad3909a692a7e0dc;hp=f22d5178833155fa4f3d78299dc70285ecb6d940;hpb=a70b4abc989121e287a16bb26b2f17c6f2b6e47e;p=platform%2Fupstream%2Fconnman.git diff --git a/src/config.c b/src/config.c index f22d517..56ca2b8 100644 --- a/src/config.c +++ b/src/config.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007-2009 Intel Corporation. All rights reserved. + * Copyright (C) 2007-2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -23,17 +23,865 @@ #include #endif +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include #include "connman.h" +struct connman_config_service { + char *ident; + char *name; + char *type; + void *ssid; + unsigned int ssid_len; + char *eap; + char *identity; + char *anonymous_identity; + char *ca_cert_file; + char *subject_match; + char *altsubject_match; + char *domain_suffix_match; + char *domain_match; + char *client_cert_file; + char *private_key_file; + char *private_key_passphrase; + char *private_key_passphrase_type; + char *phase2; + char *passphrase; + enum connman_service_security security; + GSList *service_identifiers; + char *config_ident; /* file prefix */ + char *config_entry; /* entry name */ + bool hidden; + bool virtual; + char *virtual_file; + char *ipv4_address; + char *ipv4_netmask; + char *ipv4_gateway; + char *ipv6_address; + unsigned char ipv6_prefix_length; + char *ipv6_gateway; + char *ipv6_privacy; + char *mac; + bool mdns; + char **nameservers; + char **search_domains; + char **timeservers; + char *domain_name; +#if defined TIZEN_EXT + char *connector; + char *c_sign_key; + char *net_access_key; +#endif +}; + struct connman_config { char *ident; char *name; char *description; + GHashTable *service_table; +}; + +static GHashTable *config_table = NULL; + +static bool cleanup = false; + +/* Definition of possible strings in the .config files */ +#define CONFIG_KEY_NAME "Name" +#define CONFIG_KEY_DESC "Description" + +#define SERVICE_KEY_TYPE "Type" +#define SERVICE_KEY_NAME "Name" +#define SERVICE_KEY_SSID "SSID" +#define SERVICE_KEY_EAP "EAP" +#define SERVICE_KEY_CA_CERT "CACertFile" +#define SERVICE_KEY_CL_CERT "ClientCertFile" +#define SERVICE_KEY_PRV_KEY "PrivateKeyFile" +#define SERVICE_KEY_PRV_KEY_PASS "PrivateKeyPassphrase" +#define SERVICE_KEY_PRV_KEY_PASS_TYPE "PrivateKeyPassphraseType" +#define SERVICE_KEY_IDENTITY "Identity" +#define SERVICE_KEY_ANONYMOUS_IDENTITY "AnonymousIdentity" +#define SERVICE_KEY_SUBJECT_MATCH "SubjectMatch" +#define SERVICE_KEY_ALT_SUBJECT_MATCH "AltSubjectMatch" +#define SERVICE_KEY_DOMAIN_SUFF_MATCH "DomainSuffixMatch" +#define SERVICE_KEY_DOMAIN_MATCH "DomainMatch" +#define SERVICE_KEY_PHASE2 "Phase2" +#define SERVICE_KEY_PASSPHRASE "Passphrase" +#define SERVICE_KEY_SECURITY "Security" +#define SERVICE_KEY_HIDDEN "Hidden" +#define SERVICE_KEY_MDNS "mDNS" + +#define SERVICE_KEY_IPv4 "IPv4" +#define SERVICE_KEY_IPv6 "IPv6" +#define SERVICE_KEY_IPv6_PRIVACY "IPv6.Privacy" +#define SERVICE_KEY_MAC "MAC" +#define SERVICE_KEY_NAMESERVERS "Nameservers" +#define SERVICE_KEY_SEARCH_DOMAINS "SearchDomains" +#define SERVICE_KEY_TIMESERVERS "Timeservers" +#define SERVICE_KEY_DOMAIN "Domain" +#if defined TIZEN_EXT +#define SERVICE_KEY_CONNECTOR "Connector" +#define SERVICE_KEY_C_SIGN_KEY "CSignKey" +#define SERVICE_KEY_NET_ACCESS_KEY "NetAccessKey" +#endif + +static const char *config_possible_keys[] = { + CONFIG_KEY_NAME, + CONFIG_KEY_DESC, + NULL, +}; + +static const char *service_possible_keys[] = { + SERVICE_KEY_TYPE, + SERVICE_KEY_NAME, + SERVICE_KEY_SSID, + SERVICE_KEY_EAP, + SERVICE_KEY_CA_CERT, + SERVICE_KEY_CL_CERT, + SERVICE_KEY_PRV_KEY, + SERVICE_KEY_PRV_KEY_PASS, + SERVICE_KEY_PRV_KEY_PASS_TYPE, + SERVICE_KEY_IDENTITY, + SERVICE_KEY_ANONYMOUS_IDENTITY, + SERVICE_KEY_SUBJECT_MATCH, + SERVICE_KEY_ALT_SUBJECT_MATCH, + SERVICE_KEY_DOMAIN_SUFF_MATCH, + SERVICE_KEY_DOMAIN_MATCH, + SERVICE_KEY_PHASE2, + SERVICE_KEY_PASSPHRASE, + SERVICE_KEY_SECURITY, + SERVICE_KEY_HIDDEN, + SERVICE_KEY_IPv4, + SERVICE_KEY_IPv6, + SERVICE_KEY_IPv6_PRIVACY, + SERVICE_KEY_MAC, + SERVICE_KEY_MDNS, + SERVICE_KEY_NAMESERVERS, + SERVICE_KEY_SEARCH_DOMAINS, + SERVICE_KEY_TIMESERVERS, + SERVICE_KEY_DOMAIN, +#if defined TIZEN_EXT + SERVICE_KEY_CONNECTOR, + SERVICE_KEY_C_SIGN_KEY, + SERVICE_KEY_NET_ACCESS_KEY, +#endif + NULL, }; -static GHashTable *config_hash = NULL; +static void unregister_config(gpointer data) +{ + struct connman_config *config = data; + + connman_info("Removing configuration %s", config->ident); + + g_hash_table_destroy(config->service_table); + + g_free(config->description); + g_free(config->name); + g_free(config->ident); + g_free(config); +} + +static void unregister_service(gpointer data) +{ + struct connman_config_service *config_service = data; + struct connman_service *service; + char *service_id; + GSList *list; + + if (cleanup) + goto free_only; + + connman_info("Removing service configuration %s", + config_service->ident); + + if (config_service->virtual) + goto free_only; + + for (list = config_service->service_identifiers; list; + list = list->next) { + service_id = list->data; + + service = connman_service_lookup_from_identifier(service_id); + if (service) { + __connman_service_set_immutable(service, false); + __connman_service_set_config(service, NULL, NULL); + __connman_service_remove(service); + + /* + * Ethernet or gadget service cannot be removed by + * __connman_service_remove() so reset the ipconfig + * here. + */ + if (connman_service_get_type(service) == + CONNMAN_SERVICE_TYPE_ETHERNET || + connman_service_get_type(service) == + CONNMAN_SERVICE_TYPE_GADGET) { + __connman_service_disconnect(service); + __connman_service_reset_ipconfig(service, + CONNMAN_IPCONFIG_TYPE_IPV4, NULL, NULL); + __connman_service_reset_ipconfig(service, + CONNMAN_IPCONFIG_TYPE_IPV6, NULL, NULL); + __connman_service_set_ignore(service, true); + + /* + * After these operations, user needs to + * reconnect ethernet cable to get IP + * address. + */ + } + } + + if (!__connman_storage_remove_service(service_id)) + DBG("Could not remove all files for service %s", + service_id); + } + +free_only: + g_free(config_service->ident); + g_free(config_service->type); + g_free(config_service->name); + g_free(config_service->ssid); + g_free(config_service->eap); + g_free(config_service->identity); + g_free(config_service->anonymous_identity); + g_free(config_service->ca_cert_file); + g_free(config_service->subject_match); + g_free(config_service->altsubject_match); + g_free(config_service->domain_suffix_match); + g_free(config_service->domain_match); + g_free(config_service->client_cert_file); + g_free(config_service->private_key_file); + g_free(config_service->private_key_passphrase); + g_free(config_service->private_key_passphrase_type); + g_free(config_service->phase2); + g_free(config_service->passphrase); + g_free(config_service->ipv4_address); + g_free(config_service->ipv4_gateway); + g_free(config_service->ipv4_netmask); + g_free(config_service->ipv6_address); + g_free(config_service->ipv6_gateway); + g_free(config_service->ipv6_privacy); + g_free(config_service->mac); + g_strfreev(config_service->nameservers); + g_strfreev(config_service->search_domains); + g_strfreev(config_service->timeservers); + g_free(config_service->domain_name); + g_slist_free_full(config_service->service_identifiers, g_free); + g_free(config_service->config_ident); + g_free(config_service->config_entry); + g_free(config_service->virtual_file); +#if defined TIZEN_EXT + g_free(config_service->connector); + g_free(config_service->c_sign_key); + g_free(config_service->net_access_key); +#endif + g_free(config_service); +} + +static void check_keys(GKeyFile *keyfile, const char *group, + const char **possible_keys) +{ + char **avail_keys; + gsize nb_avail_keys, i, j; + + avail_keys = g_key_file_get_keys(keyfile, group, &nb_avail_keys, NULL); + if (!avail_keys) + return; + + /* + * For each key in the configuration file, + * verify it is understood by connman + */ + for (i = 0 ; i < nb_avail_keys; i++) { + for (j = 0; possible_keys[j] ; j++) + if (g_strcmp0(avail_keys[i], possible_keys[j]) == 0) + break; + + if (!possible_keys[j]) + connman_warn("Unknown configuration key %s in [%s]", + avail_keys[i], group); + } + + g_strfreev(avail_keys); +} + +static int check_family(const char *address, int expected_family) +{ + int family; + int err = 0; + + family = connman_inet_check_ipaddress(address); + if (family < 0) { + DBG("Cannot get address family of %s (%d/%s)", address, + family, gai_strerror(family)); + err = -EINVAL; + goto out; + } + + switch (family) { + case AF_INET: + if (expected_family != AF_INET) { + DBG("Wrong type address %s, expecting IPv4", address); + err = -EINVAL; + goto out; + } + break; + case AF_INET6: + if (expected_family != AF_INET6) { + DBG("Wrong type address %s, expecting IPv6", address); + err = -EINVAL; + goto out; + } + break; + default: + DBG("Unsupported address family %d", family); + err = -EINVAL; + goto out; + } + +out: + return err; +} + +static int parse_address(const char *address_str, int address_family, + char **address, char **netmask, char **gateway) +{ + char *addr_str, *mask_str, *gw_str; + int err = 0; + char **route; + + route = g_strsplit(address_str, "/", 0); + if (!route) + return -EINVAL; + + addr_str = route[0]; + if (!addr_str || addr_str[0] == '\0') { + err = -EINVAL; + goto out; + } + + if ((err = check_family(addr_str, address_family)) < 0) + goto out; + + mask_str = route[1]; + if (!mask_str || mask_str[0] == '\0') { + err = -EINVAL; + goto out; + } + + gw_str = route[2]; + if (gw_str && gw_str[0]) { + if ((err = check_family(gw_str, address_family)) < 0) + goto out; + } + + g_free(*address); + *address = g_strdup(addr_str); + + g_free(*netmask); + *netmask = g_strdup(mask_str); + + g_free(*gateway); + *gateway = g_strdup(gw_str); + + if (*gateway) + DBG("address %s/%s via %s", *address, *netmask, *gateway); + else + DBG("address %s/%s", *address, *netmask); + +out: + g_strfreev(route); + + return err; +} + +static bool check_address(char *address_str, char **address) +{ + if (g_ascii_strcasecmp(address_str, "auto") == 0 || + g_ascii_strcasecmp(address_str, "dhcp") == 0 || + g_ascii_strcasecmp(address_str, "off") == 0) { + *address = address_str; + return false; + } + + return true; +} + +static bool load_service_generic(GKeyFile *keyfile, + const char *group, struct connman_config *config, + struct connman_config_service *service) +{ + char *str, *mask; + char **strlist; + gsize length; + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_IPv4, NULL); + if (str && check_address(str, &service->ipv4_address)) { + mask = NULL; + + if (parse_address(str, AF_INET, &service->ipv4_address, + &mask, &service->ipv4_gateway) < 0) { + connman_warn("Invalid format for IPv4 address %s", + str); + g_free(str); + goto err; + } + + if (!g_strrstr(mask, ".")) { + /* We have netmask length */ + in_addr_t addr; + struct in_addr netmask_in; + unsigned char prefix_len = 32; + char *ptr; + long int value = strtol(mask, &ptr, 10); + + if (ptr != mask && *ptr == '\0' && value && value <= 32) + prefix_len = value; + + addr = 0xffffffff << (32 - prefix_len); + netmask_in.s_addr = htonl(addr); + service->ipv4_netmask = + g_strdup(inet_ntoa(netmask_in)); + + g_free(mask); + } else + service->ipv4_netmask = mask; + + g_free(str); + } + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_IPv6, NULL); + if (str && check_address(str, &service->ipv6_address)) { + long int value; + char *ptr; + + mask = NULL; + + if (parse_address(str, AF_INET6, &service->ipv6_address, + &mask, &service->ipv6_gateway) < 0) { + connman_warn("Invalid format for IPv6 address %s", + str); + g_free(str); + goto err; + } + + value = strtol(mask, &ptr, 10); + if (ptr != mask && *ptr == '\0' && value <= 128) + service->ipv6_prefix_length = value; + else + service->ipv6_prefix_length = 128; + + g_free(mask); + g_free(str); + } + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_IPv6_PRIVACY, + NULL); + if (str) { + g_free(service->ipv6_privacy); + service->ipv6_privacy = str; + } + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_MAC, NULL); + if (str) { + g_free(service->mac); + service->mac = str; + } + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_DOMAIN, NULL); + if (str) { + g_free(service->domain_name); + service->domain_name = str; + } + + strlist = __connman_config_get_string_list(keyfile, group, + SERVICE_KEY_NAMESERVERS, + &length, NULL); + if (strlist) { + if (length != 0) { + g_strfreev(service->nameservers); + service->nameservers = strlist; + } else + g_strfreev(strlist); + } + + strlist = __connman_config_get_string_list(keyfile, group, + SERVICE_KEY_SEARCH_DOMAINS, + &length, NULL); + if (strlist) { + if (length != 0) { + g_strfreev(service->search_domains); + service->search_domains = strlist; + } else + g_strfreev(strlist); + } + + strlist = __connman_config_get_string_list(keyfile, group, + SERVICE_KEY_TIMESERVERS, + &length, NULL); + if (strlist) { + if (length != 0) { + g_strfreev(service->timeservers); + service->timeservers = strlist; + } else + g_strfreev(strlist); + } + + service->mdns = __connman_config_get_bool(keyfile, group, + SERVICE_KEY_MDNS, NULL); + + return true; + +err: + g_free(service->ident); + g_free(service->type); + g_free(service->ipv4_address); + g_free(service->ipv4_netmask); + g_free(service->ipv4_gateway); + g_free(service->ipv6_address); + g_free(service->ipv6_gateway); + g_free(service->mac); + g_free(service); + + return false; +} + +static bool load_service(GKeyFile *keyfile, const char *group, + struct connman_config *config) +{ + struct connman_config_service *service; + const char *ident; + char *str, *hex_ssid; + enum connman_service_security security; + bool service_created = false; + + /* Strip off "service_" prefix */ + ident = group + 8; + + if (strlen(ident) < 1) + return false; + + /* Verify that provided keys are good */ + check_keys(keyfile, group, service_possible_keys); + + service = g_hash_table_lookup(config->service_table, ident); + if (!service) { + service = g_try_new0(struct connman_config_service, 1); + if (!service) + return false; + + service->ident = g_strdup(ident); + + service_created = true; + } + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_TYPE, NULL); + if (str) { + g_free(service->type); + service->type = str; + } else { + connman_warn("Type of the configured service is missing " + "for group %s", group); + goto err; + } + + if (!load_service_generic(keyfile, group, config, service)) + return false; + + if (g_strcmp0(str, "ethernet") == 0) { + service->config_ident = g_strdup(config->ident); + service->config_entry = g_strdup_printf("service_%s", + service->ident); + + g_hash_table_insert(config->service_table, service->ident, + service); + return true; + } + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_NAME, NULL); + if (str) { + g_free(service->name); + service->name = str; + } + + hex_ssid = __connman_config_get_string(keyfile, group, SERVICE_KEY_SSID, + NULL); + if (hex_ssid) { + char *ssid; + unsigned int i, j = 0, hex; + size_t hex_ssid_len = strlen(hex_ssid); + + ssid = g_try_malloc0(hex_ssid_len / 2); + if (!ssid) { + g_free(hex_ssid); + goto err; + } + + for (i = 0; i < hex_ssid_len; i += 2) { + if (sscanf(hex_ssid + i, "%02x", &hex) <= 0) { + connman_warn("Invalid SSID %s", hex_ssid); + g_free(ssid); + g_free(hex_ssid); + goto err; + } + ssid[j++] = hex; + } + + g_free(hex_ssid); + + g_free(service->ssid); + service->ssid = ssid; + service->ssid_len = hex_ssid_len / 2; + } else if (service->name) { + char *ssid; + unsigned int ssid_len; + + ssid_len = strlen(service->name); +#if defined TIZEN_EXT + ssid = g_try_malloc0(ssid_len + 1); +#else + ssid = g_try_malloc0(ssid_len); +#endif + if (!ssid) + goto err; + + memcpy(ssid, service->name, ssid_len); + g_free(service->ssid); + service->ssid = ssid; + service->ssid_len = ssid_len; + } + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_EAP, NULL); + if (str) { + g_free(service->eap); + service->eap = str; + } + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_CA_CERT, NULL); + if (str) { + g_free(service->ca_cert_file); + service->ca_cert_file = str; + } + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_CL_CERT, NULL); + if (str) { + g_free(service->client_cert_file); + service->client_cert_file = str; + } + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_PRV_KEY, NULL); + if (str) { + g_free(service->private_key_file); + service->private_key_file = str; + } + + str = __connman_config_get_string(keyfile, group, + SERVICE_KEY_PRV_KEY_PASS, NULL); + if (str) { + g_free(service->private_key_passphrase); + service->private_key_passphrase = str; + } + + str = __connman_config_get_string(keyfile, group, + SERVICE_KEY_PRV_KEY_PASS_TYPE, NULL); + if (str) { + g_free(service->private_key_passphrase_type); + service->private_key_passphrase_type = str; + } + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_IDENTITY, NULL); + if (str) { + g_free(service->identity); + service->identity = str; + } + + str = __connman_config_get_string(keyfile, group, + SERVICE_KEY_ANONYMOUS_IDENTITY, NULL); + if (str) { + g_free(service->anonymous_identity); + service->anonymous_identity = str; + } + + str = __connman_config_get_string(keyfile, group, + SERVICE_KEY_SUBJECT_MATCH, NULL); + if (str) { + g_free(service->subject_match); + service->subject_match = str; + } + + str = __connman_config_get_string(keyfile, group, + SERVICE_KEY_ALT_SUBJECT_MATCH, NULL); + if (str) { + g_free(service->altsubject_match); + service->altsubject_match = str; + } + + str = __connman_config_get_string(keyfile, group, + SERVICE_KEY_DOMAIN_SUFF_MATCH, NULL); + if (str) { + g_free(service->domain_suffix_match); + service->domain_suffix_match = str; + } + + str = __connman_config_get_string(keyfile, group, + SERVICE_KEY_DOMAIN_MATCH, NULL); + if (str) { + g_free(service->domain_match); + service->domain_match = str; + } + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_PHASE2, NULL); + if (str) { + g_free(service->phase2); + service->phase2 = str; + } + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_PASSPHRASE, + NULL); + if (str) { + g_free(service->passphrase); + service->passphrase = str; + } +#if defined TIZEN_EXT + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_CONNECTOR, + NULL); + if (str) { + g_free(service->connector); + service->connector = str; + } + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_C_SIGN_KEY, + NULL); + if (str) { + g_free(service->c_sign_key); + service->c_sign_key = str; + } + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_NET_ACCESS_KEY, + NULL); + if (str) { + g_free(service->net_access_key); + service->net_access_key = str; + } +#endif + + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_SECURITY, + NULL); + security = __connman_service_string2security(str); + + if (service->eap) { + + if (str && security != CONNMAN_SERVICE_SECURITY_8021X) + connman_info("Mismatch between EAP configuration and " + "setting %s = %s", + SERVICE_KEY_SECURITY, str); + + service->security = CONNMAN_SERVICE_SECURITY_8021X; + + } else if (service->passphrase) { + + if (str) { + if (security == CONNMAN_SERVICE_SECURITY_PSK || +#if defined TIZEN_EXT + security == CONNMAN_SERVICE_SECURITY_RSN || + security == CONNMAN_SERVICE_SECURITY_SAE || +#endif + security == CONNMAN_SERVICE_SECURITY_WEP) { + service->security = security; + } else { + connman_info("Mismatch with passphrase and " + "setting %s = %s", + SERVICE_KEY_SECURITY, str); + + service->security = + CONNMAN_SERVICE_SECURITY_PSK; + } + + } else + service->security = CONNMAN_SERVICE_SECURITY_PSK; +#if defined TIZEN_EXT + } else if (service->connector) { + + if (str && security != CONNMAN_SERVICE_SECURITY_DPP) + connman_info("Mismatch between DPP configuration and " + "setting %s = %s", + SERVICE_KEY_SECURITY, str); + + service->security = CONNMAN_SERVICE_SECURITY_DPP; +#endif + } else if (str) { + + if (security != CONNMAN_SERVICE_SECURITY_NONE) + connman_info("Mismatch no security and " + "setting %s = %s", + SERVICE_KEY_SECURITY, str); + + service->security = CONNMAN_SERVICE_SECURITY_NONE; + } else + service->security = CONNMAN_SERVICE_SECURITY_NONE; + + g_free(str); + + service->config_ident = g_strdup(config->ident); + service->config_entry = g_strdup_printf("service_%s", service->ident); + + service->hidden = __connman_config_get_bool(keyfile, group, + SERVICE_KEY_HIDDEN, NULL); + + if (service_created) + g_hash_table_insert(config->service_table, service->ident, + service); + + connman_info("Adding service configuration %s", service->ident); + + return true; + +err: + if (service_created) { + g_free(service->ident); + g_free(service->type); + g_free(service->name); + g_free(service->ssid); + g_free(service); + } + + return false; +} + +static bool load_service_from_keyfile(GKeyFile *keyfile, + struct connman_config *config) +{ + bool found = false; + char **groups; + int i; + + groups = g_key_file_get_groups(keyfile, NULL); + + for (i = 0; groups[i]; i++) { + if (!g_str_has_prefix(groups[i], "service_")) { + connman_warn("Ignore group named '%s' because prefix " + "is not 'service_'", groups[i]); + continue; + } + if (load_service(keyfile, groups[i], config)) + found = true; + } + + g_strfreev(groups); + + return found; +} static int load_config(struct connman_config *config) { @@ -42,104 +890,112 @@ static int load_config(struct connman_config *config) DBG("config %p", config); - keyfile = __connman_storage_open_config(config->ident); - if (keyfile == NULL) + keyfile = __connman_storage_load_config(config->ident); + if (!keyfile) return -EIO; - str = g_key_file_get_string(keyfile, "global", "Name", NULL); - if (str != NULL) { + g_key_file_set_list_separator(keyfile, ','); + + /* Verify keys validity of the global section */ + check_keys(keyfile, "global", config_possible_keys); + + str = __connman_config_get_string(keyfile, "global", CONFIG_KEY_NAME, NULL); + if (str) { g_free(config->name); config->name = str; } - str = g_key_file_get_string(keyfile, "global", "Description", NULL); - if (str != NULL) { + str = __connman_config_get_string(keyfile, "global", CONFIG_KEY_DESC, NULL); + if (str) { g_free(config->description); config->description = str; } - __connman_storage_close_config(config->ident, keyfile, FALSE); - - return 0; -} - -static void free_config(struct connman_config *config) -{ - g_free(config->description); - g_free(config->name); - g_free(config->ident); - g_free(config); -} - -static void unregister_config(gpointer data) -{ - struct connman_config *config = data; + if (!load_service_from_keyfile(keyfile, config)) + connman_warn("Config file %s/%s.config does not contain any " + "configuration that can be provisioned!", + STORAGEDIR, config->ident); - connman_info("Removing configuration %s", config->ident); + g_key_file_free(keyfile); - free_config(config); + return 0; } -static int create_config(const char *ident) +static struct connman_config *create_config(const char *ident) { struct connman_config *config; DBG("ident %s", ident); + if (g_hash_table_lookup(config_table, ident)) + return NULL; + config = g_try_new0(struct connman_config, 1); - if (config == NULL) - return -ENOMEM; + if (!config) + return NULL; config->ident = g_strdup(ident); - if (config->ident == NULL) { - free_config(config); - return -ENOMEM; - } - - if (g_hash_table_lookup(config_hash, config->ident) != NULL) { - free_config(config); - return -EEXIST; - } + config->service_table = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, unregister_service); - g_hash_table_insert(config_hash, g_strdup(config->ident), config); + g_hash_table_insert(config_table, config->ident, config); connman_info("Adding configuration %s", config->ident); - load_config(config); + return config; +} - return 0; +static bool validate_ident(const char *ident) +{ + unsigned int i; + + if (!ident) + return false; + + for (i = 0; i < strlen(ident); i++) + if (!g_ascii_isprint(ident[i])) + return false; + + return true; } -static int config_init(void) +static int read_configs(void) { GDir *dir; - const gchar *file; DBG(""); dir = g_dir_open(STORAGEDIR, 0, NULL); - if (dir != NULL) { - while ((file = g_dir_read_name(dir)) != NULL) { + if (dir) { + const gchar *file; + + while ((file = g_dir_read_name(dir))) { GString *str; gchar *ident; - if (g_str_has_suffix(file, ".config") == FALSE) + if (!g_str_has_suffix(file, ".config")) continue; ident = g_strrstr(file, ".config"); - if (ident == NULL) + if (!ident) continue; str = g_string_new_len(file, ident - file); - if (str == NULL) + if (!str) continue; ident = g_string_free(str, FALSE); - if (connman_dbus_validate_ident(ident) == TRUE) - create_config(ident); + if (validate_ident(ident)) { + struct connman_config *config; + config = create_config(ident); + if (config) + load_config(config); + } else { + connman_error("Invalid config ident %s", ident); + } g_free(ident); } @@ -149,20 +1005,862 @@ static int config_init(void) return 0; } -int __connman_config_init(void) +static void config_notify_handler(struct inotify_event *event, + const char *ident) { - DBG(""); + char *ext; - config_hash = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, unregister_config); + if (!ident) + return; - return config_init(); -} + if (!g_str_has_suffix(ident, ".config")) + return; -void __connman_config_cleanup(void) -{ - DBG(""); + ext = g_strrstr(ident, ".config"); + if (!ext) + return; + + *ext = '\0'; + + if (!validate_ident(ident)) { + connman_error("Invalid config ident %s", ident); + return; + } + + if (event->mask & (IN_CREATE | IN_MOVED_TO)) + create_config(ident); + + if (event->mask & (IN_MODIFY | IN_MOVED_TO)) { + struct connman_config *config; + + config = g_hash_table_lookup(config_table, ident); + if (config) { + int ret; + + g_hash_table_remove_all(config->service_table); + load_config(config); + ret = __connman_service_provision_changed(ident); + if (ret > 0) { + /* + * Re-scan the config file for any + * changes + */ + g_hash_table_remove_all(config->service_table); + load_config(config); + __connman_service_provision_changed(ident); + } + } + } + + if (event->mask & (IN_DELETE | IN_MOVED_FROM)) + g_hash_table_remove(config_table, ident); +} + +int __connman_config_init(void) +{ + DBG(""); + + config_table = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, unregister_config); + + connman_inotify_register(STORAGEDIR, config_notify_handler); + + return read_configs(); +} + +void __connman_config_cleanup(void) +{ + DBG(""); + + cleanup = true; + + connman_inotify_unregister(STORAGEDIR, config_notify_handler); + + g_hash_table_destroy(config_table); + config_table = NULL; + + cleanup = false; +} + +char *__connman_config_get_string(GKeyFile *key_file, + const char *group_name, const char *key, GError **error) +{ + char *str = g_key_file_get_string(key_file, group_name, key, error); + if (!str) + return NULL; + + /* passphrases can have spaces in the end */ + if (!g_strcmp0(key, SERVICE_KEY_PASSPHRASE) || + !g_strcmp0(key, SERVICE_KEY_PRV_KEY_PASS)) + return str; + + return g_strchomp(str); +} + +char **__connman_config_get_string_list(GKeyFile *key_file, + const char *group_name, const char *key, gsize *length, GError **error) +{ + char **p; + char **strlist = g_key_file_get_string_list(key_file, group_name, key, + length, error); + if (!strlist) + return NULL; + + p = strlist; + while (*p) { + *p = g_strstrip(*p); + p++; + } + + return strlist; +} + +bool __connman_config_get_bool(GKeyFile *key_file, + const char *group_name, const char *key, GError **error) +{ + char *valstr; + bool val = false; + + valstr = g_key_file_get_value(key_file, group_name, key, error); + if (!valstr) + return false; + + valstr = g_strchomp(valstr); + if (strcmp(valstr, "true") == 0 || strcmp(valstr, "1") == 0) + val = true; + + g_free(valstr); + + return val; +} + +static char *config_pem_fsid(const char *pem_file) +{ + struct statfs buf; + unsigned *fsid = (unsigned *) &buf.f_fsid; + unsigned long long fsid64; + + if (!pem_file) + return NULL; + + if (statfs(pem_file, &buf) < 0) { + connman_error("statfs error %s for %s", + strerror(errno), pem_file); + return NULL; + } + + fsid64 = ((unsigned long long) fsid[0] << 32) | fsid[1]; + + return g_strdup_printf("%llx", fsid64); +} + +static void provision_service_wifi(struct connman_config_service *config, + struct connman_service *service, + struct connman_network *network, + const void *ssid, unsigned int ssid_len) +{ + if (config->eap) + __connman_service_set_string(service, "EAP", config->eap); + + if (config->identity) + __connman_service_set_string(service, "Identity", + config->identity); + + if (config->anonymous_identity) + __connman_service_set_string(service, "AnonymousIdentity", + config->anonymous_identity); + + if (config->ca_cert_file) + __connman_service_set_string(service, "CACertFile", + config->ca_cert_file); + + if (config->subject_match) + __connman_service_set_string(service, "SubjectMatch", + config->subject_match); + + if (config->altsubject_match) + __connman_service_set_string(service, "AltSubjectMatch", + config->altsubject_match); + + if (config->domain_suffix_match) + __connman_service_set_string(service, "DomainSuffixMatch", + config->domain_suffix_match); + + if (config->domain_match) + __connman_service_set_string(service, "DomainMatch", + config->domain_match); + + if (config->client_cert_file) + __connman_service_set_string(service, "ClientCertFile", + config->client_cert_file); + + if (config->private_key_file) + __connman_service_set_string(service, "PrivateKeyFile", + config->private_key_file); + + if (g_strcmp0(config->private_key_passphrase_type, "fsid") == 0 && + config->private_key_file) { + char *fsid; + + fsid = config_pem_fsid(config->private_key_file); + if (!fsid) + return; + + g_free(config->private_key_passphrase); + config->private_key_passphrase = fsid; + } + + if (config->private_key_passphrase) { + __connman_service_set_string(service, "PrivateKeyPassphrase", + config->private_key_passphrase); + /* + * TODO: Support for PEAP with both identity and key passwd. + * In that case, we should check if both of them are found + * from the config file. If not, we should not set the + * service passphrase in order for the UI to request for an + * additional passphrase. + */ + } + + if (config->phase2) + __connman_service_set_string(service, "Phase2", config->phase2); +#if defined TIZEN_EXT + else + __connman_service_set_string(service, "Phase2", NULL); +#endif + + if (config->passphrase) + __connman_service_set_string(service, "Passphrase", + config->passphrase); + + if (config->hidden) + __connman_service_set_hidden(service); + +#if defined TIZEN_EXT + if (config->connector) + __connman_service_set_string(service, "Connector", + config->connector); + if (config->c_sign_key) + __connman_service_set_string(service, "CSignKey", + config->c_sign_key); + if (config->net_access_key) + __connman_service_set_string(service, "NetAccessKey", + config->net_access_key); +#endif +} + +struct connect_virtual { + struct connman_service *service; + const char *vfile; +}; + +static gboolean remove_virtual_config(gpointer user_data) +{ + struct connect_virtual *virtual = user_data; + + __connman_service_connect(virtual->service, + CONNMAN_SERVICE_CONNECT_REASON_AUTO); + g_hash_table_remove(config_table, virtual->vfile); + + g_free(virtual); + + return FALSE; +} + +#if defined TIZEN_EXT +static bool __check_address_type(int address_family, const char *address) +{ + unsigned char buf[sizeof(struct in6_addr)] = {0, }; + int err = 0; + + err = inet_pton(address_family, address, buf); + if(err > 0) + return TRUE; + + return FALSE; +} +#endif + +static int try_provision_service(struct connman_config_service *config, + struct connman_service *service) +{ + struct connman_network *network; + const void *service_id; + enum connman_service_type type; + const void *ssid; + unsigned int ssid_len; + const char *str; + + network = __connman_service_get_network(service); + if (!network) { + connman_error("Service has no network set"); + return -EINVAL; + } + + DBG("network %p ident %s", network, + connman_network_get_identifier(network)); + + type = connman_service_get_type(service); + + switch(type) { + case CONNMAN_SERVICE_TYPE_WIFI: + if (__connman_service_string2type(config->type) != type) + return -ENOENT; + + ssid = connman_network_get_blob(network, "WiFi.SSID", + &ssid_len); + if (!ssid) + return -ENOENT; + + if (!config->ssid || ssid_len != config->ssid_len) + return -ENOENT; + + if (memcmp(config->ssid, ssid, ssid_len)) + return -ENOENT; + + str = connman_network_get_string(network, "WiFi.Security"); + if (config->security != __connman_service_string2security(str)) + return -ENOENT; + + break; + + case CONNMAN_SERVICE_TYPE_ETHERNET: + case CONNMAN_SERVICE_TYPE_GADGET: + + if (__connman_service_string2type(config->type) != type) + return -ENOENT; + + break; + + case CONNMAN_SERVICE_TYPE_UNKNOWN: + case CONNMAN_SERVICE_TYPE_SYSTEM: + case CONNMAN_SERVICE_TYPE_BLUETOOTH: + case CONNMAN_SERVICE_TYPE_CELLULAR: + case CONNMAN_SERVICE_TYPE_GPS: + case CONNMAN_SERVICE_TYPE_VPN: + case CONNMAN_SERVICE_TYPE_P2P: +#if defined TIZEN_EXT_WIFI_MESH + case CONNMAN_SERVICE_TYPE_MESH: +#endif + + return -ENOENT; + } + + DBG("service %p ident %s", service, + connman_service_get_identifier(service)); + + if (config->mac) { + struct connman_device *device; + const char *device_addr; + + device = connman_network_get_device(network); + if (!device) { + connman_error("Network device is missing"); + return -ENODEV; + } + + device_addr = connman_device_get_string(device, "Address"); + + DBG("wants %s has %s", config->mac, device_addr); + + if (g_ascii_strcasecmp(device_addr, config->mac) != 0) + return -ENOENT; + } + +#if defined TIZEN_EXT + struct connman_ipconfig *ip6config = __connman_service_get_ip6config(service); + enum connman_ipconfig_method ipv6_method = __connman_ipconfig_get_method(ip6config); + if (ipv6_method == CONNMAN_IPCONFIG_METHOD_MANUAL) + goto ipv6_out; +#endif + if (!config->ipv6_address) { + connman_network_set_ipv6_method(network, + CONNMAN_IPCONFIG_METHOD_AUTO); + } else if (g_ascii_strcasecmp(config->ipv6_address, "off") == 0) { + connman_network_set_ipv6_method(network, + CONNMAN_IPCONFIG_METHOD_OFF); + } else if (g_ascii_strcasecmp(config->ipv6_address, "auto") == 0 || + g_ascii_strcasecmp(config->ipv6_address, "dhcp") == 0) { + connman_network_set_ipv6_method(network, + CONNMAN_IPCONFIG_METHOD_AUTO); + } else { + struct connman_ipaddress *address; + + if (config->ipv6_prefix_length == 0) { + DBG("IPv6 prefix missing"); + return -EINVAL; + } + + address = connman_ipaddress_alloc(AF_INET6); + if (!address) + return -ENOENT; + + connman_ipaddress_set_ipv6(address, config->ipv6_address, + config->ipv6_prefix_length, + config->ipv6_gateway); + + connman_network_set_ipv6_method(network, + CONNMAN_IPCONFIG_METHOD_FIXED); + + if (connman_network_set_ipaddress(network, address) < 0) + DBG("Unable to set IPv6 address to network %p", + network); + + connman_ipaddress_free(address); + } + +#if defined TIZEN_EXT +ipv6_out: +#endif + if (config->ipv6_privacy) { + struct connman_ipconfig *ipconfig; + + ipconfig = __connman_service_get_ip6config(service); + if (ipconfig) + __connman_ipconfig_ipv6_set_privacy(ipconfig, + config->ipv6_privacy); + } + +#if defined TIZEN_EXT + struct connman_ipconfig *ip4config = __connman_service_get_ip4config(service); + enum connman_ipconfig_method ipv4_method = __connman_ipconfig_get_method(ip4config); + if (ipv4_method == CONNMAN_IPCONFIG_METHOD_MANUAL) + goto ipv4_out; +#endif + if (!config->ipv4_address) { + connman_network_set_ipv4_method(network, + CONNMAN_IPCONFIG_METHOD_DHCP); + } else if (g_ascii_strcasecmp(config->ipv4_address, "off") == 0) { + connman_network_set_ipv4_method(network, + CONNMAN_IPCONFIG_METHOD_OFF); + } else if (g_ascii_strcasecmp(config->ipv4_address, "auto") == 0 || + g_ascii_strcasecmp(config->ipv4_address, "dhcp") == 0) { + connman_network_set_ipv4_method(network, + CONNMAN_IPCONFIG_METHOD_DHCP); + } else { + struct connman_ipaddress *address; + + if (!config->ipv4_netmask) { + DBG("IPv4 netmask missing"); + return -EINVAL; + } + + address = connman_ipaddress_alloc(AF_INET); + if (!address) + return -ENOENT; + + connman_ipaddress_set_ipv4(address, config->ipv4_address, + config->ipv4_netmask, + config->ipv4_gateway); + + connman_network_set_ipv4_method(network, + CONNMAN_IPCONFIG_METHOD_FIXED); + + if (connman_network_set_ipaddress(network, address) < 0) + DBG("Unable to set IPv4 address to network %p", + network); + + connman_ipaddress_free(address); + } + +#if defined TIZEN_EXT +ipv4_out: +#endif + __connman_service_disconnect(service); + + service_id = connman_service_get_identifier(service); + config->service_identifiers = + g_slist_prepend(config->service_identifiers, + g_strdup(service_id)); + + __connman_service_set_favorite_delayed(service, true, true); + + __connman_service_set_config(service, config->config_ident, + config->config_entry); + + if (config->domain_name) + __connman_service_set_domainname(service, config->domain_name); + + if (config->nameservers) { + int i; + + __connman_service_nameserver_clear(service); + + for (i = 0; config->nameservers[i]; i++) { +#if defined TIZEN_EXT + if (__check_address_type(AF_INET, config->nameservers[i])) + __connman_service_nameserver_append(service, + config->nameservers[i], false, + CONNMAN_IPCONFIG_TYPE_IPV4); + else if (__check_address_type(AF_INET6, config->nameservers[i])) + __connman_service_nameserver_append(service, + config->nameservers[i], false, + CONNMAN_IPCONFIG_TYPE_IPV6); +#else + __connman_service_nameserver_append(service, + config->nameservers[i], false); +#endif + } + } + + if (config->search_domains) + __connman_service_set_search_domains(service, + config->search_domains); + + __connman_service_set_mdns(service, config->mdns); + + if (config->timeservers) + __connman_service_set_timeservers(service, + config->timeservers); + + if (type == CONNMAN_SERVICE_TYPE_WIFI) { + provision_service_wifi(config, service, network, + ssid, ssid_len); + } + + __connman_service_mark_dirty(); + + __connman_service_load_modifiable(service); + + if (config->virtual) { + struct connect_virtual *virtual; + + virtual = g_malloc0(sizeof(struct connect_virtual)); + virtual->service = service; + virtual->vfile = config->virtual_file; + + g_idle_add(remove_virtual_config, virtual); + + return 0; + } + +#if !defined TIZEN_EXT + __connman_service_set_immutable(service, true); +#endif + + if (type == CONNMAN_SERVICE_TYPE_ETHERNET || + type == CONNMAN_SERVICE_TYPE_GADGET) { + __connman_service_connect(service, + CONNMAN_SERVICE_CONNECT_REASON_AUTO); + + return 0; + } + + __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO); + + return 0; +} + +static int +find_and_provision_service_from_config(struct connman_service *service, + struct connman_config *config) +{ + GHashTableIter iter; + gpointer value, key; + + g_hash_table_iter_init(&iter, config->service_table); + while (g_hash_table_iter_next(&iter, &key, + &value)) { + if (!try_provision_service(value, service)) + return 0; + } + + return -ENOENT; +} + +static int find_and_provision_service(struct connman_service *service) +{ + GHashTableIter iter; + gpointer value, key; + + g_hash_table_iter_init(&iter, config_table); + + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct connman_config *config = value; + + if (!find_and_provision_service_from_config(service, config)) + return 0; + } + + return -ENOENT; +} + +int __connman_config_provision_service(struct connman_service *service) +{ + enum connman_service_type type; + + /* For now only WiFi, Gadget and Ethernet services are supported */ + type = connman_service_get_type(service); + + DBG("service %p type %d", service, type); + + if (type != CONNMAN_SERVICE_TYPE_WIFI && + type != CONNMAN_SERVICE_TYPE_ETHERNET && + type != CONNMAN_SERVICE_TYPE_GADGET) + return -ENOSYS; + +#if defined TIZEN_EXT + if(type == CONNMAN_SERVICE_TYPE_WIFI && + __connman_service_get_security(service) == + CONNMAN_SERVICE_SECURITY_NONE) + return -ENOSYS; +#endif + + return find_and_provision_service(service); +} + +int __connman_config_provision_service_ident(struct connman_service *service, + const char *ident, const char *file, const char *entry) +{ + enum connman_service_type type; + struct connman_config *config; + int ret = 0; + + /* For now only WiFi, Gadget and Ethernet services are supported */ + type = connman_service_get_type(service); + + DBG("service %p type %d", service, type); + + if (type != CONNMAN_SERVICE_TYPE_WIFI && + type != CONNMAN_SERVICE_TYPE_ETHERNET && + type != CONNMAN_SERVICE_TYPE_GADGET) + return -ENOSYS; + + config = g_hash_table_lookup(config_table, ident); + if (config) { + GHashTableIter iter; + gpointer value, key; + bool found = false; + + g_hash_table_iter_init(&iter, config->service_table); + + /* + * Check if we need to remove individual service if it + * is missing from config file. + */ + if (file && entry) { + while (g_hash_table_iter_next(&iter, &key, + &value)) { + struct connman_config_service *config_service; + + config_service = value; + + if (g_strcmp0(config_service->config_ident, + file) != 0) + continue; + + if (g_strcmp0(config_service->config_entry, + entry) != 0) + continue; + + found = true; + break; + } + + DBG("found %d ident %s file %s entry %s", found, ident, + file, entry); + + if (!found) { + /* + * The entry+8 will skip "service_" prefix + */ + g_hash_table_remove(config->service_table, + entry + 8); + ret = 1; + } + } + + find_and_provision_service_from_config(service, config); + } + + return ret; +} + +static void generate_random_string(char *str, int length) +{ + uint8_t val; + int i; + uint64_t rand; + + memset(str, '\0', length); + + for (i = 0; i < length-1; i++) { + do { + __connman_util_get_random(&rand); + val = (uint8_t)(rand % 122); + if (val < 48) + val += 48; + } while((val > 57 && val < 65) || (val > 90 && val < 97)); + + str[i] = val; + } +} + +int connman_config_provision_mutable_service(GKeyFile *keyfile) +{ + struct connman_config_service *service_config; + struct connman_config *config; + char *vfile, *group = NULL; + char rstr[11]; + + DBG(""); + + generate_random_string(rstr, 11); + + vfile = g_strdup_printf("service_mutable_%s.config", rstr); + + config = create_config(vfile); + if (!config) + return -ENOMEM; + + if (!load_service_from_keyfile(keyfile, config)) + goto error; + + group = g_key_file_get_start_group(keyfile); + + service_config = g_hash_table_lookup(config->service_table, group+8); + if (!service_config) + goto error; + + /* Specific to non file based config: */ + g_free(service_config->config_ident); + service_config->config_ident = NULL; + g_free(service_config->config_entry); + service_config->config_entry = NULL; + + service_config->virtual = true; + service_config->virtual_file = vfile; + + __connman_service_provision_changed(vfile); + + if (g_strcmp0(service_config->type, "wifi") == 0) + __connman_device_request_scan(CONNMAN_SERVICE_TYPE_WIFI); + + g_free(group); + return 0; + +error: + DBG("Could not proceed"); + g_hash_table_remove(config_table, vfile); + g_free(vfile); + g_free(group); + return -EINVAL; +} + +struct connman_config_entry **connman_config_get_entries(const char *type) +{ + GHashTableIter iter_file, iter_config; + gpointer value, key; + struct connman_config_entry **entries = NULL; + int i = 0, count; + + g_hash_table_iter_init(&iter_file, config_table); + while (g_hash_table_iter_next(&iter_file, &key, &value)) { + struct connman_config *config_file = value; + struct connman_config_entry **tmp_entries = entries; + + count = g_hash_table_size(config_file->service_table); + + entries = g_try_realloc(entries, (i + count + 1) * + sizeof(struct connman_config_entry *)); + if (!entries) { + g_free(tmp_entries); + return NULL; + } + + g_hash_table_iter_init(&iter_config, + config_file->service_table); + while (g_hash_table_iter_next(&iter_config, &key, + &value)) { + struct connman_config_service *config = value; + + if (type && + g_strcmp0(config->type, type) != 0) + continue; + + entries[i] = g_try_new0(struct connman_config_entry, + 1); + if (!entries[i]) + goto cleanup; + + entries[i]->ident = g_strdup(config->ident); + entries[i]->name = g_strdup(config->name); + entries[i]->ssid = g_try_malloc0(config->ssid_len + 1); + if (!entries[i]->ssid) + goto cleanup; + + memcpy(entries[i]->ssid, config->ssid, + config->ssid_len); + entries[i]->ssid_len = config->ssid_len; + entries[i]->hidden = config->hidden; + + i++; + } + } + + if (entries) { + struct connman_config_entry **tmp_entries = entries; + + entries = g_try_realloc(entries, (i + 1) * + sizeof(struct connman_config_entry *)); + if (!entries) { + g_free(tmp_entries); + return NULL; + } + + entries[i] = NULL; + + DBG("%d provisioned AP found", i); + } + + return entries; + +cleanup: + connman_config_free_entries(entries); + return NULL; +} + +void connman_config_free_entries(struct connman_config_entry **entries) +{ + int i; + + if (!entries) + return; + + for (i = 0; entries[i]; i++) { + g_free(entries[i]->ident); + g_free(entries[i]->name); + g_free(entries[i]->ssid); + g_free(entries[i]); + } + + g_free(entries); +} + +bool __connman_config_address_provisioned(const char *address, + const char *netmask) +{ + GHashTableIter iter, siter; + gpointer value, key, svalue, skey; + + if (!address || !netmask) + return false; + + g_hash_table_iter_init(&iter, config_table); + + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct connman_config *config = value; + + g_hash_table_iter_init(&siter, config->service_table); + while (g_hash_table_iter_next(&siter, &skey, &svalue)) { + struct connman_config_service *service = svalue; + + if (!g_strcmp0(address, service->ipv4_address) && + !g_strcmp0(netmask, + service->ipv4_netmask)) + return true; + } + } - g_hash_table_destroy(config_hash); - config_hash = NULL; + return false; }