Change 802.11 plugin to use D-Bus interface of supplicant
authorMarcel Holtmann <marcel@holtmann.org>
Wed, 12 Mar 2008 13:51:59 +0000 (14:51 +0100)
committerMarcel Holtmann <marcel@holtmann.org>
Wed, 12 Mar 2008 13:51:59 +0000 (14:51 +0100)
plugins/80211.c
plugins/supplicant.c
plugins/supplicant.h

index 1bada4f..d21eec6 100644 (file)
 
 #include "supplicant.h"
 
-#ifndef IW_EV_LCP_PK_LEN
-#define IW_EV_LCP_PK_LEN       (4)
-#endif
-
-struct station_data {
-       char *address;
-       char *name;
-       int mode;
-       int qual;
-       int noise;
-       int level;
-
-       unsigned char wpa_ie[40];
-       int wpa_ie_len;
-       unsigned char rsn_ie[40];
-       int rsn_ie_len;
-
-       int has_wep;
-       int has_wpa;
-       int has_rsn;
-};
-
 struct iface_data {
        char ifname[IFNAMSIZ];
-       GSList *stations;
-
-       gchar *network;
-       gchar *passphrase;
 };
 
-static void report_station(struct connman_iface *iface,
-                                               struct station_data *station)
-{
-       int security = 0;
-
-       if (station == NULL)
-               return;
-
-       if (station->name == NULL)
-               return;
-
-       if (station->has_wep)
-               security |= 0x01;
-       if (station->has_wpa)
-               security |= 0x02;
-       if (station->has_rsn)
-               security |= 0x04;
-
-       connman_iface_indicate_station(iface, station->name,
-                                               station->qual, security);
-}
-
-static struct station_data *create_station(struct iface_data *iface,
-                                                       const char *address)
-{
-       struct station_data *station;
-       GSList *list;
-
-       for (list = iface->stations; list; list = list->next) {
-               station = list->data;
-
-               if (g_ascii_strcasecmp(station->address, address) == 0)
-                       return station;
-       }
-
-       station = g_try_new0(struct station_data, 1);
-       if (station == NULL)
-               return NULL;
-
-       station->address = g_strdup(address);
-       if (station->address == NULL) {
-               g_free(station);
-               return NULL;
-       }
-
-       iface->stations = g_slist_append(iface->stations, station);
-
-       return station;
-}
-
-static void load_stations(struct iface_data *iface)
-{
-       GKeyFile *keyfile;
-       gchar **groups, **group;
-       gsize length;
-
-       keyfile = g_key_file_new();
-
-       if (g_key_file_load_from_file(keyfile, "/tmp/stations.list",
-                               G_KEY_FILE_KEEP_COMMENTS, NULL) == FALSE)
-               goto done;
-
-       groups = g_key_file_get_groups(keyfile, &length);
-
-       for (group = groups; *group; group++) {
-               struct station_data *station;
-
-               station = create_station(iface, *group);
-               if (station == NULL)
-                       continue;
-
-               station->name = g_key_file_get_string(keyfile,
-                                               *group, "Name", NULL);
-       
-               station->mode = g_key_file_get_integer(keyfile,
-                                               *group, "Mode", NULL);
-       }
-
-       g_strfreev(groups);
-
-done:
-       g_key_file_free(keyfile);
-
-       printf("[802.11] loaded %d stations\n",
-                               g_slist_length(iface->stations));
-}
-
-static void print_stations(struct iface_data *iface)
-{
-       GKeyFile *keyfile;
-       gchar *data;
-       gsize length;
-       GSList *list;
-
-       keyfile = g_key_file_new();
-
-       for (list = iface->stations; list; list = list->next) {
-               struct station_data *station = list->data;
-
-               //printf("Address:%s Mode:%d ESSID:\"%s\" Quality:%d/100\n",
-               //                      station->address, station->mode,
-               //                              station->name, station->qual);
-
-               if (station->name == NULL)
-                       continue;
-
-               g_key_file_set_string(keyfile, station->address,
-                                               "Name", station->name);
-
-               g_key_file_set_integer(keyfile, station->address,
-                                               "Mode", station->mode);
-       }
-
-       data = g_key_file_to_data(keyfile, &length, NULL);
-
-       g_file_set_contents("/tmp/stations.list", data, length, NULL);
-
-       g_key_file_free(keyfile);
-}
-
 static int wifi_probe(struct connman_iface *iface)
 {
        struct iface_data *data;
@@ -231,8 +85,6 @@ static int wifi_probe(struct connman_iface *iface)
 
        connman_iface_set_data(iface, data);
 
-       load_stations(data);
-
        return 0;
 }
 
@@ -246,9 +98,6 @@ static void wifi_remove(struct connman_iface *iface)
 
        connman_iface_set_data(iface, NULL);
 
-       g_free(data->network);
-       g_free(data->passphrase);
-
        free(data);
 }
 
@@ -277,36 +126,12 @@ static int wifi_stop(struct connman_iface *iface)
 static int wifi_scan(struct connman_iface *iface)
 {
        struct iface_data *data = connman_iface_get_data(iface);
-       struct iwreq iwr;
-       struct iw_scan_req iws;
-       int sk, err;
 
        DBG("iface %p %s", iface, data->ifname);
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
-       if (sk < 0)
-               return -EIO;
-
-       memset(&iws, 0, sizeof(iws));
-       iws.scan_type = IW_SCAN_TYPE_PASSIVE;
-       //iws.scan_type = IW_SCAN_TYPE_ACTIVE;
-
-       memset(&iwr, 0, sizeof(iwr));
-       strncpy(iwr.ifr_name, data->ifname, IFNAMSIZ);
+       __supplicant_scan(iface);
 
-       iwr.u.data.pointer = (caddr_t ) &iws;
-       iwr.u.data.length = sizeof(iws);
-       iwr.u.data.flags = IW_SCAN_DEFAULT;
-
-       err = ioctl(sk, SIOCSIWSCAN, &iwr);
-
-       close(sk);
-
-       if (err < 0)
-               connman_error("%s: scan initiate error %d",
-                                               data->ifname, errno);
-
-       return err;
+       return 0;
 }
 
 static int wifi_connect(struct connman_iface *iface,
@@ -316,8 +141,7 @@ static int wifi_connect(struct connman_iface *iface,
 
        DBG("iface %p %s", iface, data->ifname);
 
-       if (data->network != NULL)
-               __supplicant_connect(iface, data->network, data->passphrase);
+       __supplicant_connect(iface, network->identifier, network->passphrase);
 
        return 0;
 }
@@ -328,256 +152,11 @@ static int wifi_disconnect(struct connman_iface *iface)
 
        DBG("iface %p %s", iface, data->ifname);
 
-       if (data->network != NULL)
-               __supplicant_disconnect(iface);
+       __supplicant_disconnect(iface);
 
        return 0;
 }
 
-static void wifi_set_network(struct connman_iface *iface,
-                                               const char *network)
-{
-       struct iface_data *data = connman_iface_get_data(iface);
-
-       DBG("iface %p %s", iface, data->ifname);
-
-       g_free(data->network);
-
-       data->network = g_strdup(network);
-}
-
-static void wifi_set_passphrase(struct connman_iface *iface,
-                                               const char *passphrase)
-{
-       struct iface_data *data = connman_iface_get_data(iface);
-
-       DBG("iface %p %s", iface, data->ifname);
-
-       g_free(data->passphrase);
-
-       data->passphrase = g_strdup(passphrase);
-}
-
-static void parse_genie(struct station_data *station,
-                                       unsigned char *data, int len)
-{
-       int offset = 0;
-
-       while (offset <= len - 2) {
-               //int i;
-
-               switch (data[offset]) {
-               case 0xdd:      /* WPA1 (and other) */
-                       station->has_wpa = 1;
-                       break;
-               case 0x30:      /* WPA2 (RSN) */
-                       station->has_rsn = 1;
-                       break;
-               default:
-                       break;
-               }
-
-               //for (i = 0; i < len; i++)
-               //      printf(" %02x", data[i]);
-               //printf("\n");
-
-               offset += data[offset + 1] + 2;
-       }
-}
-
-static void parse_scan_results(struct connman_iface *iface,
-                                       unsigned char *data, int len)
-{
-       unsigned char *ptr = data;
-       struct station_data *station = NULL;
-       struct ether_addr *eth;
-       char addr[18];
-       int num = 0;
-
-       while (len > IW_EV_LCP_PK_LEN) {
-               struct iw_event *event = (void *) ptr;
-
-               switch (event->cmd) {
-               case SIOCGIWAP:
-                       report_station(iface, station);
-                       eth = (void *) &event->u.ap_addr.sa_data;
-                       sprintf(addr, "%02X:%02X:%02X:%02X:%02X:%02X",
-                                               eth->ether_addr_octet[0],
-                                               eth->ether_addr_octet[1],
-                                               eth->ether_addr_octet[2],
-                                               eth->ether_addr_octet[3],
-                                               eth->ether_addr_octet[4],
-                                               eth->ether_addr_octet[5]);
-                       station = create_station(connman_iface_get_data(iface),
-                                                                       addr);
-                       num++;
-                       break;
-               case SIOCGIWESSID:
-                       if (station != NULL) {
-                               station->name = malloc(event->len - IW_EV_POINT_LEN + 1);
-                               if (station->name != NULL) {
-                                       memset(station->name, 0,
-                                               event->len - IW_EV_POINT_LEN + 1);
-                                       memcpy(station->name, ptr + IW_EV_POINT_LEN,
-                                               event->len - IW_EV_POINT_LEN);
-                               }
-                       }
-                       break;
-               case SIOCGIWNAME:
-                       break;
-               case SIOCGIWMODE:
-                       if (station != NULL)
-                               station->mode = event->u.mode;
-                       break;
-               case SIOCGIWFREQ:
-                       break;
-               case SIOCGIWENCODE:
-                       if (station != NULL) {
-                               if (!event->u.data.pointer)
-                                       event->u.data.flags |= IW_ENCODE_NOKEY;
-
-                               if (!(event->u.data.flags & IW_ENCODE_DISABLED))
-                                       station->has_wep = 1;
-                       }
-                       break;
-               case SIOCGIWRATE:
-                       break;
-               case IWEVQUAL:
-                       if (station != NULL) {
-                               station->qual = event->u.qual.qual;
-                               station->noise = event->u.qual.noise;
-                               station->level = event->u.qual.level;
-                       }
-                       break;
-               case IWEVGENIE:
-                       if (station != NULL)
-                               parse_genie(station, ptr + 8, event->len - 8);
-                       break;
-               case IWEVCUSTOM:
-                       break;
-               default:
-                       printf("[802.11] scan element 0x%04x (len %d)\n",
-                                               event->cmd, event->len);
-                       if (event->len == 0)
-                               len = 0;
-                       break;
-               }
-
-               ptr += event->len;
-               len -= event->len;
-       }
-
-       report_station(iface, station);
-
-       printf("[802.11] found %d networks\n", num);
-}
-
-static void scan_results(struct connman_iface *iface)
-{
-       struct iface_data *data = connman_iface_get_data(iface);
-       struct iwreq iwr;
-       void *buf;
-       size_t size;
-       int sk, err, done = 0;
-
-       if (data == NULL)
-               return;
-
-       memset(&iwr, 0, sizeof(iwr));
-       memcpy(iwr.ifr_name, data->ifname, IFNAMSIZ);
-
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
-       if (sk < 0)
-               return;
-
-       buf = NULL;
-       size = 1024;
-
-       while (!done) {
-               void *newbuf;
-
-               newbuf = g_realloc(buf, size);
-               if (newbuf == NULL) {
-                       close(sk);
-                       return;
-               }
-
-               buf = newbuf;
-               iwr.u.data.pointer = buf;
-               iwr.u.data.length = size;
-               iwr.u.data.flags = 0;
-
-               err = ioctl(sk, SIOCGIWSCAN, &iwr);
-               if (err < 0) {
-                       if (errno == E2BIG)
-                               size *= 2;
-                       else
-                               done = 1;
-               } else {
-                       parse_scan_results(iface, iwr.u.data.pointer,
-                                                       iwr.u.data.length);
-                       done = 1;
-               }
-       }
-
-       g_free(buf);
-
-       close(sk);
-
-       print_stations(data);
-}
-
-static void wifi_wireless(struct connman_iface *iface,
-                                       void *data, unsigned short len)
-{
-       struct iw_event *event = data;
-       struct iw_point point;
-       struct ether_addr *eth;
-       char addr[18];
-
-       switch (event->cmd) {
-       case SIOCSIWFREQ:
-               printf("[802.11] Set Frequency (flags %d)\n",
-                                                       event->u.freq.flags);
-               break;
-       case SIOCSIWMODE:
-               printf("[802.11] Set Mode (mode %d)\n", event->u.mode);
-               break;
-       case SIOCSIWESSID:
-               memcpy(&point, data + IW_EV_LCP_LEN -
-                                       IW_EV_POINT_OFF, sizeof(point));
-               point.pointer = data + IW_EV_LCP_LEN +
-                                       sizeof(point) - IW_EV_POINT_OFF;
-               printf("[802.11] Set ESSID (length %d flags %d) \"%s\"\n",
-                                       point.length, point.flags,
-                                               (char *) point.pointer);
-               break;
-       case SIOCSIWENCODE:
-               printf("[802.11] Set Encryption key (flags %d)\n",
-                                                       event->u.data.flags);
-               break;
-
-       case SIOCGIWAP:
-               eth = (void *) &event->u.ap_addr.sa_data;
-               sprintf(addr, "%02X:%02X:%02X:%02X:%02X:%02X",
-                                               eth->ether_addr_octet[0],
-                                               eth->ether_addr_octet[1],
-                                               eth->ether_addr_octet[2],
-                                               eth->ether_addr_octet[3],
-                                               eth->ether_addr_octet[4],
-                                               eth->ether_addr_octet[5]);
-               printf("[802.11] New Access Point %s\n", addr);
-               break;
-       case SIOCGIWSCAN:
-               scan_results(iface);
-               break;
-       default:
-               printf("[802.11] Wireless event (cmd 0x%04x len %d)\n",
-                                               event->cmd, event->len);
-               break;
-       }
-}
-
 static struct connman_iface_driver wifi_driver = {
        .name           = "80211",
        .capability     = "net.80211",
@@ -588,9 +167,6 @@ static struct connman_iface_driver wifi_driver = {
        .scan           = wifi_scan,
        .connect        = wifi_connect,
        .disconnect     = wifi_disconnect,
-       .set_network    = wifi_set_network,
-       .set_passphrase = wifi_set_passphrase,
-       .rtnl_wireless  = wifi_wireless,
 };
 
 static int wifi_init(void)
index b30b16c..c8853e1 100644 (file)
 #include <sys/un.h>
 #include <net/if.h>
 
-#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include <connman/log.h>
 
 #include "supplicant.h"
 
+enum supplicant_state {
+       STATE_INACTIVE,
+       STATE_SCANNING,
+       STATE_ASSOCIATING,
+       STATE_ASSOCIATED,
+       STATE_4WAY_HANDSHAKE,
+       STATE_GROUP_HANDSHAKE,
+       STATE_COMPLETED,
+       STATE_DISCONNECTED,
+};
+
+// COMPLETED       ==> ASSOCIATING
+// ASSOCIATED      ==> DISCONNECTED
+// DISCONNECTED    ==> INACTIVE
+
+// DISCONNECTED    ==> SCANNING
+// SCANNING        ==> ASSOCIATED
+
+// ASSOCIATING     ==> ASSOCIATED
+// ASSOCIATED      ==> 4WAY_HANDSHAKE
+// 4WAY_HANDSHAKE  ==> GROUP_HANDSHAKE
+// GROUP_HANDSHAKE ==> COMPLETED
+
 struct supplicant_task {
-       GPid pid;
+       DBusConnection *conn;
        int ifindex;
-       char *ifname;
+       gchar *ifname;
        struct connman_iface *iface;
-       int socket;
-       GIOChannel *channel;
+       gchar *path;
+       gboolean created;
+       gchar *network;
+       enum supplicant_state state;
 };
 
 static GSList *tasks = NULL;
 
+struct supplicant_ap {
+       gchar *identifier;
+       GByteArray *ssid;
+       guint capabilities;
+       gboolean has_wep;
+       gboolean has_wpa;
+       gboolean has_rsn;
+};
+
+#define IEEE80211_CAP_ESS       0x0001
+#define IEEE80211_CAP_IBSS      0x0002
+#define IEEE80211_CAP_PRIVACY   0x0010
+
 static struct supplicant_task *find_task(int ifindex)
 {
        GSList *list;
@@ -62,95 +103,715 @@ static struct supplicant_task *find_task(int ifindex)
        return NULL;
 }
 
-static int exec_cmd(struct supplicant_task *task, char *cmd)
+static int get_interface(struct supplicant_task *task)
 {
-       return write(task->socket, cmd, strlen(cmd));
+       DBusMessage *message, *reply;
+       DBusError error;
+       const char *path;
+
+       DBG("task %p", task);
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH,
+                                       SUPPLICANT_INTF, "getInterface");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &task->ifname,
+                                                       DBUS_TYPE_INVALID);
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(task->conn,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to get interface");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_message_unref(message);
+
+       dbus_error_init(&error);
+
+       if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for interface");
+               dbus_message_unref(reply);
+               return -EIO;
+       }
+
+       DBG("path %s", path);
+
+       task->path = g_strdup(path);
+       task->created = FALSE;
+
+       dbus_message_unref(reply);
+
+       return 0;
 }
 
-static gboolean control_event(GIOChannel *chan,
-                               GIOCondition cond, gpointer data)
+static int add_interface(struct supplicant_task *task)
 {
-       struct supplicant_task *task = data;
-       char buf[256];
-       gsize len;
-       GIOError err;
+       DBusMessage *message, *reply;
+       DBusError error;
+       const char *path;
+
+       DBG("task %p", task);
 
-       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
-               return FALSE;
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH,
+                                       SUPPLICANT_INTF, "addInterface");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_error_init(&error);
 
-       memset(buf, 0, sizeof(buf));
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &task->ifname,
+                                                       DBUS_TYPE_INVALID);
 
-       err = g_io_channel_read(chan, buf, sizeof(buf), &len);
-       if (err) {
-               if (err == G_IO_ERROR_AGAIN)
-                       return TRUE;
-               return FALSE;
+       reply = dbus_connection_send_with_reply_and_block(task->conn,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to add interface");
+               dbus_message_unref(message);
+               return -EIO;
        }
 
-       if (buf[0] != '<')
-               return TRUE;
+       dbus_message_unref(message);
 
-       printf("[SUPPLICANT] %s\n", buf + 3);
+       dbus_error_init(&error);
 
-       if (g_str_has_prefix(buf + 3, "CTRL-EVENT-CONNECTED") == TRUE) {
-               printf("[SUPPLICANT] connected\n");
-               connman_iface_indicate_connected(task->iface);
+       if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for interface");
+               dbus_message_unref(reply);
+               return -EIO;
        }
 
-       if (g_str_has_prefix(buf + 3, "CTRL-EVENT-DISCONNECTED") == TRUE) {
-               printf("[SUPPLICANT] disconnected\n");
+       DBG("path %s", path);
+
+       task->path = g_strdup(path);
+       task->created = TRUE;
+
+       dbus_message_unref(reply);
+
+       return 0;
+}
+
+static int add_network(struct supplicant_task *task)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+       const char *path;
+
+       DBG("task %p", task);
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
+                               SUPPLICANT_INTF ".Interface", "addNetwork");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(task->conn,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to add network");
+               dbus_message_unref(message);
+               return -EIO;
        }
 
-       if (g_str_has_prefix(buf + 3, "CTRL-EVENT-TERMINATING") == TRUE) {
-               printf("[SUPPLICANT] terminating\n");
+       dbus_message_unref(message);
+
+       dbus_error_init(&error);
+
+       if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for network");
+               dbus_message_unref(reply);
+               return -EIO;
        }
 
-       return TRUE;
+       DBG("path %s", path);
+
+       task->network = g_strdup(path);
+
+       dbus_message_unref(reply);
+
+       return 0;
 }
 
-static int open_control(struct supplicant_task *task)
+static int remove_network(struct supplicant_task *task)
 {
-       struct sockaddr_un addr;
-       int sk;
+       DBusMessage *message, *reply;
+       DBusError error;
 
-       printf("[SUPPLICANT] open control for %s\n", task->ifname);
+       DBG("task %p", task);
 
-       sk = socket(PF_UNIX, SOCK_DGRAM, 0);
-       if (sk < 0)
-               return -1;
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
+                               SUPPLICANT_INTF ".Interface", "removeNetwork");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->network,
+                                                       DBUS_TYPE_INVALID);
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(task->conn,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to remove network");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_message_unref(message);
+
+       dbus_message_unref(reply);
+
+       return 0;
+}
+
+static int select_network(struct supplicant_task *task)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
 
-       memset(&addr, 0, sizeof(addr));
-       addr.sun_family = AF_UNIX;
-       snprintf(addr.sun_path, sizeof(addr.sun_path),
-                                       "%s/%s.cli", STATEDIR, task->ifname);
-       //unlink(addr.sun_path);
+       DBG("task %p", task);
 
-       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               close(sk);
-               return -1;
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
+                               SUPPLICANT_INTF ".Interface", "selectNetwork");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->network,
+                                                       DBUS_TYPE_INVALID);
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(task->conn,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to select network");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_message_unref(message);
+
+       dbus_message_unref(reply);
+
+       return 0;
+}
+
+static int enable_network(struct supplicant_task *task)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+
+       DBG("task %p", task);
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, task->network,
+                                       SUPPLICANT_INTF ".Network", "enable");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(task->conn,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to enable network");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_message_unref(message);
+
+       dbus_message_unref(reply);
+
+       return 0;
+}
+
+static int disable_network(struct supplicant_task *task)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+
+       DBG("task %p", task);
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, task->network,
+                                       SUPPLICANT_INTF ".Network", "disable");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(task->conn,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to disable network");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_message_unref(message);
+
+       dbus_message_unref(reply);
+
+       return 0;
+}
+
+static void append_entry(DBusMessageIter *dict,
+                               const char *key, int type, void *val)
+{
+       DBusMessageIter entry, value;
+       const char *signature;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       switch (type) {
+       case DBUS_TYPE_STRING:
+               signature = DBUS_TYPE_STRING_AS_STRING;
+               break;
+       case DBUS_TYPE_UINT16:
+               signature = DBUS_TYPE_UINT16_AS_STRING;
+               break;
+       default:
+               signature = DBUS_TYPE_VARIANT_AS_STRING;
+               break;
        }
 
-       memset(&addr, 0, sizeof(addr));
-       addr.sun_family = AF_UNIX;
-       snprintf(addr.sun_path, sizeof(addr.sun_path),
-                                       "%s/%s", STATEDIR, task->ifname);
+       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+                                                       signature, &value);
+       dbus_message_iter_append_basic(&value, type, val);
+       dbus_message_iter_close_container(&entry, &value);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
 
-       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               close(sk);
-               return -1;
+static int set_network(struct supplicant_task *task, const char *network,
+                                               const char *passphrase)
+{
+       DBusMessage *message, *reply;
+       DBusMessageIter array, dict;
+       DBusError error;
+
+       DBG("task %p", task);
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, task->network,
+                                       SUPPLICANT_INTF ".Network", "set");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &array);
+
+       dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       append_entry(&dict, "ssid", DBUS_TYPE_STRING, &network);
+
+       if (passphrase && strlen(passphrase) > 0) {
+               //exec_cmd(task, "SET_NETWORK 0 proto RSN WPA");
+               //exec_cmd(task, "SET_NETWORK 0 key_mgmt WPA-PSK");
+
+               append_entry(&dict, "psk", DBUS_TYPE_STRING, &passphrase);
+       } else {
+               //exec_cmd(task, "SET_NETWORK 0 proto RSN WPA");
+               //exec_cmd(task, "SET_NETWORK 0 key_mgmt NONE");
        }
 
-       task->socket = sk;
+       dbus_message_iter_close_container(&array, &dict);
 
-       task->channel = g_io_channel_unix_new(sk);
-       g_io_channel_set_close_on_unref(task->channel, TRUE);
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(task->conn,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to set network options");
+               dbus_message_unref(message);
+               return -EIO;
+       }
 
-       g_io_add_watch(task->channel,
-                       G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
-                                               control_event, task);
+       dbus_message_unref(message);
 
-       exec_cmd(task, "ATTACH");
-       exec_cmd(task, "ADD_NETWORK");
+       dbus_message_unref(reply);
+
+       return 0;
+}
+
+static int initiate_scan(struct supplicant_task *task)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+
+       DBG("task %p", task);
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
+                                       SUPPLICANT_INTF ".Interface", "scan");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(task->conn,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to initiate scan");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_message_unref(message);
+
+       dbus_message_unref(reply);
+
+       return 0;
+}
+
+static void extract_ssid(struct supplicant_ap *ap, DBusMessageIter *value)
+{
+       DBusMessageIter array;
+       unsigned char *ssid;
+       int ssid_len;
+
+       dbus_message_iter_recurse(value, &array);
+       dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len);
+
+       ap->identifier = g_strdup((char *) ssid);
+}
+
+static void extract_wpaie(struct supplicant_ap *ap, DBusMessageIter *value)
+{
+       DBusMessageIter array;
+       unsigned char *ie;
+       int ie_len;
+
+       dbus_message_iter_recurse(value, &array);
+       dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
+
+       if (ie_len > 0)
+               ap->has_wpa = TRUE;
+}
+
+static void extract_rsnie(struct supplicant_ap *ap, DBusMessageIter *value)
+{
+       DBusMessageIter array;
+       unsigned char *ie;
+       int ie_len;
+
+       dbus_message_iter_recurse(value, &array);
+       dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
+
+       if (ie_len > 0)
+               ap->has_rsn = TRUE;
+}
+
+static void extract_capabilites(struct supplicant_ap *ap,
+                                               DBusMessageIter *value)
+{
+       guint capabilities;
+
+       dbus_message_iter_get_basic(value, &capabilities);
+
+       ap->capabilities = capabilities;
+
+       if (capabilities & IEEE80211_CAP_PRIVACY)
+               ap->has_wep = TRUE;
+}
+
+static int parse_network_properties(struct supplicant_task *task,
+                                                       DBusMessage *message)
+{
+       DBusMessageIter array, dict;
+       struct supplicant_ap *ap;
+       int security = 0;
+
+       DBG("task %p", task);
+
+       ap = g_try_new0(struct supplicant_ap, 1);
+       if (ap == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init(message, &array);
+
+       dbus_message_iter_recurse(&array, &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);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+
+               dbus_message_iter_recurse(&entry, &value);
+
+               //type = dbus_message_iter_get_arg_type(&value);
+               //dbus_message_iter_get_basic(&value, &val);
+
+               if (g_str_equal(key, "ssid") == TRUE)
+                       extract_ssid(ap, &value);
+               else if (g_str_equal(key, "wpaie") == TRUE)
+                       extract_wpaie(ap, &value);
+               else if (g_str_equal(key, "rsnie") == TRUE)
+                       extract_rsnie(ap, &value);
+               else if (g_str_equal(key, "capabilities") == TRUE)
+                       extract_capabilites(ap, &value);
+
+               dbus_message_iter_next(&dict);
+       }
+
+       DBG("SSID %s", ap->identifier);
+
+       if (ap->has_wep)
+               security |= 0x01;
+       if (ap->has_wpa)
+               security |= 0x02;
+       if (ap->has_rsn)
+               security |= 0x04;
+
+       connman_iface_indicate_station(task->iface,
+                                       ap->identifier, 25, security);
+
+       g_free(ap);
+
+       return 0;
+}
+
+static int get_network_properties(struct supplicant_task *task,
+                                                       const char *path)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+
+       DBG("task %p", task);
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, path,
+                                               SUPPLICANT_INTF ".BSSID",
+                                                               "properties");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(task->conn,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to get network properties");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_message_unref(message);
+
+       parse_network_properties(task, reply);
+
+       dbus_message_unref(reply);
+
+       return 0;
+}
+
+static int scan_results_available(struct supplicant_task *task)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+       char **results;
+       int i, num_results;
+
+       DBG("task %p", task);
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
+                                               SUPPLICANT_INTF ".Interface",
+                                                       "scanResults");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(task->conn,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to request scan result");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_message_unref(message);
+
+       dbus_error_init(&error);
+
+       if (dbus_message_get_args(reply, &error,
+                               DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
+                                               &results, &num_results,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for scan result");
+               dbus_message_unref(reply);
+               return -EIO;
+       }
+
+       for (i = 0; i < num_results; i++)
+               get_network_properties(task, results[i]);
+
+       g_strfreev(results);
+
+       dbus_message_unref(reply);
+
+       return 0;
+}
+
+static void state_change(struct supplicant_task *task, DBusMessage *msg)
+{
+       DBusError error;
+       const char *state, *previous;
+
+       dbus_error_init(&error);
+
+       if (dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &state,
+                                               DBUS_TYPE_STRING, &previous,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for state change");
+               return;
+       }
+
+       DBG("state %s ==> %s", previous, state);
+
+       if (g_str_equal(state, "INACTIVE") == TRUE)
+               task->state = STATE_INACTIVE;
+       else if (g_str_equal(state, "SCANNING") == TRUE)
+               task->state = STATE_SCANNING;
+       else if (g_str_equal(state, "ASSOCIATING") == TRUE)
+               task->state = STATE_ASSOCIATING;
+       else if (g_str_equal(state, "ASSOCIATED") == TRUE)
+               task->state = STATE_ASSOCIATED;
+       else if (g_str_equal(state, "GROUP_HANDSHAKE") == TRUE)
+               task->state = STATE_4WAY_HANDSHAKE;
+       else if (g_str_equal(state, "4WAY_HANDSHAKE") == TRUE)
+               task->state = STATE_4WAY_HANDSHAKE;
+       else if (g_str_equal(state, "COMPLETED") == TRUE)
+               task->state = STATE_COMPLETED;
+       else if (g_str_equal(state, "DISCONNECTED") == TRUE)
+               task->state = STATE_DISCONNECTED;
+}
+
+static DBusHandlerResult supplicant_filter(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct supplicant_task *task = data;
+       const char *member;
+
+       if (dbus_message_has_interface(msg,
+                               SUPPLICANT_INTF ".Interface") == FALSE)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       member = dbus_message_get_member(msg);
+       if (member == NULL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       DBG("task %p member %s", task, member);
+
+       if (g_str_equal(member, "ScanResultsAvailable") == TRUE)
+               scan_results_available(task);
+       else if (g_str_equal(member, "StateChange") == TRUE)
+               state_change(task, msg);
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static int add_filter(struct supplicant_task *task)
+{
+       DBusError error;
+       gchar *filter;
+
+       if (dbus_connection_add_filter(task->conn,
+                               supplicant_filter, task, NULL) == FALSE)
+               return -EIO;
+
+       filter = g_strdup_printf("type=signal,interface=%s.Interface,path=%s",
+                                               SUPPLICANT_INTF, task->path);
+
+       DBG("filter %s", filter);
+
+       dbus_error_init(&error);
+
+       dbus_bus_add_match(task->conn, filter, &error);
+
+       g_free(filter);
+
+       if (dbus_error_is_set(&error) == TRUE) {
+               connman_error("Can't add match: %s", error.message);
+               dbus_error_free(&error);
+       }
 
        return 0;
 }
@@ -159,7 +820,6 @@ int __supplicant_start(struct connman_iface *iface)
 {
        struct ifreq ifr;
        struct supplicant_task *task;
-       char *argv[9];
        int sk, err;
 
        sk = socket(PF_INET, SOCK_DGRAM, 0);
@@ -176,14 +836,14 @@ int __supplicant_start(struct connman_iface *iface)
        if (err < 0)
                return -EIO;
 
-       printf("[SUPPLICANT] start %s\n", ifr.ifr_name);
+       DBG("interface %s", ifr.ifr_name);
 
        task = g_try_new0(struct supplicant_task, 1);
        if (task == NULL)
                return -ENOMEM;
 
        task->ifindex = iface->index;
-       task->ifname = strdup(ifr.ifr_name);
+       task->ifname = g_strdup(ifr.ifr_name);
        task->iface = iface;
 
        if (task->ifname == NULL) {
@@ -191,32 +851,33 @@ int __supplicant_start(struct connman_iface *iface)
                return -ENOMEM;
        }
 
-       argv[0] = "/sbin/wpa_supplicant";
-       argv[1] = "-qq";
-       argv[2] = "-C";
-       argv[3] = STATEDIR;
-       argv[4] = "-D";
-       argv[5] = "wext";
-       argv[6] = "-i";
-       argv[7] = task->ifname;
-       argv[8] = NULL;
-
-       if (g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
-                               NULL, NULL, &task->pid, NULL) == FALSE) {
-               printf("Failed to spawn wpa_supplicant\n");
-               return -1;
+       task->conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (task->conn == NULL) {
+               g_free(task);
+               return -EIO;
+       }
+
+       task->created = FALSE;
+
+       err = get_interface(task);
+       if (err < 0) {
+               err = add_interface(task);
+               if (err < 0) {
+                       g_free(task);
+                       return err;
+               }
        }
 
-       tasks = g_slist_append(tasks, task);
+       task->state = STATE_INACTIVE;
 
-       printf("[SUPPLICANT] executed with pid %d\n", task->pid);
+       tasks = g_slist_append(tasks, task);
 
-       sleep(1);
+       add_filter(task);
 
-       task->socket = -1;
+       add_network(task);
 
-       if (open_control(task) < 0)
-               printf("[SUPPLICANT] control failed\n");
+       select_network(task);
+       disable_network(task);
 
        return 0;
 }
@@ -224,33 +885,51 @@ int __supplicant_start(struct connman_iface *iface)
 int __supplicant_stop(struct connman_iface *iface)
 {
        struct supplicant_task *task;
-       char pathname[PATH_MAX];
 
        task = find_task(iface->index);
        if (task == NULL)
                return -ENODEV;
 
-       printf("[SUPPLICANT] stop %s\n", task->ifname);
+       DBG("interface %s", task->ifname);
 
        tasks = g_slist_remove(tasks, task);
 
-       exec_cmd(task, "DISABLE_NETWORK 0");
-       exec_cmd(task, "DETACH");
+       remove_network(task);
 
-       sleep(1);
+       dbus_connection_unref(task->conn);
+
+       g_free(task->ifname);
+       g_free(task->network);
+       g_free(task->path);
+       g_free(task);
 
-       kill(task->pid, SIGTERM);
+       return 0;
+}
 
-       g_io_channel_shutdown(task->channel, TRUE, NULL);
-       g_io_channel_unref(task->channel);
+int __supplicant_scan(struct connman_iface *iface)
+{
+       struct supplicant_task *task;
+       int err;
 
-       snprintf(pathname, sizeof(pathname),
-                                       "%s/%s.cli", STATEDIR, task->ifname);
-       unlink(pathname);
+       task = find_task(iface->index);
+       if (task == NULL)
+               return -ENODEV;
 
-       free(task->ifname);
+       DBG("interface %s", task->ifname);
+
+       switch (task->state) {
+       case STATE_SCANNING:
+               return -EALREADY;
+       case STATE_ASSOCIATING:
+       case STATE_ASSOCIATED:
+       case STATE_4WAY_HANDSHAKE:
+       case STATE_GROUP_HANDSHAKE:
+               return -EBUSY;
+       default:
+               break;
+       }
 
-       g_free(task);
+       err = initiate_scan(task);
 
        return 0;
 }
@@ -259,34 +938,16 @@ int __supplicant_connect(struct connman_iface *iface,
                                const char *network, const char *passphrase)
 {
        struct supplicant_task *task;
-       char cmd[128];
 
        task = find_task(iface->index);
        if (task == NULL)
                return -ENODEV;
 
-       printf("[SUPPLICANT] connect %s\n", task->ifname);
-
-       exec_cmd(task, "DISABLE_NETWORK 0");
-
-       if (network == NULL)
-               return 0;
-
-       sprintf(cmd, "SET_NETWORK 0 ssid \"%s\"", network);
-       exec_cmd(task, cmd);
+       DBG("interface %s", task->ifname);
 
-       if (passphrase && strlen(passphrase) > 0) {
-               exec_cmd(task, "SET_NETWORK 0 proto RSN WPA");
-               exec_cmd(task, "SET_NETWORK 0 key_mgmt WPA-PSK");
-
-               sprintf(cmd, "SET_NETWORK 0 psk \"%s\"", passphrase);
-               exec_cmd(task, cmd);
-       } else {
-               exec_cmd(task, "SET_NETWORK 0 proto RSN WPA");
-               exec_cmd(task, "SET_NETWORK 0 key_mgmt NONE");
-       }
+       set_network(task, network, passphrase);
 
-       exec_cmd(task, "ENABLE_NETWORK 0");
+       enable_network(task);
 
        return 0;
 }
@@ -299,9 +960,9 @@ int __supplicant_disconnect(struct connman_iface *iface)
        if (task == NULL)
                return -ENODEV;
 
-       printf("[SUPPLICANT] disconnect %s\n", task->ifname);
+       DBG("interface %s", task->ifname);
 
-       exec_cmd(task, "DISABLE_NETWORK 0");
+       disable_network(task);
 
        return 0;
 }
index dc015ed..59e4dfe 100644 (file)
 
 #include <connman/iface.h>
 
+#define SUPPLICANT_NAME  "fi.epitest.hostap.WPASupplicant"
+#define SUPPLICANT_INTF  "fi.epitest.hostap.WPASupplicant"
+#define SUPPLICANT_PATH  "/fi/epitest/hostap/WPASupplicant"
+
 int __supplicant_start(struct connman_iface *iface);
 int __supplicant_stop(struct connman_iface *iface);
 
+int __supplicant_scan(struct connman_iface *iface);
+
 int __supplicant_connect(struct connman_iface *iface,
                                const char *network, const char *passphrase);
 int __supplicant_disconnect(struct connman_iface *iface);