From: Vibhav Pant vibhavp@gmail.com
--- dlls/wlanapi/dbus.c | 290 ++++++++++++++++++++++++++++++++++++ dlls/wlanapi/main.c | 72 ++++++++- dlls/wlanapi/unixlib.c | 70 ++++++++- dlls/wlanapi/unixlib.h | 31 ++++ dlls/wlanapi/unixlib_priv.h | 7 + 5 files changed, 465 insertions(+), 5 deletions(-)
diff --git a/dlls/wlanapi/dbus.c b/dlls/wlanapi/dbus.c index 8a6b5e5b5bb..dca8156afe5 100644 --- a/dlls/wlanapi/dbus.c +++ b/dlls/wlanapi/dbus.c @@ -24,6 +24,7 @@
#include "config.h"
+#include <stdlib.h> #include <dlfcn.h>
#ifdef SONAME_LIBDBUS_1 @@ -33,9 +34,13 @@ #include <ntstatus.h> #define WIN32_NO_STATUS #include <winternl.h> +#include <wlanapi.h>
#include <wine/debug.h> +#include <wine/list.h>
+#include "unixlib.h" +#include "unixlib_priv.h" #include "dbus.h"
WINE_DEFAULT_DEBUG_CHANNEL( wlanapi ); @@ -46,6 +51,11 @@ WINE_DEFAULT_DEBUG_CHANNEL( wlanapi ); DBUS_FUNCS; #undef DO_FUNC
+#define NETWORKMANAGER_SERVICE "org.freedesktop.NetworkManager" + +#define NETWORKMANAGER_INTERFACE_MANAGER "org.freedesktop.NetworkManager" +#define NETWORKMANAGER_INTERFACE_DEVICE "org.freedesktop.NetworkManager.Device" + BOOL load_dbus_functions( void ) { void *handle = dlopen( SONAME_LIBDBUS_1, RTLD_NOW ); @@ -94,6 +104,286 @@ void close_dbus_connection( void *c ) p_dbus_connection_close( c ); p_dbus_connection_unref( c ); } + +const static int dbus_timeout = -1; + + +static NTSTATUS dbus_error_to_ntstatus( const DBusError *error ) +{ + +#define DBUS_ERROR_CASE(n, s) if(p_dbus_error_has_name( error, (n)) ) return (s) + + DBUS_ERROR_CASE( DBUS_ERROR_UNKNOWN_OBJECT, STATUS_INVALID_PARAMETER ); + DBUS_ERROR_CASE( DBUS_ERROR_NO_MEMORY, STATUS_NO_MEMORY ); + DBUS_ERROR_CASE( DBUS_ERROR_NOT_SUPPORTED, STATUS_NOT_SUPPORTED ); + DBUS_ERROR_CASE( DBUS_ERROR_ACCESS_DENIED, STATUS_ACCESS_DENIED ); + return STATUS_INTERNAL_ERROR; +#undef DBUS_ERROR_CASE +} + +#define NM_DEVICE_TYPE_WIFI 2 + +static BOOL networkmanager_device_is_wifi( void *connection, const char *object_path ) +{ + DBusMessage *request, *reply; + DBusError error; + DBusMessageIter iter, variant; + dbus_bool_t success; + BOOL is_wifi; + const char *device_iface = NETWORKMANAGER_INTERFACE_DEVICE; + const char *devicetype_prop = "DeviceType"; + + request = p_dbus_message_new_method_call( NETWORKMANAGER_SERVICE, object_path, + DBUS_INTERFACE_PROPERTIES, "Get" ); + if (!request) return FALSE; + success = p_dbus_message_append_args( request, DBUS_TYPE_STRING, &device_iface, + DBUS_TYPE_STRING, &devicetype_prop, DBUS_TYPE_INVALID ); + if (!success) + { + p_dbus_message_unref( request ); + return FALSE; + } + + p_dbus_error_init( &error ); + reply = p_dbus_connection_send_with_reply_and_block( connection, request, dbus_timeout, &error ); + p_dbus_message_unref( request ); + if (!reply) + { + p_dbus_error_free( &error ); + return FALSE; + } + p_dbus_error_free( &error ); + p_dbus_error_init( &error ); + + p_dbus_message_iter_init( reply, &iter ); + p_dbus_message_iter_recurse( &iter, &variant ); + if (p_dbus_message_iter_get_arg_type( &variant) == DBUS_TYPE_UINT32) + { + dbus_uint32_t device_type; + + p_dbus_message_iter_get_basic( &variant, &device_type ); + is_wifi = device_type == NM_DEVICE_TYPE_WIFI; + } + else + { + ERR( "Unexpected signature for property DeviceType: %c\n", + p_dbus_message_iter_get_arg_type( &variant ) ); + is_wifi = FALSE; + } + + p_dbus_error_free( &error ); + p_dbus_message_unref( reply ); + + return is_wifi; +} + +/* NetworkManager device objects do not have any UUID-like propertiy which we could use to + deterministically derive an interface GUID for Win32. However, all device objects have + the object path prefix "/org/freedesktop/NetworkManager/Devices/" followed by a numerical + index. We use index as the last 4 bytes of this GUID to create a Win32 WLAN interface GUID. */ +const static GUID NETWORKMANAGER_DEVICE_BASE_INTERFACE_GUID = { + 0xa53634f7, 0xc1bc, 0x4d41, { 0xbc, 0x06, 0xd3, 0xf7, 0x00, 0x00, 0x00, 0x00 } }; + +static BOOL networkmanager_device_path_to_guid( const char *object_path, GUID *guid ) +{ + const static char device_prefix[] = "/org/freedesktop/NetworkManager/Devices/"; + BOOL is_device = strncmp( object_path, device_prefix, sizeof( device_prefix ) ); + UINT32 idx; + + if (!is_device) return FALSE; + idx = atoi( object_path + sizeof(device_prefix) - 1 ); + if (!idx) /* NetworkManager doesn't seem to use 0 as an index for devices. */ + { + ERR( "Could not parse index from device path %s:\n", debugstr_a( object_path )); + return FALSE; + } + + *guid = NETWORKMANAGER_DEVICE_BASE_INTERFACE_GUID; + memcpy( &guid->Data4[4], &idx, 4 ); + + return TRUE; +} + +static const char *dbus_next_dict_entry( DBusMessageIter *iter, DBusMessageIter *variant ) +{ + DBusMessageIter sub; + const char *name; + + if (p_dbus_message_iter_get_arg_type( iter ) != DBUS_TYPE_DICT_ENTRY) + return NULL; + + p_dbus_message_iter_recurse( iter, &sub ); + p_dbus_message_iter_next( iter ); + p_dbus_message_iter_get_basic( &sub, &name ); + p_dbus_message_iter_next( &sub ); + p_dbus_message_iter_recurse( &sub, variant ); + return name; +} + +#define NM_DEVICE_STATE_UNKNOWN 0 +#define NM_DEVICE_STATE_UNMANAGED 10 +#define NM_DEVICE_STATE_UNAVAILABLE 20 +#define NM_DEVICE_STATE_DISCONNECTED 30 +#define NM_DEVICE_STATE_PREPARE 40 +#define NM_DEVICE_STATE_CONFIG 50 +#define NM_DEVICE_STATE_NEED_AUTH 60 +#define NM_DEVICE_STATE_IP_CONFIG 70 +#define NM_DEVICE_STATE_IP_CHECK 80 +#define NM_DEVICE_STATE_SECONDARIES 90 +#define NM_DEVICE_STATE_ACTIVATED 100 +#define NM_DEVICE_STATE_DEACTIVATING 110 +#define NM_DEVICE_STATE_FAILED 120 + +static BOOL networkmanager_wifi_device_get_info( void *connection, const char *object_path, + struct unix_wlan_interface_info *info ) +{ + DBusMessage *request, *reply; + DBusError error; + DBusMessageIter dict, prop_iter, variant; + const char *prop_name; + const char *device_iface = NETWORKMANAGER_INTERFACE_DEVICE; + + if (!networkmanager_device_path_to_guid( object_path, &info->guid )) return FALSE; + request = p_dbus_message_new_method_call( NETWORKMANAGER_SERVICE, object_path, + DBUS_INTERFACE_PROPERTIES, "GetAll" ); + if (!request) return FALSE; + + p_dbus_message_append_args( request, DBUS_TYPE_STRING, &device_iface, DBUS_TYPE_INVALID ); + p_dbus_error_init( &error ); + reply = p_dbus_connection_send_with_reply_and_block( connection, request, dbus_timeout, &error ); + p_dbus_message_unref( request ); + if (!reply) + { + ERR( "Could not get properties for %s: %s: %s.\n", debugstr_a( object_path ), error.name, + error.message ); + p_dbus_error_free( &error ); + return FALSE; + } + + p_dbus_error_free( &error ); + p_dbus_message_iter_init( reply, &dict ); + p_dbus_message_iter_recurse( &dict, &prop_iter ); + while ((prop_name = dbus_next_dict_entry( &prop_iter, &variant ))) + { + if (!strcmp( prop_name, "Interface" ) && + p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_STRING) + { + const char *interface; + size_t len; + + p_dbus_message_iter_get_basic( &variant, &interface ); + len = min( strlen( interface ), sizeof( info->description ) - 1 ); + memcpy( info->description, interface, len); + info->description[len] = '\0'; + } + else if (!strcmp( prop_name, "State" ) && + p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_UINT32) + { + dbus_uint32_t state; + + p_dbus_message_iter_get_basic( &variant, &state ); + switch (state) + { + case NM_DEVICE_STATE_UNKNOWN: + case NM_DEVICE_STATE_UNMANAGED: + case NM_DEVICE_STATE_UNAVAILABLE: + case NM_DEVICE_STATE_FAILED: + case NM_DEVICE_STATE_SECONDARIES: + info->state = wlan_interface_state_not_ready; + break; + case NM_DEVICE_STATE_CONFIG: + case NM_DEVICE_STATE_PREPARE: + info->state = wlan_interface_state_associating; + break; + case NM_DEVICE_STATE_IP_CONFIG: + case NM_DEVICE_STATE_IP_CHECK: + info->state = wlan_interface_state_discovering; + break; + case NM_DEVICE_STATE_NEED_AUTH: + info->state = wlan_interface_state_authenticating; + break; + case NM_DEVICE_STATE_ACTIVATED: + info->state = wlan_interface_state_connected; + break; + case NM_DEVICE_STATE_DEACTIVATING: + info->state = wlan_interface_state_disconnecting; + break; + case NM_DEVICE_STATE_DISCONNECTED: + info->state = wlan_interface_state_disconnected; + break; + default: + FIXME( "Unknown NMDeviceState value %d\n", (int)state ); + info->state = wlan_interface_state_not_ready; + break; + } + } + } + p_dbus_message_unref(reply); + return TRUE; +} + +NTSTATUS networkmanager_get_wifi_devices( void *connection, struct list *devices ) +{ + DBusMessage *request, *reply; + char **object_paths = NULL; + int n_objects, i; + dbus_bool_t success; + DBusError error; + + request = + p_dbus_message_new_method_call( NETWORKMANAGER_SERVICE, "/org/freedesktop/NetworkManager", + NETWORKMANAGER_INTERFACE_MANAGER, "GetDevices" ); + if (!request) return STATUS_NO_MEMORY; + + p_dbus_error_init( &error ); + reply = + p_dbus_connection_send_with_reply_and_block( connection, request, dbus_timeout, &error ); + p_dbus_message_unref( request ); + if (!reply) + { + NTSTATUS ret = dbus_error_to_ntstatus( &error ); + ERR( "Could not get list of network devices: %s: %s.\n", debugstr_a( error.name ), + debugstr_a( error.message ) ); + p_dbus_error_free( &error ); + return ret; + } + + p_dbus_error_free( &error ); + p_dbus_error_init( &error ); + success = p_dbus_message_get_args( reply, &error, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, + &object_paths, &n_objects, DBUS_TYPE_INVALID ); + if (!success) + { + NTSTATUS ret = dbus_error_to_ntstatus( &error ); + ERR( "Could not read object paths from GetDevices reply: %s: %s.\n", + debugstr_a( error.name ), debugstr_a( error.message ) ); + p_dbus_error_free( &error ); + p_dbus_message_unref( reply ); + return ret; + } + + p_dbus_error_free( &error ); + for (i = 0; i < n_objects; i++) + { + const char *object_path = object_paths[i]; + struct unix_wlan_interface_info info = {0}; + + if (networkmanager_device_is_wifi( connection, object_path ) && + networkmanager_device_path_to_guid( object_path, &info.guid ) && + networkmanager_wifi_device_get_info( connection, object_path, &info )) + { + struct wlan_interface *entry = malloc( sizeof(*entry) ); + if (!entry) continue; + entry->info = info; + list_add_head( devices, &entry->entry ); + } + } + + p_dbus_free_string_array( object_paths ); + p_dbus_message_unref( reply ); + return STATUS_SUCCESS; +} + #else /* SONAME_LIBDBUS_1 */ BOOL load_dbus_functions( void ) { return FALSE; } NTSTATUS init_dbus_connection( UINT_PTR *handle ) { return STATUS_NOT_SUPPORTED; } diff --git a/dlls/wlanapi/main.c b/dlls/wlanapi/main.c index 6335088973c..8c47b7244d0 100644 --- a/dlls/wlanapi/main.c +++ b/dlls/wlanapi/main.c @@ -27,12 +27,14 @@ */
#include <stdarg.h> +#include <stdlib.h>
#include <ntstatus.h> #define WIN32_NO_STATUS
#include "windef.h" #include "winbase.h" + #include "wine/debug.h" #include "wine/unixlib.h"
@@ -79,9 +81,12 @@ static HANDLE handle_new(struct wine_wlan **entry) DWORD WINAPI WlanEnumInterfaces(HANDLE handle, void *reserved, WLAN_INTERFACE_INFO_LIST **interface_list) { struct wine_wlan *wlan; + struct wlan_get_interfaces_params args = {0}; + SIZE_T count, list_size; WLAN_INTERFACE_INFO_LIST *ret_list; + NTSTATUS status;
- FIXME("(%p, %p, %p) semi-stub\n", handle, reserved, interface_list); + TRACE( "(%p, %p, %p)\n", handle, reserved, interface_list );
if (!handle || reserved || !interface_list) return ERROR_INVALID_PARAMETER; @@ -90,12 +95,71 @@ DWORD WINAPI WlanEnumInterfaces(HANDLE handle, void *reserved, WLAN_INTERFACE_IN if (!wlan) return ERROR_INVALID_HANDLE;
- ret_list = WlanAllocateMemory(sizeof(WLAN_INTERFACE_INFO_LIST)); + args.handle = wlan->unix_handle; + status = UNIX_WLAN_CALL( wlan_get_interfaces, &args ); + if (status == STATUS_NOT_SUPPORTED) + { + count = -1; + } + else if (status != STATUS_SUCCESS) + { + ERR( "Could not get list of interfaces from host: %lx.\n", status ); + return RtlNtStatusToDosError( status ); + } + else + count = args.len; + + list_size = offsetof(WLAN_INTERFACE_INFO_LIST, InterfaceInfo[count > 1 ? count : 1]); + ret_list = WlanAllocateMemory(list_size); if (!ret_list) + { + if (count != -1) + { + struct wlan_free_interfaces_params free_args = {0}; + + free_args.interfaces = args.interfaces; + UNIX_WLAN_CALL( wlan_free_interfaces, &free_args ); + } return ERROR_NOT_ENOUGH_MEMORY; + }
- memset(&ret_list->InterfaceInfo[0], 0, sizeof(WLAN_INTERFACE_INFO)); - ret_list->dwNumberOfItems = 0; + memset( ret_list, 0, list_size ); + if (count != -1) + { + struct wlan_copy_and_free_interfaces_params copy_args = {0}; + struct unix_wlan_interface_info *unix_ifaces = NULL; + SIZE_T i; + + unix_ifaces = malloc( sizeof( *unix_ifaces ) * count ); + if (!unix_ifaces) + { + struct wlan_free_interfaces_params free_args = {0}; + + free_args.interfaces = args.interfaces; + UNIX_WLAN_CALL( wlan_free_interfaces, &free_args ); + WlanFreeMemory( ret_list ); + return ERROR_NOT_ENOUGH_MEMORY; + } + + copy_args.info = unix_ifaces; + copy_args.interfaces = args.interfaces; + UNIX_WLAN_CALL( wlan_copy_and_free_interfaces, ©_args ); + + for (i = 0; i < count; i++) + { + const size_t desc_max = + sizeof( ret_list->InterfaceInfo[i].strInterfaceDescription ) / sizeof( WCHAR ); + + ret_list->InterfaceInfo[i].InterfaceGuid = unix_ifaces[i].guid; + ret_list->InterfaceInfo[i].isState = unix_ifaces[i].state; + + mbstowcs( ret_list->InterfaceInfo[i].strInterfaceDescription, + unix_ifaces[i].description, desc_max ); + ret_list->InterfaceInfo[i].strInterfaceDescription[desc_max - 1] = '\0'; + } + free( unix_ifaces ); + } + ret_list->dwNumberOfItems = count != -1 ? count : 0; ret_list->dwIndex = 0; /* unused in this function */ *interface_list = ret_list;
diff --git a/dlls/wlanapi/unixlib.c b/dlls/wlanapi/unixlib.c index 92311ffb856..8888f620ed0 100644 --- a/dlls/wlanapi/unixlib.c +++ b/dlls/wlanapi/unixlib.c @@ -24,10 +24,14 @@
#include "config.h"
+#include <stdlib.h> + #include <ntstatus.h> #define WIN32_NO_STATUS #include <winternl.h> +#include <wlanapi.h>
+#include <wine/list.h> #include <wine/unixlib.h>
#include "unixlib.h" @@ -62,10 +66,74 @@ NTSTATUS wlan_close_handle( void *params ) return STATUS_SUCCESS; }
+NTSTATUS wlan_get_interfaces( void *params ) +{ + struct wlan_get_interfaces_params *args = params; + struct list *ifaces; + NTSTATUS status; + + if (!initialized) + return STATUS_NOT_SUPPORTED; + + ifaces = malloc( sizeof( *ifaces ) ); + if (!ifaces) return STATUS_NO_MEMORY; + + list_init( ifaces ); + status = networkmanager_get_wifi_devices( (void *)args->handle, ifaces ); + if (status != STATUS_SUCCESS) + { + free( ifaces ); + return status; + } + + args->interfaces = (UINT_PTR)ifaces; + args->len = list_count( ifaces ); + return STATUS_SUCCESS; +} + +NTSTATUS wlan_copy_and_free_interfaces( void *params ) +{ + struct wlan_copy_and_free_interfaces_params *args = params; + struct wlan_interface *ifaces = (struct wlan_interface *)args->interfaces; + struct wlan_interface *cur, *next; + SIZE_T i = 0; + + LIST_FOR_EACH_ENTRY_SAFE(cur, next, &ifaces->entry, struct wlan_interface, entry) + { + args->info[i++] = cur->info; + list_remove( &cur->entry ); + free( cur ); + } + + free( ifaces ); + return STATUS_SUCCESS; +} + +NTSTATUS wlan_free_interfaces( void *params ) +{ + struct wlan_free_interfaces_params *args = params; + struct wlan_interface *ifaces = (struct wlan_interface *)args->interfaces; + struct wlan_interface *cur, *next; + + LIST_FOR_EACH_ENTRY_SAFE(cur, next, &ifaces->entry, struct wlan_interface, entry) + { + list_remove( &cur->entry ); + free( cur ); + } + + free( ifaces ); + return STATUS_SUCCESS; +} + const unixlib_entry_t __wine_unix_call_funcs[] = { wlan_init, wlan_open_handle, - wlan_close_handle + wlan_close_handle, + + wlan_get_interfaces, + wlan_copy_and_free_interfaces, + + wlan_free_interfaces, };
C_ASSERT( ARRAYSIZE( __wine_unix_call_funcs ) == unix_funcs_count ); diff --git a/dlls/wlanapi/unixlib.h b/dlls/wlanapi/unixlib.h index 9bf2149e7c3..0f9f41ffb3d 100644 --- a/dlls/wlanapi/unixlib.h +++ b/dlls/wlanapi/unixlib.h @@ -33,6 +33,33 @@ struct wlan_close_handle_params UINT_PTR handle; };
+struct wlan_get_interfaces_params +{ + UINT_PTR handle; + + UINT_PTR interfaces; + SIZE_T len; +}; + +struct unix_wlan_interface_info +{ + GUID guid; + CHAR description[256]; + WLAN_INTERFACE_STATE state; +}; + +struct wlan_copy_and_free_interfaces_params +{ + UINT_PTR interfaces; + + struct unix_wlan_interface_info *info; +}; + +struct wlan_free_interfaces_params +{ + UINT_PTR interfaces; +}; + enum wlanpi_funcs { unix_wlan_init, @@ -40,6 +67,10 @@ enum wlanpi_funcs unix_wlan_open_handle, unix_wlan_close_handle,
+ unix_wlan_get_interfaces, + unix_wlan_copy_and_free_interfaces, + unix_wlan_free_interfaces, + unix_funcs_count };
diff --git a/dlls/wlanapi/unixlib_priv.h b/dlls/wlanapi/unixlib_priv.h index fa813d07844..9b6982f0e46 100644 --- a/dlls/wlanapi/unixlib_priv.h +++ b/dlls/wlanapi/unixlib_priv.h @@ -21,8 +21,15 @@ #ifndef __WINE_WLANAPI_UNIXLIB_PRIV_H #define __WINE_WLANAPI_UNIXLIB_PRIV_H
+struct wlan_interface +{ + struct list entry; + struct unix_wlan_interface_info info; +}; + extern BOOL load_dbus_functions( void ); extern NTSTATUS init_dbus_connection( UINT_PTR *handle ); extern void close_dbus_connection( void *c ); +extern NTSTATUS networkmanager_get_wifi_devices( void *connection, struct list *devices );
#endif /* __WINE_WLANAPI_UNIXLIB_PRIV_H */