summaryrefslogtreecommitdiffstats
path: root/src/plugins/wireless
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/wireless')
-rw-r--r--src/plugins/wireless/plugin.c148
-rw-r--r--src/plugins/wireless/wpa_ctrl.c238
-rw-r--r--src/plugins/wireless/wpa_ctrl.h179
3 files changed, 565 insertions, 0 deletions
diff --git a/src/plugins/wireless/plugin.c b/src/plugins/wireless/plugin.c
new file mode 100644
index 0000000..1ddeb95
--- /dev/null
+++ b/src/plugins/wireless/plugin.c
@@ -0,0 +1,148 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include "wpa_ctrl.h"
+#include "plugin.h"
+
+#define WIRELESS_PATH PLUGIN_ROOT_PATH "/wireless"
+#define WIRELESS_INTERFACE PLUGIN_ROOT_INTF ".wireless"
+
+static int init(plugin_t *plugin, DBusConnection *connection);
+static void deinit(plugin_t *plugin, DBusConnection *connection);
+static void handle_incoming(plugin_t *plugin, DBusConnection *connection);
+static DBusHandlerResult handle_message(DBusConnection *connection, DBusMessage *message, plugin_t *plugin);
+static DBusHandlerResult select_network(DBusConnection *connection, DBusMessage *message, plugin_t *plugin);
+static DBusHandlerResult wpa_supplicant_request(DBusConnection *connection, DBusMessage *message, plugin_t *plugin, char *cmd);
+
+static int init(plugin_t *plugin, DBusConnection *connection) {
+ struct wpa_ctrl *ctrl_conn = NULL;
+ const char *ctrl_iface_dir = "/var/run/wpa_supplicant";
+ DIR *dir = opendir(ctrl_iface_dir);
+
+ if (dir) {
+ struct dirent *dent;
+ while ((dent = readdir(dir))) {
+ char *cfile;
+ int flen;
+ if (strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0)
+ continue;
+ printf("Selected interface '%s'\n",
+ dent->d_name);
+ flen = strlen(ctrl_iface_dir) + strlen(dent->d_name) + 2;
+ cfile = malloc(flen);
+ if (cfile == NULL)
+ return -1;
+ snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, dent->d_name);
+ ctrl_conn = wpa_ctrl_open(cfile);
+ free(cfile);
+ break;
+ }
+ closedir(dir);
+ }
+
+ if (ctrl_conn != NULL) {
+ plugin->fd = wpa_ctrl_get_fd(ctrl_conn);
+ } else {
+ /* do not fail, the plugin will try to re-init when needed */
+ plugin->fd = -1;
+ }
+ plugin->priv = (void *) ctrl_conn;
+ return 0;
+}
+
+static void deinit(plugin_t *plugin, DBusConnection *connection) {
+ if (plugin->fd > 0)
+ close(plugin->fd);
+}
+
+static void handle_incoming(plugin_t *plugin, DBusConnection *connection) {
+ struct wpa_ctrl *ctrl_conn = (struct wpa_ctrl *) plugin->priv;
+ char buf[2048];
+ size_t len;
+ wpa_ctrl_recv(ctrl_conn, buf, &len);
+ buf[len] = '\0';
+ printf("received event: %s\n", buf);
+}
+
+static DBusHandlerResult handle_message(DBusConnection *connection, DBusMessage *message, plugin_t *plugin) {
+ if (dbus_message_is_method_call(message, WIRELESS_INTERFACE, "ScanResults")) {
+ return wpa_supplicant_request(connection, message, plugin, "SCAN_RESULTS");
+ } else if (dbus_message_is_method_call(message, WIRELESS_INTERFACE, "ListNetworks")) {
+ return wpa_supplicant_request(connection, message, plugin, "LIST_NETWORKS");
+ } else if (dbus_message_is_method_call(message, WIRELESS_INTERFACE, "SelectNetwork")) {
+ return select_network(connection, message, plugin);
+ }
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult select_network(DBusConnection *connection, DBusMessage *message, plugin_t *plugin) {
+ DBusError error;
+ u_int32_t net;
+ char cmd[32];
+
+ dbus_error_init (&error);
+ if (!dbus_message_get_args (message,
+ &error,
+ DBUS_TYPE_UINT32,
+ &net,
+ DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "select_network(): failed to read D-BUS message args: %s\n", error.message);
+ dbus_error_free (&error);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ dbus_error_free (&error);
+
+ snprintf(cmd, sizeof(cmd), "SELECT_NETWORK %u", net);
+ return wpa_supplicant_request(connection, message, plugin, cmd);
+}
+
+static DBusHandlerResult wpa_supplicant_request(DBusConnection *connection, DBusMessage *message, plugin_t *plugin, char *cmd) {
+ struct wpa_ctrl *ctrl_conn = (struct wpa_ctrl *) plugin->priv;
+ DBusMessage *reply;
+ char buf[2048];
+ size_t len;
+ int ret = -1;
+
+ len = sizeof(buf) - 1;
+ if (ctrl_conn) {
+ ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, &len, NULL);
+ }
+ if (ret == -1) {
+ fprintf(stderr, "connection to wpa_supplicant daemon lost, reconnecting\n");
+ init(plugin, connection);
+ ctrl_conn = (struct wpa_ctrl *) plugin->priv;
+ if (ctrl_conn) {
+ ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, &len, NULL);
+ }
+ }
+ if (ret != 0) {
+ fprintf(stderr, "unable to request command\n");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ buf[len] = '\0';
+
+ reply = dbus_message_new_method_return(message);
+ dbus_message_append_args(reply,
+ DBUS_TYPE_STRING,
+ buf,
+ DBUS_TYPE_INVALID);
+ dbus_connection_send(connection, reply, NULL);
+ dbus_connection_flush(connection);
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+plugin_t wpa_supplicant_plugin = {
+ .name = "Wireless",
+ .path = WIRELESS_PATH,
+ .init = init,
+ .handle_incoming = handle_incoming,
+ .handle_message = handle_message,
+ .deinit = deinit,
+};
diff --git a/src/plugins/wireless/wpa_ctrl.c b/src/plugins/wireless/wpa_ctrl.c
new file mode 100644
index 0000000..1c88e69
--- /dev/null
+++ b/src/plugins/wireless/wpa_ctrl.c
@@ -0,0 +1,238 @@
+/*
+ * wpa_supplicant/hostapd control interface library
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#include "wpa_ctrl.h"
+#ifdef CONFIG_NATIVE_WINDOWS
+#include "common.h"
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+/**
+ * struct wpa_ctrl - Internal structure for control interface library
+ *
+ * This structure is used by the wpa_supplicant/hostapd control interface
+ * library to store internal data. Programs using the library should not touch
+ * this data directly. They can only use the pointer to the data structure as
+ * an identifier for the control interface connection and use this as one of
+ * the arguments for most of the control interface library functions.
+ */
+struct wpa_ctrl {
+ int s;
+#ifdef CONFIG_CTRL_IFACE_UDP
+ struct sockaddr_in local;
+ struct sockaddr_in dest;
+#else /* CONFIG_CTRL_IFACE_UDP */
+ struct sockaddr_un local;
+ struct sockaddr_un dest;
+#endif /* CONFIG_CTRL_IFACE_UDP */
+};
+
+
+struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
+{
+ struct wpa_ctrl *ctrl;
+#ifndef CONFIG_CTRL_IFACE_UDP
+ static int counter = 0;
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+ ctrl = malloc(sizeof(*ctrl));
+ if (ctrl == NULL)
+ return NULL;
+ memset(ctrl, 0, sizeof(*ctrl));
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+ ctrl->s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (ctrl->s < 0) {
+ perror("socket");
+ free(ctrl);
+ return NULL;
+ }
+
+ ctrl->local.sin_family = AF_INET;
+ ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1);
+ if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
+ sizeof(ctrl->local)) < 0) {
+ close(ctrl->s);
+ free(ctrl);
+ return NULL;
+ }
+
+ ctrl->dest.sin_family = AF_INET;
+ ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1);
+ ctrl->dest.sin_port = htons(9877);
+ if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
+ sizeof(ctrl->dest)) < 0) {
+ perror("connect");
+ close(ctrl->s);
+ free(ctrl);
+ return NULL;
+ }
+#else /* CONFIG_CTRL_IFACE_UDP */
+ ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (ctrl->s < 0) {
+ free(ctrl);
+ return NULL;
+ }
+
+ ctrl->local.sun_family = AF_UNIX;
+ snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path),
+ "/tmp/wpa_ctrl_%d-%d", getpid(), counter++);
+ if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
+ sizeof(ctrl->local)) < 0) {
+ close(ctrl->s);
+ free(ctrl);
+ return NULL;
+ }
+
+ ctrl->dest.sun_family = AF_UNIX;
+ snprintf(ctrl->dest.sun_path, sizeof(ctrl->dest.sun_path), "%s",
+ ctrl_path);
+ if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
+ sizeof(ctrl->dest)) < 0) {
+ close(ctrl->s);
+ unlink(ctrl->local.sun_path);
+ free(ctrl);
+ return NULL;
+ }
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+ return ctrl;
+}
+
+
+void wpa_ctrl_close(struct wpa_ctrl *ctrl)
+{
+#ifndef CONFIG_CTRL_IFACE_UDP
+ unlink(ctrl->local.sun_path);
+#endif /* CONFIG_CTRL_IFACE_UDP */
+ close(ctrl->s);
+ free(ctrl);
+}
+
+
+int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
+ char *reply, size_t *reply_len,
+ void (*msg_cb)(char *msg, size_t len))
+{
+ struct timeval tv;
+ int res;
+ fd_set rfds;
+
+ if (send(ctrl->s, cmd, cmd_len, 0) < 0)
+ return -1;
+
+ for (;;) {
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ FD_ZERO(&rfds);
+ FD_SET(ctrl->s, &rfds);
+ res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
+ if (FD_ISSET(ctrl->s, &rfds)) {
+ res = recv(ctrl->s, reply, *reply_len, 0);
+ if (res < 0)
+ return res;
+ if (res > 0 && reply[0] == '<') {
+ /* This is an unsolicited message from
+ * wpa_supplicant, not the reply to the
+ * request. Use msg_cb to report this to the
+ * caller. */
+ if (msg_cb) {
+ /* Make sure the message is nul
+ * terminated. */
+ if ((size_t) res == *reply_len)
+ res = (*reply_len) - 1;
+ reply[res] = '\0';
+ msg_cb(reply, res);
+ }
+ continue;
+ }
+ *reply_len = res;
+ break;
+ } else {
+ return -2;
+ }
+ }
+ return 0;
+}
+
+
+static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach)
+{
+ char buf[10];
+ int ret;
+ size_t len = 10;
+
+ ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6,
+ buf, &len, NULL);
+ if (ret < 0)
+ return ret;
+ if (len == 3 && memcmp(buf, "OK\n", 3) == 0)
+ return 0;
+ return -1;
+}
+
+
+int wpa_ctrl_attach(struct wpa_ctrl *ctrl)
+{
+ return wpa_ctrl_attach_helper(ctrl, 1);
+}
+
+
+int wpa_ctrl_detach(struct wpa_ctrl *ctrl)
+{
+ return wpa_ctrl_attach_helper(ctrl, 0);
+}
+
+
+int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)
+{
+ int res;
+
+ res = recv(ctrl->s, reply, *reply_len, 0);
+ if (res < 0)
+ return res;
+ *reply_len = res;
+ return 0;
+}
+
+
+int wpa_ctrl_pending(struct wpa_ctrl *ctrl)
+{
+ struct timeval tv;
+ int res;
+ fd_set rfds;
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ FD_ZERO(&rfds);
+ FD_SET(ctrl->s, &rfds);
+ res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
+ return FD_ISSET(ctrl->s, &rfds);
+}
+
+
+int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl)
+{
+ return ctrl->s;
+}
diff --git a/src/plugins/wireless/wpa_ctrl.h b/src/plugins/wireless/wpa_ctrl.h
new file mode 100644
index 0000000..964f7ab
--- /dev/null
+++ b/src/plugins/wireless/wpa_ctrl.h
@@ -0,0 +1,179 @@
+/*
+ * wpa_supplicant/hostapd control interface library
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPA_CTRL_H
+#define WPA_CTRL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* wpa_supplicant control interface - fixed message prefixes */
+
+/** Interactive request for identity/password/pin */
+#define WPA_CTRL_REQ "CTRL-REQ-"
+
+/** Response to identity/password/pin request */
+#define WPA_CTRL_RSP "CTRL-RSP-"
+
+/* Event messages with fixed prefix */
+/** Authentication completed successfully and data connection enabled */
+#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED "
+/** Disconnected, data connection is not available */
+#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED "
+/** wpa_supplicant is exiting */
+#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING "
+/** Password change was completed successfully */
+#define WPA_EVENT_PASSWORD_CHANGED "CTRL-EVENT-PASSWORD-CHANGED "
+/** EAP-Request/Notification received */
+#define WPA_EVENT_EAP_NOTIFICATION "CTRL-EVENT-EAP-NOTIFICATION "
+/** EAP authentication started (EAP-Request/Identity received) */
+#define WPA_EVENT_EAP_STARTED "CTRL-EVENT-EAP-STARTED "
+/** EAP method selected */
+#define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD "
+/** EAP authentication completed successfully */
+#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS "
+/** EAP authentication failed (EAP-Failure received) */
+#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE "
+
+
+/* wpa_supplicant/hostapd control interface access */
+
+/**
+ * wpa_ctrl_open - Open a control interface to wpa_supplicant/hostapd
+ * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used.
+ * Returns: Pointer to abstract control interface data or %NULL on failure
+ *
+ * This function is used to open a control interface to wpa_supplicant/hostapd.
+ * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd. This path
+ * is configured in wpa_supplicant/hostapd and other programs using the control
+ * interface need to use matching path configuration.
+ */
+struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);
+
+
+/**
+ * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ *
+ * This function is used to close a control interface.
+ */
+void wpa_ctrl_close(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_request - Send a command to wpa_supplicant/hostapd
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * @cmd: Command; usually, ASCII text, e.g., "PING"
+ * @cmd_len: Length of the cmd in bytes
+ * @reply: Buffer for the response
+ * @reply_len: Reply buffer length
+ * @msg_cb: Callback function for unsolicited messages or %NULL if not used
+ * Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout
+ *
+ * This function is used to send commands to wpa_supplicant/hostapd. Received
+ * response will be written to reply and reply_len is set to the actual length
+ * of the reply. This function will block for up to two seconds while waiting
+ * for the reply. If unsolicited messages are received, the blocking time may
+ * be longer.
+ *
+ * msg_cb can be used to register a callback function that will be called for
+ * unsolicited messages received while waiting for the command response. These
+ * messages may be received if wpa_ctrl_request() is called at the same time as
+ * wpa_supplicant/hostapd is sending such a message. This can happen only if
+ * the program has used wpa_ctrl_attach() to register itself as a monitor for
+ * event messages. Alternatively to msg_cb, programs can register two control
+ * interface connections and use one of them for commands and the other one for
+ * receiving event messages, in other words, call wpa_ctrl_attach() only for
+ * the control interface connection that will be used for event messages.
+ */
+int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
+ char *reply, size_t *reply_len,
+ void (*msg_cb)(char *msg, size_t len));
+
+
+/**
+ * wpa_ctrl_attach - Register as an event monitor for the control interface
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: 0 on success, -1 on failure, -2 on timeout
+ *
+ * This function registers the control interface connection as a monitor for
+ * wpa_supplicant/hostapd events. After a success wpa_ctrl_attach() call, the
+ * control interface connection starts receiving event messages that can be
+ * read with wpa_ctrl_recv().
+ */
+int wpa_ctrl_attach(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_detach - Unregister event monitor from the control interface
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: 0 on success, -1 on failure, -2 on timeout
+ *
+ * This function unregisters the control interface connection as a monitor for
+ * wpa_supplicant/hostapd events, i.e., cancels the registration done with
+ * wpa_ctrl_attach().
+ */
+int wpa_ctrl_detach(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_recv - Receive a pending control interface message
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * @reply: Buffer for the message data
+ * @reply_len: Length of the reply buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function will receive a pending control interface message. This
+ * function will block if no messages are available. The received response will
+ * be written to reply and reply_len is set to the actual length of the reply.
+ * wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach()
+ * must have been used to register the control interface as an event monitor.
+ */
+int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);
+
+
+/**
+ * wpa_ctrl_pending - Check whether there are pending event messages
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: Non-zero if there are pending messages
+ *
+ * This function will check whether there are any pending control interface
+ * message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is
+ * only used for event messages, i.e., wpa_ctrl_attach() must have been used to
+ * register the control interface as an event monitor.
+ */
+int wpa_ctrl_pending(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_get_fd - Get file descriptor used by the control interface
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: File descriptor used for the connection
+ *
+ * This function can be used to get the file descriptor that is used for the
+ * control interface connection. The returned value can be used, e.g., with
+ * select() while waiting for multiple events.
+ *
+ * The returned file descriptor must not be used directly for sending or
+ * receiving packets; instead, the library functions wpa_ctrl_request() and
+ * wpa_ctrl_recv() must be used for this.
+ */
+int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WPA_CTRL_H */