5 * Copyright (C) 2007-2009 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
33 #include "supplicant-dbus.h"
34 #include "supplicant.h"
36 #define DBG(fmt, arg...) do { \
37 syslog(LOG_DEBUG, "%s() " fmt, __FUNCTION__ , ## arg); \
42 static DBusConnection *connection;
44 static const struct supplicant_callbacks *callbacks_pointer;
46 static unsigned int eap_methods;
51 } eap_method_map[] = {
52 { "MD5", SUPPLICANT_EAP_METHOD_MD5 },
53 { "TLS", SUPPLICANT_EAP_METHOD_TLS },
54 { "MSCHAPV2", SUPPLICANT_EAP_METHOD_MSCHAPV2 },
55 { "PEAP", SUPPLICANT_EAP_METHOD_PEAP },
56 { "TTLS", SUPPLICANT_EAP_METHOD_TTLS },
57 { "GTC", SUPPLICANT_EAP_METHOD_GTC },
58 { "OTP", SUPPLICANT_EAP_METHOD_OTP },
59 { "LEAP", SUPPLICANT_EAP_METHOD_LEAP },
67 { "active", SUPPLICANT_CAPABILITY_SCAN_ACTIVE },
68 { "passive", SUPPLICANT_CAPABILITY_SCAN_PASSIVE },
69 { "ssid", SUPPLICANT_CAPABILITY_SCAN_SSID },
73 static GHashTable *interface_table;
75 struct supplicant_interface {
77 unsigned int scan_capa;
78 enum supplicant_state state;
84 GHashTable *network_table;
85 GHashTable *bss_mapping;
88 struct supplicant_network {
89 struct supplicant_interface *interface;
92 enum supplicant_network_mode mode;
93 GHashTable *bss_table;
96 struct supplicant_bss {
97 struct supplicant_interface *interface;
99 unsigned char bssid[6];
100 unsigned char ssid[32];
101 unsigned int ssid_len;
102 unsigned int frequency;
105 static enum supplicant_state string2state(const char *state)
108 return SUPPLICANT_STATE_UNKNOWN;
110 if (g_str_equal(state, "unknown") == TRUE)
111 return SUPPLICANT_STATE_UNKNOWN;
112 else if (g_str_equal(state, "disconnected") == TRUE)
113 return SUPPLICANT_STATE_DISCONNECTED;
114 else if (g_str_equal(state, "inactive") == TRUE)
115 return SUPPLICANT_STATE_INACTIVE;
116 else if (g_str_equal(state, "scanning") == TRUE)
117 return SUPPLICANT_STATE_SCANNING;
118 else if (g_str_equal(state, "authenticating") == TRUE)
119 return SUPPLICANT_STATE_AUTHENTICATING;
120 else if (g_str_equal(state, "associating") == TRUE)
121 return SUPPLICANT_STATE_ASSOCIATING;
122 else if (g_str_equal(state, "associated") == TRUE)
123 return SUPPLICANT_STATE_ASSOCIATED;
124 else if (g_str_equal(state, "group_handshake") == TRUE)
125 return SUPPLICANT_STATE_GROUP_HANDSHAKE;
126 else if (g_str_equal(state, "4way_handshake") == TRUE)
127 return SUPPLICANT_STATE_4WAY_HANDSHAKE;
128 else if (g_str_equal(state, "completed") == TRUE)
129 return SUPPLICANT_STATE_COMPLETED;
131 return SUPPLICANT_STATE_UNKNOWN;
134 static void callback_interface_added(struct supplicant_interface *interface)
136 if (callbacks_pointer == NULL)
139 if (callbacks_pointer->interface_added == NULL)
142 callbacks_pointer->interface_added(interface);
145 static void callback_interface_removed(struct supplicant_interface *interface)
147 if (callbacks_pointer == NULL)
150 if (callbacks_pointer->interface_removed == NULL)
153 callbacks_pointer->interface_removed(interface);
156 static void callback_network_added(struct supplicant_network *network)
158 if (callbacks_pointer == NULL)
161 if (callbacks_pointer->network_added == NULL)
164 callbacks_pointer->network_added(network);
167 static void callback_network_removed(struct supplicant_network *network)
169 if (callbacks_pointer == NULL)
172 if (callbacks_pointer->network_removed == NULL)
175 callbacks_pointer->network_removed(network);
178 static void remove_interface(gpointer data)
180 struct supplicant_interface *interface = data;
182 callback_interface_removed(interface);
184 g_hash_table_destroy(interface->bss_mapping);
185 g_hash_table_destroy(interface->network_table);
187 g_free(interface->path);
188 g_free(interface->ifname);
189 g_free(interface->driver);
190 g_free(interface->bridge);
194 static void remove_network(gpointer data)
196 struct supplicant_network *network = data;
198 callback_network_removed(network);
200 g_free(network->group);
201 g_free(network->name);
205 static void remove_bss(gpointer data)
207 struct supplicant_bss *bss = data;
213 static void debug_eap_methods(void)
217 for (i = 0; eap_method_map[i].str != NULL; i++) {
218 if (eap_methods & eap_method_map[i].val)
219 DBG("EAP Method: %s", eap_method_map[i].str);
223 static void debug_scan_capabilities(struct supplicant_interface *interface)
227 for (i = 0; scan_capa_map[i].str != NULL; i++) {
228 if (interface->scan_capa & scan_capa_map[i].val)
229 DBG("Scan Capability: %s", scan_capa_map[i].str);
233 static void interface_capability_scan(DBusMessageIter *iter, void *user_data)
235 struct supplicant_interface *interface = user_data;
236 const char *str = NULL;
239 dbus_message_iter_get_basic(iter, &str);
243 for (i = 0; scan_capa_map[i].str != NULL; i++)
244 if (strcmp(str, scan_capa_map[i].str) == 0) {
245 interface->scan_capa |= scan_capa_map[i].val;
250 static void interface_capability(const char *key, DBusMessageIter *iter,
253 struct supplicant_interface *interface = user_data;
258 if (g_strcmp0(key, "Scan") == 0)
259 supplicant_dbus_array_foreach(iter, interface_capability_scan,
262 DBG("key %s type %c",
263 key, dbus_message_iter_get_arg_type(iter));
266 const char *supplicant_interface_get_ifname(struct supplicant_interface *interface)
268 if (interface == NULL)
271 return interface->ifname;
274 struct supplicant_interface *supplicant_network_get_interface(struct supplicant_network *network)
279 return network->interface;
282 const char *supplicant_network_get_name(struct supplicant_network *network)
284 if (network == NULL || network->name == NULL)
287 return network->name;
290 enum supplicant_network_mode supplicant_network_get_mode(struct supplicant_network *network)
293 return SUPPLICANT_NETWORK_MODE_UNKNOWN;
295 return network->mode;
298 static void network_property(const char *key, DBusMessageIter *iter,
304 DBG("key %s type %c", key, dbus_message_iter_get_arg_type(iter));
307 static void interface_network_added(DBusMessageIter *iter, void *user_data)
309 const char *path = NULL;
311 dbus_message_iter_get_basic(iter, &path);
315 supplicant_dbus_property_get_all(path,
316 SUPPLICANT_INTERFACE ".Interface.Network",
317 network_property, NULL);
320 static char *create_name(unsigned char *ssid, int ssid_len)
325 if (ssid_len < 1 || ssid[0] == '\0')
328 name = g_try_malloc0(ssid_len + 1);
333 for (i = 0; i < ssid_len; i++) {
334 if (g_ascii_isprint(ssid[i]))
343 static void add_bss_to_network(struct supplicant_bss *bss)
345 struct supplicant_interface *interface = bss->interface;
346 struct supplicant_network *network;
351 str = g_string_sized_new((bss->ssid_len * 2) + 24);
355 if (bss->ssid_len > 0 && bss->ssid[0] != '\0') {
356 for (i = 0; i < bss->ssid_len; i++)
357 g_string_append_printf(str, "%02x", bss->ssid[i]);
359 g_string_append_printf(str, "hidden");
361 group = g_string_free(str, FALSE);
363 network = g_hash_table_lookup(interface->network_table, group);
364 if (network != NULL) {
369 network = g_try_new0(struct supplicant_network, 1);
370 if (network == NULL) {
375 network->group = group;
376 network->name = create_name(bss->ssid, bss->ssid_len);
378 network->bss_table = g_hash_table_new_full(g_str_hash, g_str_equal,
381 g_hash_table_replace(interface->network_table,
382 network->group, network);
384 callback_network_added(network);
387 g_hash_table_replace(interface->bss_mapping, bss->path, network);
388 g_hash_table_replace(network->bss_table, bss->path, bss);
391 static void bss_property(const char *key, DBusMessageIter *iter,
394 struct supplicant_bss *bss = user_data;
396 if (bss->interface == NULL)
400 add_bss_to_network(bss);
404 if (g_strcmp0(key, "BSSID") == 0) {
405 DBusMessageIter array;
409 dbus_message_iter_recurse(iter, &array);
410 dbus_message_iter_get_fixed_array(&array, &addr, &addr_len);
413 memcpy(bss->bssid, addr, addr_len);
414 } else if (g_strcmp0(key, "SSID") == 0) {
415 DBusMessageIter array;
419 dbus_message_iter_recurse(iter, &array);
420 dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len);
422 if (ssid_len > 0 && ssid_len < 33) {
423 memcpy(bss->ssid, ssid, ssid_len);
424 bss->ssid_len = ssid_len;
426 memset(bss->ssid, 0, sizeof(bss->ssid));
429 } else if (g_strcmp0(key, "Capabilities") == 0) {
430 unsigned char capabilities = 0x00;
432 dbus_message_iter_get_basic(iter, &capabilities);
433 } else if (g_strcmp0(key, "Frequency") == 0) {
434 dbus_int32_t frequency = 0;
436 dbus_message_iter_get_basic(iter, &frequency);
437 bss->frequency = frequency;
438 } else if (g_strcmp0(key, "Level") == 0) {
439 dbus_int32_t level = 0;
441 dbus_message_iter_get_basic(iter, &level);
442 } else if (g_strcmp0(key, "MaxRate") == 0) {
443 dbus_int32_t maxrate = 0;
445 dbus_message_iter_get_basic(iter, &maxrate);
446 } else if (g_strcmp0(key, "RSNIE") == 0) {
447 DBusMessageIter array;
451 dbus_message_iter_recurse(iter, &array);
452 dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
453 } else if (g_strcmp0(key, "WPAIE") == 0) {
454 DBusMessageIter array;
458 dbus_message_iter_recurse(iter, &array);
459 dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
460 } else if (g_strcmp0(key, "WPSIE") == 0) {
461 DBusMessageIter array;
465 dbus_message_iter_recurse(iter, &array);
466 dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
468 DBG("key %s type %c",
469 key, dbus_message_iter_get_arg_type(iter));
472 static void interface_bss_added(DBusMessageIter *iter, void *user_data)
474 struct supplicant_interface *interface = user_data;
475 struct supplicant_network *network;
476 struct supplicant_bss *bss;
477 const char *path = NULL;
479 dbus_message_iter_get_basic(iter, &path);
483 network = g_hash_table_lookup(interface->bss_mapping, path);
484 if (network != NULL) {
485 bss = g_hash_table_lookup(network->bss_table, path);
490 bss = g_try_new0(struct supplicant_bss, 1);
494 bss->interface = interface;
495 bss->path = g_strdup(path);
497 supplicant_dbus_property_get_all(path,
498 SUPPLICANT_INTERFACE ".Interface.BSS",
502 static void interface_bss_removed(DBusMessageIter *iter, void *user_data)
504 struct supplicant_interface *interface = user_data;
505 struct supplicant_network *network;
506 const char *path = NULL;
508 dbus_message_iter_get_basic(iter, &path);
512 network = g_hash_table_lookup(interface->bss_mapping, path);
516 g_hash_table_remove(interface->bss_mapping, path);
517 g_hash_table_remove(network->bss_table, path);
519 if (g_hash_table_size(network->bss_table) == 0)
520 g_hash_table_remove(interface->network_table, network->group);
523 static void interface_property(const char *key, DBusMessageIter *iter,
526 struct supplicant_interface *interface = user_data;
528 if (interface == NULL)
532 debug_scan_capabilities(interface);
534 g_hash_table_replace(interface_table,
535 interface->path, interface);
537 callback_interface_added(interface);
541 if (g_strcmp0(key, "Capabilities") == 0) {
542 supplicant_dbus_property_foreach(iter, interface_capability,
544 } else if (g_strcmp0(key, "State") == 0) {
545 const char *str = NULL;
547 dbus_message_iter_get_basic(iter, &str);
549 interface->state = string2state(str);
550 } else if (g_strcmp0(key, "Scanning") == 0) {
551 dbus_bool_t scanning = FALSE;
553 dbus_message_iter_get_basic(iter, &scanning);
554 interface->scanning = scanning;
555 } else if (g_strcmp0(key, "ApScan") == 0) {
558 dbus_message_iter_get_basic(iter, &apscan);
559 interface->apscan = apscan;
560 } else if (g_strcmp0(key, "Ifname") == 0) {
561 const char *str = NULL;
563 dbus_message_iter_get_basic(iter, &str);
565 interface->ifname = g_strdup(str);
566 } else if (g_strcmp0(key, "Driver") == 0) {
567 const char *str = NULL;
569 dbus_message_iter_get_basic(iter, &str);
571 interface->driver = g_strdup(str);
572 } else if (g_strcmp0(key, "BridgeIfname") == 0) {
573 const char *str = NULL;
575 dbus_message_iter_get_basic(iter, &str);
577 interface->bridge = g_strdup(str);
578 } else if (g_strcmp0(key, "CurrentBSS") == 0) {
579 interface_bss_added(iter, interface);
580 } else if (g_strcmp0(key, "CurrentNetwork") == 0) {
581 interface_network_added(iter, interface);
582 } else if (g_strcmp0(key, "BSSs") == 0) {
583 supplicant_dbus_array_foreach(iter, interface_bss_added,
585 } else if (g_strcmp0(key, "Blobs") == 0) {
586 } else if (g_strcmp0(key, "Networks") == 0) {
587 supplicant_dbus_array_foreach(iter, interface_network_added,
590 DBG("key %s type %c",
591 key, dbus_message_iter_get_arg_type(iter));
594 static void interface_path(DBusMessageIter *iter, void *user_data)
596 struct supplicant_interface *interface;
597 const char *path = NULL;
599 dbus_message_iter_get_basic(iter, &path);
603 interface = g_hash_table_lookup(interface_table, path);
604 if (interface != NULL)
607 interface = g_try_new0(struct supplicant_interface, 1);
608 if (interface == NULL)
611 interface->path = g_strdup(path);
613 interface->network_table = g_hash_table_new_full(g_str_hash, g_str_equal,
614 NULL, remove_network);
616 interface->bss_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
619 supplicant_dbus_property_get_all(path,
620 SUPPLICANT_INTERFACE ".Interface",
621 interface_property, interface);
624 static void eap_method(DBusMessageIter *iter, void *user_data)
626 const char *str = NULL;
629 dbus_message_iter_get_basic(iter, &str);
633 for (i = 0; eap_method_map[i].str != NULL; i++)
634 if (strcmp(str, eap_method_map[i].str) == 0) {
635 eap_methods |= eap_method_map[i].val;
640 static void service_property(const char *key, DBusMessageIter *iter,
646 if (g_strcmp0(key, "Interfaces") == 0)
647 supplicant_dbus_array_foreach(iter, interface_path, user_data);
648 else if (g_strcmp0(key, "EapMethods") == 0) {
649 supplicant_dbus_array_foreach(iter, eap_method, user_data);
651 } else if (g_strcmp0(key, "DebugParams") == 0) {
655 static void signal_interface_added(const char *path, DBusMessageIter *iter)
657 interface_path(iter, NULL);
660 static void signal_interface_removed(const char *path, DBusMessageIter *iter)
662 DBG("path %s", path);
665 static void signal_bss_added(const char *path, DBusMessageIter *iter)
667 struct supplicant_interface *interface;
669 interface = g_hash_table_lookup(interface_table, path);
670 if (interface == NULL)
673 interface_bss_added(iter, interface);
676 static void signal_bss_removed(const char *path, DBusMessageIter *iter)
678 struct supplicant_interface *interface;
680 interface = g_hash_table_lookup(interface_table, path);
681 if (interface == NULL)
684 interface_bss_removed(iter, interface);
688 const char *interface;
690 void (*function) (const char *path, DBusMessageIter *iter);
692 { SUPPLICANT_INTERFACE, "InterfaceAdded", signal_interface_added },
693 { SUPPLICANT_INTERFACE, "InterfaceRemoved", signal_interface_removed },
694 { SUPPLICANT_INTERFACE ".Interface", "BSSAdded", signal_bss_added },
695 { SUPPLICANT_INTERFACE ".Interface", "BSSRemoved", signal_bss_removed },
699 static DBusHandlerResult supplicant_filter(DBusConnection *conn,
700 DBusMessage *message, void *data)
702 DBusMessageIter iter;
706 path = dbus_message_get_path(message);
708 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
710 if (dbus_message_iter_init(message, &iter) == FALSE)
711 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
713 for (i = 0; signal_map[i].interface != NULL; i++) {
714 if (dbus_message_has_interface(message,
715 signal_map[i].interface) == FALSE)
718 if (dbus_message_has_member(message,
719 signal_map[i].member) == FALSE)
722 signal_map[i].function(path, &iter);
726 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
729 static const char *supplicant_rule1 = "type=signal,"
730 "interface=" SUPPLICANT_INTERFACE;
731 static const char *supplicant_rule2 = "type=signal,"
732 "interface=" SUPPLICANT_INTERFACE ".Interface";
733 static const char *supplicant_rule3 = "type=signal,"
734 "interface=" SUPPLICANT_INTERFACE ".Interface.WPS";
735 static const char *supplicant_rule4 = "type=signal,"
736 "interface=" SUPPLICANT_INTERFACE ".Interface.BSS";
737 static const char *supplicant_rule5 = "type=signal,"
738 "interface=" SUPPLICANT_INTERFACE ".Interface.Network";
739 static const char *supplicant_rule6 = "type=signal,"
740 "interface=" SUPPLICANT_INTERFACE ".Interface.Blob";
742 int supplicant_register(const struct supplicant_callbacks *callbacks)
744 connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
745 if (connection == NULL)
748 if (dbus_connection_add_filter(connection,
749 supplicant_filter, NULL, NULL) == FALSE) {
750 dbus_connection_unref(connection);
755 callbacks_pointer = callbacks;
758 interface_table = g_hash_table_new_full(g_str_hash, g_str_equal,
759 NULL, remove_interface);
761 supplicant_dbus_setup(connection);
763 dbus_bus_add_match(connection, supplicant_rule1, NULL);
764 dbus_bus_add_match(connection, supplicant_rule2, NULL);
765 dbus_bus_add_match(connection, supplicant_rule3, NULL);
766 dbus_bus_add_match(connection, supplicant_rule4, NULL);
767 dbus_bus_add_match(connection, supplicant_rule5, NULL);
768 dbus_bus_add_match(connection, supplicant_rule6, NULL);
769 dbus_connection_flush(connection);
771 supplicant_dbus_property_get_all(SUPPLICANT_PATH,
772 SUPPLICANT_INTERFACE,
773 service_property, NULL);
778 void supplicant_unregister(const struct supplicant_callbacks *callbacks)
780 if (connection != NULL) {
781 dbus_bus_remove_match(connection, supplicant_rule6, NULL);
782 dbus_bus_remove_match(connection, supplicant_rule5, NULL);
783 dbus_bus_remove_match(connection, supplicant_rule4, NULL);
784 dbus_bus_remove_match(connection, supplicant_rule3, NULL);
785 dbus_bus_remove_match(connection, supplicant_rule2, NULL);
786 dbus_bus_remove_match(connection, supplicant_rule1, NULL);
787 dbus_connection_flush(connection);
789 dbus_connection_remove_filter(connection,
790 supplicant_filter, NULL);
793 if (interface_table != NULL) {
794 g_hash_table_destroy(interface_table);
795 interface_table = NULL;
798 if (connection != NULL) {
799 dbus_connection_unref(connection);
803 callbacks_pointer = NULL;