From 3497514256cdbc94a7c72e4f48c1f38e7b1e559a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 29 Dec 2009 04:07:06 -0800 Subject: [PATCH] Add interface and network handling to supplicant test program --- Makefile.am | 3 +- tools/supplicant-dbus.c | 180 ++++++++++++ tools/supplicant-dbus.h | 46 +++ tools/supplicant-test.c | 28 +- tools/supplicant.c | 747 +++++++++++++++++++++++++++++++++++++++++------- tools/supplicant.h | 46 ++- 6 files changed, 933 insertions(+), 117 deletions(-) create mode 100644 tools/supplicant-dbus.c create mode 100644 tools/supplicant-dbus.h diff --git a/Makefile.am b/Makefile.am index cd4d4a5..46f3037 100644 --- a/Makefile.am +++ b/Makefile.am @@ -118,7 +118,8 @@ noinst_PROGRAMS += tools/wifi-scan tools/supplicant-test \ tools_wifi_scan_LDADD = @GLIB_LIBS@ @NETLINK_LIBS@ tools_supplicant_test_SOURCES = $(gdbus_sources) tools/supplicant-test.c \ - tools/supplicant.h tools/supplicant.c + tools/supplicant-dbus.h tools/supplicant-dbus.c \ + tools/supplicant.h tools/supplicant.c tools_supplicant_test_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ tools_polkit_test_LDADD = @DBUS_LIBS@ diff --git a/tools/supplicant-dbus.c b/tools/supplicant-dbus.c new file mode 100644 index 0000000..3f1c9af --- /dev/null +++ b/tools/supplicant-dbus.c @@ -0,0 +1,180 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2007-2009 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 + * published by the Free Software Foundation. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "supplicant-dbus.h" + +#define TIMEOUT 5000 + +static DBusConnection *connection; + +void supplicant_dbus_setup(DBusConnection *conn) +{ + connection = conn; +} + +void supplicant_dbus_array_foreach(DBusMessageIter *iter, + supplicant_dbus_array_function function, + void *user_data) +{ + DBusMessageIter entry; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return; + + dbus_message_iter_recurse(iter, &entry); + + while (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_INVALID) { + if (function != NULL) + function(&entry, user_data); + + dbus_message_iter_next(&entry); + } +} + +void supplicant_dbus_property_foreach(DBusMessageIter *iter, + supplicant_dbus_property_function function, + void *user_data) +{ + DBusMessageIter dict; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return; + + dbus_message_iter_recurse(iter, &dict); + + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry, value; + const char *key; + + dbus_message_iter_recurse(&dict, &entry); + + if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) + return; + + dbus_message_iter_get_basic(&entry, &key); + dbus_message_iter_next(&entry); + + if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) + return; + + dbus_message_iter_recurse(&entry, &value); + + if (key != NULL) { + if (strcmp(key, "Properties") == 0) + supplicant_dbus_property_foreach(&value, + function, user_data); + else if (function != NULL) + function(key, &value, user_data); + } + + dbus_message_iter_next(&dict); + } +} + +struct property_data { + supplicant_dbus_property_function function; + void *user_data; +}; + +static void property_get_all_reply(DBusPendingCall *call, void *user_data) +{ + struct property_data *data = user_data; + DBusMessage *reply; + DBusMessageIter iter; + + reply = dbus_pending_call_steal_reply(call); + if (reply == NULL) + return; + + if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) + goto done; + + if (dbus_message_iter_init(reply, &iter) == FALSE) + goto done; + + supplicant_dbus_property_foreach(&iter, data->function, + data->user_data); + + if (data->function != NULL) + data->function(NULL, NULL, data->user_data); + +done: + dbus_message_unref(reply); +} + +int supplicant_dbus_property_get_all(const char *path, const char *interface, + supplicant_dbus_property_function function, + void *user_data) +{ + struct property_data *data; + DBusMessage *message; + DBusPendingCall *call; + + if (path == NULL || interface == NULL) + return -EINVAL; + + data = dbus_malloc0(sizeof(*data)); + if (data == NULL) + return -ENOMEM; + + message = dbus_message_new_method_call(SUPPLICANT_SERVICE, path, + DBUS_INTERFACE_PROPERTIES, "GetAll"); + if (message == NULL) { + dbus_free(data); + return -ENOMEM; + } + + dbus_message_set_auto_start(message, FALSE); + + dbus_message_append_args(message, DBUS_TYPE_STRING, &interface, NULL); + + if (dbus_connection_send_with_reply(connection, message, + &call, TIMEOUT) == FALSE) { + dbus_message_unref(message); + dbus_free(data); + return -EIO; + } + + if (call == NULL) { + dbus_message_unref(message); + dbus_free(data); + return -EIO; + } + + data->function = function; + data->user_data = user_data; + + dbus_pending_call_set_notify(call, property_get_all_reply, + data, dbus_free); + + dbus_message_unref(message); + + return 0; +} diff --git a/tools/supplicant-dbus.h b/tools/supplicant-dbus.h new file mode 100644 index 0000000..04cf0f5 --- /dev/null +++ b/tools/supplicant-dbus.h @@ -0,0 +1,46 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2007-2009 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 + * published by the Free Software Foundation. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +#define SUPPLICANT_SERVICE "fi.w1.wpa_supplicant1" +#define SUPPLICANT_INTERFACE "fi.w1.wpa_supplicant1" +#define SUPPLICANT_PATH "/fi/w1/wpa_supplicant1" + +typedef void (* supplicant_dbus_array_function) (DBusMessageIter *iter, + void *user_data); + +typedef void (* supplicant_dbus_property_function) (const char *key, + DBusMessageIter *iter, void *user_data); + +void supplicant_dbus_setup(DBusConnection *conn); + +void supplicant_dbus_array_foreach(DBusMessageIter *iter, + supplicant_dbus_array_function function, + void *user_data); + +void supplicant_dbus_property_foreach(DBusMessageIter *iter, + supplicant_dbus_property_function function, + void *user_data); + +int supplicant_dbus_property_get_all(const char *path, const char *interface, + supplicant_dbus_property_function function, + void *user_data); diff --git a/tools/supplicant-test.c b/tools/supplicant-test.c index b448a02..47e183b 100644 --- a/tools/supplicant-test.c +++ b/tools/supplicant-test.c @@ -37,19 +37,39 @@ syslog(LOG_DEBUG, "%s() " fmt, __FUNCTION__ , ## arg); \ } while (0) -static void interface_added(const struct supplicant_interface *interface) +static void interface_added(struct supplicant_interface *interface) { - DBG("interface %p", interface); + const char *ifname = supplicant_interface_get_ifname(interface); + + DBG("ifname %s", ifname); +} + +static void interface_removed(struct supplicant_interface *interface) +{ + const char *ifname = supplicant_interface_get_ifname(interface); + + DBG("ifname %s", ifname); } -static void interface_removed(const struct supplicant_interface *interface) +static void network_added(struct supplicant_network *network) { - DBG("interface %p", interface); + const char *name = supplicant_network_get_name(network); + + DBG("name %s", name); +} + +static void network_removed(struct supplicant_network *network) +{ + const char *name = supplicant_network_get_name(network); + + DBG("name %s", name); } static const struct supplicant_callbacks callbacks = { .interface_added = interface_added, .interface_removed = interface_removed, + .network_added = network_added, + .network_removed = network_removed, }; static GMainLoop *main_loop = NULL; diff --git a/tools/supplicant.c b/tools/supplicant.c index 4653a7f..d9cd084 100644 --- a/tools/supplicant.c +++ b/tools/supplicant.c @@ -30,182 +30,697 @@ #include #include +#include "supplicant-dbus.h" #include "supplicant.h" #define DBG(fmt, arg...) do { \ syslog(LOG_DEBUG, "%s() " fmt, __FUNCTION__ , ## arg); \ } while (0) -#define SUPPLICANT_SERVICE "fi.w1.wpa_supplicant1" -#define SUPPLICANT_INTERFACE "fi.w1.wpa_supplicant1" -#define SUPPLICANT_PATH "/fi/w1/wpa_supplicant1" - #define TIMEOUT 5000 static DBusConnection *connection; static const struct supplicant_callbacks *callbacks_pointer; -static void show_property(const char *key, DBusMessageIter *iter) -{ - DBusMessageIter array; +static unsigned int eap_methods; + +static struct { + const char *str; + unsigned int val; +} eap_method_map[] = { + { "MD5", SUPPLICANT_EAP_METHOD_MD5 }, + { "TLS", SUPPLICANT_EAP_METHOD_TLS }, + { "MSCHAPV2", SUPPLICANT_EAP_METHOD_MSCHAPV2 }, + { "PEAP", SUPPLICANT_EAP_METHOD_PEAP }, + { "TTLS", SUPPLICANT_EAP_METHOD_TTLS }, + { "GTC", SUPPLICANT_EAP_METHOD_GTC }, + { "OTP", SUPPLICANT_EAP_METHOD_OTP }, + { "LEAP", SUPPLICANT_EAP_METHOD_LEAP }, + { } +}; + +static struct { const char *str; - unsigned char byte; + unsigned int val; +} scan_capa_map[] = { + { "active", SUPPLICANT_CAPABILITY_SCAN_ACTIVE }, + { "passive", SUPPLICANT_CAPABILITY_SCAN_PASSIVE }, + { "ssid", SUPPLICANT_CAPABILITY_SCAN_SSID }, + { } +}; + +static GHashTable *interface_table; + +struct supplicant_interface { + char *path; + unsigned int scan_capa; + enum supplicant_state state; + dbus_bool_t scanning; + int apscan; + char *ifname; + char *driver; + char *bridge; + GHashTable *network_table; + GHashTable *bss_mapping; +}; + +struct supplicant_network { + struct supplicant_interface *interface; + char *group; + char *name; + enum supplicant_network_mode mode; + GHashTable *bss_table; +}; + +struct supplicant_bss { + struct supplicant_interface *interface; + char *path; + unsigned char bssid[6]; + unsigned char ssid[32]; + unsigned int ssid_len; + unsigned int frequency; +}; + +static enum supplicant_state string2state(const char *state) +{ + if (state == NULL) + return SUPPLICANT_STATE_UNKNOWN; + + if (g_str_equal(state, "unknown") == TRUE) + return SUPPLICANT_STATE_UNKNOWN; + else if (g_str_equal(state, "disconnected") == TRUE) + return SUPPLICANT_STATE_DISCONNECTED; + else if (g_str_equal(state, "inactive") == TRUE) + return SUPPLICANT_STATE_INACTIVE; + else if (g_str_equal(state, "scanning") == TRUE) + return SUPPLICANT_STATE_SCANNING; + else if (g_str_equal(state, "authenticating") == TRUE) + return SUPPLICANT_STATE_AUTHENTICATING; + else if (g_str_equal(state, "associating") == TRUE) + return SUPPLICANT_STATE_ASSOCIATING; + else if (g_str_equal(state, "associated") == TRUE) + return SUPPLICANT_STATE_ASSOCIATED; + else if (g_str_equal(state, "group_handshake") == TRUE) + return SUPPLICANT_STATE_GROUP_HANDSHAKE; + else if (g_str_equal(state, "4way_handshake") == TRUE) + return SUPPLICANT_STATE_4WAY_HANDSHAKE; + else if (g_str_equal(state, "completed") == TRUE) + return SUPPLICANT_STATE_COMPLETED; + + return SUPPLICANT_STATE_UNKNOWN; +} - switch (dbus_message_iter_get_arg_type(iter)) { - case DBUS_TYPE_STRING: - case DBUS_TYPE_OBJECT_PATH: - dbus_message_iter_get_basic(iter, &str); - DBG("%s = %s", key, str); - break; - case DBUS_TYPE_BYTE: - case DBUS_TYPE_BOOLEAN: - dbus_message_iter_get_basic(iter, &byte); - DBG("%s = %u", key, byte); - break; - case DBUS_TYPE_ARRAY: - DBG("%s = {array}", key); - dbus_message_iter_recurse(iter, &array); - while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) { - dbus_message_iter_get_basic(&array, &str); - DBG(" %s", str); - dbus_message_iter_next(&array); +static void callback_interface_added(struct supplicant_interface *interface) +{ + if (callbacks_pointer == NULL) + return; + + if (callbacks_pointer->interface_added == NULL) + return; + + callbacks_pointer->interface_added(interface); +} + +static void callback_interface_removed(struct supplicant_interface *interface) +{ + if (callbacks_pointer == NULL) + return; + + if (callbacks_pointer->interface_removed == NULL) + return; + + callbacks_pointer->interface_removed(interface); +} + +static void callback_network_added(struct supplicant_network *network) +{ + if (callbacks_pointer == NULL) + return; + + if (callbacks_pointer->network_added == NULL) + return; + + callbacks_pointer->network_added(network); +} + +static void callback_network_removed(struct supplicant_network *network) +{ + if (callbacks_pointer == NULL) + return; + + if (callbacks_pointer->network_removed == NULL) + return; + + callbacks_pointer->network_removed(network); +} + +static void remove_interface(gpointer data) +{ + struct supplicant_interface *interface = data; + + callback_interface_removed(interface); + + g_hash_table_destroy(interface->bss_mapping); + g_hash_table_destroy(interface->network_table); + + g_free(interface->path); + g_free(interface->ifname); + g_free(interface->driver); + g_free(interface->bridge); + g_free(interface); +} + +static void remove_network(gpointer data) +{ + struct supplicant_network *network = data; + + callback_network_removed(network); + + g_free(network->group); + g_free(network->name); + g_free(network); +} + +static void remove_bss(gpointer data) +{ + struct supplicant_bss *bss = data; + + g_free(bss->path); + g_free(bss); +} + +static void debug_eap_methods(void) +{ + int i; + + for (i = 0; eap_method_map[i].str != NULL; i++) { + if (eap_methods & eap_method_map[i].val) + DBG("EAP Method: %s", eap_method_map[i].str); + } +} + +static void debug_scan_capabilities(struct supplicant_interface *interface) +{ + int i; + + for (i = 0; scan_capa_map[i].str != NULL; i++) { + if (interface->scan_capa & scan_capa_map[i].val) + DBG("Scan Capability: %s", scan_capa_map[i].str); + } +} + +static void interface_capability_scan(DBusMessageIter *iter, void *user_data) +{ + struct supplicant_interface *interface = user_data; + const char *str = NULL; + int i; + + dbus_message_iter_get_basic(iter, &str); + if (str == NULL) + return; + + for (i = 0; scan_capa_map[i].str != NULL; i++) + if (strcmp(str, scan_capa_map[i].str) == 0) { + interface->scan_capa |= scan_capa_map[i].val; + break; } - break; - default: - DBG("%s = ...", key); - break; +} + +static void interface_capability(const char *key, DBusMessageIter *iter, + void *user_data) +{ + struct supplicant_interface *interface = user_data; + + if (key == NULL) + return; + + if (g_strcmp0(key, "Scan") == 0) + supplicant_dbus_array_foreach(iter, interface_capability_scan, + interface); + else + DBG("key %s type %c", + key, dbus_message_iter_get_arg_type(iter)); +} + +const char *supplicant_interface_get_ifname(struct supplicant_interface *interface) +{ + if (interface == NULL) + return NULL; + + return interface->ifname; +} + +struct supplicant_interface *supplicant_network_get_interface(struct supplicant_network *network) +{ + if (network == NULL) + return NULL; + + return network->interface; +} + +const char *supplicant_network_get_name(struct supplicant_network *network) +{ + if (network == NULL || network->name == NULL) + return ""; + + return network->name; +} + +enum supplicant_network_mode supplicant_network_get_mode(struct supplicant_network *network) +{ + if (network == NULL) + return SUPPLICANT_NETWORK_MODE_UNKNOWN; + + return network->mode; +} + +static void network_property(const char *key, DBusMessageIter *iter, + void *user_data) +{ + if (key == NULL) + return; + + DBG("key %s type %c", key, dbus_message_iter_get_arg_type(iter)); +} + +static void interface_network_added(DBusMessageIter *iter, void *user_data) +{ + const char *path = NULL; + + dbus_message_iter_get_basic(iter, &path); + if (path == NULL) + return; + + supplicant_dbus_property_get_all(path, + SUPPLICANT_INTERFACE ".Interface.Network", + network_property, NULL); +} + +static char *create_name(unsigned char *ssid, int ssid_len) +{ + char *name; + int i; + + if (ssid_len < 1 || ssid[0] == '\0') + name = NULL; + else + name = g_try_malloc0(ssid_len + 1); + + if (name == NULL) + return g_strdup(""); + + for (i = 0; i < ssid_len; i++) { + if (g_ascii_isprint(ssid[i])) + name[i] = ssid[i]; + else + name[i] = ' '; } + + return name; } -static void properties_decode(DBusMessageIter *iter) +static void add_bss_to_network(struct supplicant_bss *bss) { - DBusMessageIter dict; + struct supplicant_interface *interface = bss->interface; + struct supplicant_network *network; + GString *str; + char *group; + unsigned int i; + + str = g_string_sized_new((bss->ssid_len * 2) + 24); + if (str == NULL) + return; + + if (bss->ssid_len > 0 && bss->ssid[0] != '\0') { + for (i = 0; i < bss->ssid_len; i++) + g_string_append_printf(str, "%02x", bss->ssid[i]); + } else + g_string_append_printf(str, "hidden"); - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) { - syslog(LOG_ERR, "Invalid message type"); + group = g_string_free(str, FALSE); + + network = g_hash_table_lookup(interface->network_table, group); + if (network != NULL) { + g_free(group); + goto done; + } + + network = g_try_new0(struct supplicant_network, 1); + if (network == NULL) { + g_free(group); return; } - dbus_message_iter_recurse(iter, &dict); + network->group = group; + network->name = create_name(bss->ssid, bss->ssid_len); - while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { - DBusMessageIter entry, value; - const char *key; + network->bss_table = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, remove_bss); - dbus_message_iter_recurse(&dict, &entry); + g_hash_table_replace(interface->network_table, + network->group, network); - if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) - return; + callback_network_added(network); - dbus_message_iter_get_basic(&entry, &key); - dbus_message_iter_next(&entry); +done: + g_hash_table_replace(interface->bss_mapping, bss->path, network); + g_hash_table_replace(network->bss_table, bss->path, bss); +} - if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) - return; +static void bss_property(const char *key, DBusMessageIter *iter, + void *user_data) +{ + struct supplicant_bss *bss = user_data; + + if (bss->interface == NULL) + return; + + if (key == NULL) { + add_bss_to_network(bss); + return; + } + + if (g_strcmp0(key, "BSSID") == 0) { + DBusMessageIter array; + unsigned char *addr; + int addr_len; + + dbus_message_iter_recurse(iter, &array); + dbus_message_iter_get_fixed_array(&array, &addr, &addr_len); + + if (addr_len == 6) + memcpy(bss->bssid, addr, addr_len); + } else if (g_strcmp0(key, "SSID") == 0) { + DBusMessageIter array; + unsigned char *ssid; + int ssid_len; + + dbus_message_iter_recurse(iter, &array); + dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len); + + if (ssid_len > 0 && ssid_len < 33) { + memcpy(bss->ssid, ssid, ssid_len); + bss->ssid_len = ssid_len; + } else { + memset(bss->ssid, 0, sizeof(bss->ssid)); + bss->ssid_len = 0; + } + } else if (g_strcmp0(key, "Capabilities") == 0) { + unsigned char capabilities = 0x00; + + dbus_message_iter_get_basic(iter, &capabilities); + } else if (g_strcmp0(key, "Frequency") == 0) { + dbus_int32_t frequency = 0; + + dbus_message_iter_get_basic(iter, &frequency); + bss->frequency = frequency; + } else if (g_strcmp0(key, "Level") == 0) { + dbus_int32_t level = 0; + + dbus_message_iter_get_basic(iter, &level); + } else if (g_strcmp0(key, "MaxRate") == 0) { + dbus_int32_t maxrate = 0; + + dbus_message_iter_get_basic(iter, &maxrate); + } else if (g_strcmp0(key, "RSNIE") == 0) { + DBusMessageIter array; + unsigned char *ie; + int ie_len; + + dbus_message_iter_recurse(iter, &array); + dbus_message_iter_get_fixed_array(&array, &ie, &ie_len); + } else if (g_strcmp0(key, "WPAIE") == 0) { + DBusMessageIter array; + unsigned char *ie; + int ie_len; + + dbus_message_iter_recurse(iter, &array); + dbus_message_iter_get_fixed_array(&array, &ie, &ie_len); + } else if (g_strcmp0(key, "WPSIE") == 0) { + DBusMessageIter array; + unsigned char *ie; + int ie_len; - dbus_message_iter_recurse(&entry, &value); + dbus_message_iter_recurse(iter, &array); + dbus_message_iter_get_fixed_array(&array, &ie, &ie_len); + } else + DBG("key %s type %c", + key, dbus_message_iter_get_arg_type(iter)); +} - show_property(key, &value); +static void interface_bss_added(DBusMessageIter *iter, void *user_data) +{ + struct supplicant_interface *interface = user_data; + struct supplicant_network *network; + struct supplicant_bss *bss; + const char *path = NULL; - dbus_message_iter_next(&dict); + dbus_message_iter_get_basic(iter, &path); + if (path == NULL) + return; + + network = g_hash_table_lookup(interface->bss_mapping, path); + if (network != NULL) { + bss = g_hash_table_lookup(network->bss_table, path); + if (bss != NULL) + return; } + + bss = g_try_new0(struct supplicant_bss, 1); + if (bss == NULL) + return; + + bss->interface = interface; + bss->path = g_strdup(path); + + supplicant_dbus_property_get_all(path, + SUPPLICANT_INTERFACE ".Interface.BSS", + bss_property, bss); } -static void properties_get_all_reply(DBusPendingCall *call, void *user_data) +static void interface_bss_removed(DBusMessageIter *iter, void *user_data) { - DBusMessage *reply; - DBusMessageIter iter; + struct supplicant_interface *interface = user_data; + struct supplicant_network *network; + const char *path = NULL; - DBG("call %p", call); + dbus_message_iter_get_basic(iter, &path); + if (path == NULL) + return; - reply = dbus_pending_call_steal_reply(call); - if (reply == NULL) + network = g_hash_table_lookup(interface->bss_mapping, path); + if (network == NULL) return; - if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) - goto failed; + g_hash_table_remove(interface->bss_mapping, path); + g_hash_table_remove(network->bss_table, path); - if (dbus_message_iter_init(reply, &iter) == FALSE) - goto failed; + if (g_hash_table_size(network->bss_table) == 0) + g_hash_table_remove(interface->network_table, network->group); +} - DBG("success"); +static void interface_property(const char *key, DBusMessageIter *iter, + void *user_data) +{ + struct supplicant_interface *interface = user_data; - properties_decode(&iter); + if (interface == NULL) + return; + + if (key == NULL) { + debug_scan_capabilities(interface); + + g_hash_table_replace(interface_table, + interface->path, interface); + + callback_interface_added(interface); + return; + } - dbus_message_unref(reply); + if (g_strcmp0(key, "Capabilities") == 0) { + supplicant_dbus_property_foreach(iter, interface_capability, + interface); + } else if (g_strcmp0(key, "State") == 0) { + const char *str = NULL; + + dbus_message_iter_get_basic(iter, &str); + if (str != NULL) + interface->state = string2state(str); + } else if (g_strcmp0(key, "Scanning") == 0) { + dbus_bool_t scanning = FALSE; + + dbus_message_iter_get_basic(iter, &scanning); + interface->scanning = scanning; + } else if (g_strcmp0(key, "ApScan") == 0) { + int apscan; + + dbus_message_iter_get_basic(iter, &apscan); + interface->apscan = apscan; + } else if (g_strcmp0(key, "Ifname") == 0) { + const char *str = NULL; + + dbus_message_iter_get_basic(iter, &str); + if (str != NULL) + interface->ifname = g_strdup(str); + } else if (g_strcmp0(key, "Driver") == 0) { + const char *str = NULL; - return; + dbus_message_iter_get_basic(iter, &str); + if (str != NULL) + interface->driver = g_strdup(str); + } else if (g_strcmp0(key, "BridgeIfname") == 0) { + const char *str = NULL; -failed: - dbus_message_unref(reply); + dbus_message_iter_get_basic(iter, &str); + if (str != NULL) + interface->bridge = g_strdup(str); + } else if (g_strcmp0(key, "CurrentBSS") == 0) { + interface_bss_added(iter, interface); + } else if (g_strcmp0(key, "CurrentNetwork") == 0) { + interface_network_added(iter, interface); + } else if (g_strcmp0(key, "BSSs") == 0) { + supplicant_dbus_array_foreach(iter, interface_bss_added, + interface); + } else if (g_strcmp0(key, "Blobs") == 0) { + } else if (g_strcmp0(key, "Networks") == 0) { + supplicant_dbus_array_foreach(iter, interface_network_added, + interface); + } else + DBG("key %s type %c", + key, dbus_message_iter_get_arg_type(iter)); } -static int properties_get_all(const char *path, const char *interface) +static void interface_path(DBusMessageIter *iter, void *user_data) { - DBusMessage *message; - DBusPendingCall *call; + struct supplicant_interface *interface; + const char *path = NULL; - DBG(""); + dbus_message_iter_get_basic(iter, &path); + if (path == NULL) + return; - message = dbus_message_new_method_call(SUPPLICANT_SERVICE, path, - DBUS_INTERFACE_PROPERTIES, "GetAll"); - if (message == NULL) - return -ENOMEM; + interface = g_hash_table_lookup(interface_table, path); + if (interface != NULL) + return; - dbus_message_set_auto_start(message, FALSE); + interface = g_try_new0(struct supplicant_interface, 1); + if (interface == NULL) + return; - dbus_message_append_args(message, DBUS_TYPE_STRING, &interface, NULL); + interface->path = g_strdup(path); - if (dbus_connection_send_with_reply(connection, message, - &call, TIMEOUT) == FALSE) { - syslog(LOG_ERR, "Failed to add interface"); - dbus_message_unref(message); - return -EIO; - } + interface->network_table = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, remove_network); - if (call == NULL) { - syslog(LOG_ERR, "D-Bus connection not available"); - dbus_message_unref(message); - return -EIO; + interface->bss_mapping = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, NULL); + + supplicant_dbus_property_get_all(path, + SUPPLICANT_INTERFACE ".Interface", + interface_property, interface); +} + +static void eap_method(DBusMessageIter *iter, void *user_data) +{ + const char *str = NULL; + int i; + + dbus_message_iter_get_basic(iter, &str); + if (str == NULL) + return; + + for (i = 0; eap_method_map[i].str != NULL; i++) + if (strcmp(str, eap_method_map[i].str) == 0) { + eap_methods |= eap_method_map[i].val; + break; + } +} + +static void service_property(const char *key, DBusMessageIter *iter, + void *user_data) +{ + if (key == NULL) + return; + + if (g_strcmp0(key, "Interfaces") == 0) + supplicant_dbus_array_foreach(iter, interface_path, user_data); + else if (g_strcmp0(key, "EapMethods") == 0) { + supplicant_dbus_array_foreach(iter, eap_method, user_data); + debug_eap_methods(); + } else if (g_strcmp0(key, "DebugParams") == 0) { } +} - DBG("call %p", call); +static void signal_interface_added(const char *path, DBusMessageIter *iter) +{ + interface_path(iter, NULL); +} - dbus_pending_call_set_notify(call, properties_get_all_reply, - NULL, NULL); +static void signal_interface_removed(const char *path, DBusMessageIter *iter) +{ + DBG("path %s", path); +} - dbus_message_unref(message); +static void signal_bss_added(const char *path, DBusMessageIter *iter) +{ + struct supplicant_interface *interface; - return 0; + interface = g_hash_table_lookup(interface_table, path); + if (interface == NULL) + return; + + interface_bss_added(iter, interface); } -static DBusHandlerResult supplicant_filter(DBusConnection *conn, - DBusMessage *msg, void *data) +static void signal_bss_removed(const char *path, DBusMessageIter *iter) { - int prefixlen = strlen(SUPPLICANT_INTERFACE); - const char *interface, *member, *path; + struct supplicant_interface *interface; - interface = dbus_message_get_interface(msg); + interface = g_hash_table_lookup(interface_table, path); if (interface == NULL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + return; - if (g_str_has_prefix(interface, SUPPLICANT_INTERFACE) == FALSE) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + interface_bss_removed(iter, interface); +} + +static struct { + const char *interface; + const char *member; + void (*function) (const char *path, DBusMessageIter *iter); +} signal_map[] = { + { SUPPLICANT_INTERFACE, "InterfaceAdded", signal_interface_added }, + { SUPPLICANT_INTERFACE, "InterfaceRemoved", signal_interface_removed }, + { SUPPLICANT_INTERFACE ".Interface", "BSSAdded", signal_bss_added }, + { SUPPLICANT_INTERFACE ".Interface", "BSSRemoved", signal_bss_removed }, + { } +}; - member = dbus_message_get_member(msg); - if (member == NULL) +static DBusHandlerResult supplicant_filter(DBusConnection *conn, + DBusMessage *message, void *data) +{ + DBusMessageIter iter; + const char *path; + int i; + + path = dbus_message_get_path(message); + if (path == NULL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - path = dbus_message_get_path(msg); + if (dbus_message_iter_init(message, &iter) == FALSE) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - syslog(LOG_DEBUG, "[ %s ]%s.%s", path, interface + prefixlen, member); + for (i = 0; signal_map[i].interface != NULL; i++) { + if (dbus_message_has_interface(message, + signal_map[i].interface) == FALSE) + continue; - if (g_str_equal(member, "PropertiesChanged") == TRUE) { - DBusMessageIter iter; + if (dbus_message_has_member(message, + signal_map[i].member) == FALSE) + continue; - if (dbus_message_iter_init(msg, &iter) == TRUE) - properties_decode(&iter); + signal_map[i].function(path, &iter); + break; } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -226,8 +741,6 @@ static const char *supplicant_rule6 = "type=signal," int supplicant_register(const struct supplicant_callbacks *callbacks) { - DBG(""); - connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); if (connection == NULL) return -EIO; @@ -240,6 +753,12 @@ int supplicant_register(const struct supplicant_callbacks *callbacks) } callbacks_pointer = callbacks; + eap_methods = 0; + + interface_table = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, remove_interface); + + supplicant_dbus_setup(connection); dbus_bus_add_match(connection, supplicant_rule1, NULL); dbus_bus_add_match(connection, supplicant_rule2, NULL); @@ -249,15 +768,15 @@ int supplicant_register(const struct supplicant_callbacks *callbacks) dbus_bus_add_match(connection, supplicant_rule6, NULL); dbus_connection_flush(connection); - properties_get_all(SUPPLICANT_PATH, SUPPLICANT_INTERFACE); + supplicant_dbus_property_get_all(SUPPLICANT_PATH, + SUPPLICANT_INTERFACE, + service_property, NULL); return 0; } void supplicant_unregister(const struct supplicant_callbacks *callbacks) { - DBG(""); - if (connection != NULL) { dbus_bus_remove_match(connection, supplicant_rule6, NULL); dbus_bus_remove_match(connection, supplicant_rule5, NULL); @@ -269,10 +788,18 @@ void supplicant_unregister(const struct supplicant_callbacks *callbacks) dbus_connection_remove_filter(connection, supplicant_filter, NULL); + } + if (interface_table != NULL) { + g_hash_table_destroy(interface_table); + interface_table = NULL; + } + + if (connection != NULL) { dbus_connection_unref(connection); connection = NULL; } callbacks_pointer = NULL; + eap_methods = 0; } diff --git a/tools/supplicant.h b/tools/supplicant.h index e7ba876..64a2feb 100644 --- a/tools/supplicant.h +++ b/tools/supplicant.h @@ -19,11 +19,53 @@ * */ +#define SUPPLICANT_EAP_METHOD_MD5 (1 << 0) +#define SUPPLICANT_EAP_METHOD_TLS (1 << 1) +#define SUPPLICANT_EAP_METHOD_MSCHAPV2 (1 << 2) +#define SUPPLICANT_EAP_METHOD_PEAP (1 << 3) +#define SUPPLICANT_EAP_METHOD_TTLS (1 << 4) +#define SUPPLICANT_EAP_METHOD_GTC (1 << 5) +#define SUPPLICANT_EAP_METHOD_OTP (1 << 6) +#define SUPPLICANT_EAP_METHOD_LEAP (1 << 7) + +#define SUPPLICANT_CAPABILITY_SCAN_ACTIVE (1 << 0) +#define SUPPLICANT_CAPABILITY_SCAN_PASSIVE (1 << 1) +#define SUPPLICANT_CAPABILITY_SCAN_SSID (1 << 2) + +enum supplicant_state { + SUPPLICANT_STATE_UNKNOWN, + SUPPLICANT_STATE_DISCONNECTED, + SUPPLICANT_STATE_INACTIVE, + SUPPLICANT_STATE_SCANNING, + SUPPLICANT_STATE_AUTHENTICATING, + SUPPLICANT_STATE_ASSOCIATING, + SUPPLICANT_STATE_ASSOCIATED, + SUPPLICANT_STATE_4WAY_HANDSHAKE, + SUPPLICANT_STATE_GROUP_HANDSHAKE, + SUPPLICANT_STATE_COMPLETED, +}; + struct supplicant_interface; +const char *supplicant_interface_get_ifname(struct supplicant_interface *interface); + +struct supplicant_network; + +enum supplicant_network_mode { + SUPPLICANT_NETWORK_MODE_UNKNOWN, + SUPPLICANT_NETWORK_MODE_INFRA, + SUPPLICANT_NETWORK_MODE_ADHOC, +}; + +struct supplicant_interface *supplicant_network_get_interface(struct supplicant_network *network); +const char *supplicant_network_get_name(struct supplicant_network *network); +enum supplicant_network_mode supplicant_network_get_mode(struct supplicant_network *network); + struct supplicant_callbacks { - void (*interface_added) (const struct supplicant_interface *interface); - void (*interface_removed) (const struct supplicant_interface *interface); + void (*interface_added) (struct supplicant_interface *interface); + void (*interface_removed) (struct supplicant_interface *interface); + void (*network_added) (struct supplicant_network *network); + void (*network_removed) (struct supplicant_network *network); }; int supplicant_register(const struct supplicant_callbacks *callbacks); -- 2.7.4