[pkg-wpa-devel] r1324 - in /wpasupplicant/branches/upstream/current: src/ src/common/ src/crypto/ src/drivers/ src/eap_common/ src/eap_peer/ src/eap_server/ src/eapol_supp/ src/hlr_auc_gw/ src/l2_packet/ src/radius/ src/rsn_supp/ src/tls/ src/utils/ src/wps/ wpa_supplicant/ wpa_supplicant/doc/docbook/ wpa_supplicant/tests/ wpa_supplicant/wpa_gui-qt4/

kelmo-guest at users.alioth.debian.org kelmo-guest at users.alioth.debian.org
Sun Feb 15 19:38:57 UTC 2009


Author: kelmo-guest
Date: Sun Feb 15 19:38:55 2009
New Revision: 1324

URL: http://svn.debian.org/wsvn/?sc=1&rev=1324
Log:
[svn-upgrade] Integrating new upstream version, wpasupplicant (0.6.8)

Added:
    wpasupplicant/branches/upstream/current/src/wps/httpread.c
    wpasupplicant/branches/upstream/current/src/wps/httpread.h
    wpasupplicant/branches/upstream/current/src/wps/wps_upnp.c
    wpasupplicant/branches/upstream/current/src/wps/wps_upnp.h
    wpasupplicant/branches/upstream/current/src/wps/wps_upnp_event.c
    wpasupplicant/branches/upstream/current/src/wps/wps_upnp_i.h
    wpasupplicant/branches/upstream/current/src/wps/wps_upnp_ssdp.c
    wpasupplicant/branches/upstream/current/src/wps/wps_upnp_web.c
Modified:
    wpasupplicant/branches/upstream/current/src/Makefile
    wpasupplicant/branches/upstream/current/src/common/Makefile
    wpasupplicant/branches/upstream/current/src/common/ieee802_11_common.c
    wpasupplicant/branches/upstream/current/src/common/ieee802_11_common.h
    wpasupplicant/branches/upstream/current/src/common/ieee802_11_defs.h
    wpasupplicant/branches/upstream/current/src/common/nl80211_copy.h
    wpasupplicant/branches/upstream/current/src/common/privsep_commands.h
    wpasupplicant/branches/upstream/current/src/common/version.h
    wpasupplicant/branches/upstream/current/src/common/wpa_ctrl.c
    wpasupplicant/branches/upstream/current/src/common/wpa_ctrl.h
    wpasupplicant/branches/upstream/current/src/crypto/Makefile
    wpasupplicant/branches/upstream/current/src/crypto/aes_wrap.c
    wpasupplicant/branches/upstream/current/src/crypto/crypto_gnutls.c
    wpasupplicant/branches/upstream/current/src/crypto/crypto_openssl.c
    wpasupplicant/branches/upstream/current/src/crypto/tls_gnutls.c
    wpasupplicant/branches/upstream/current/src/drivers/Makefile
    wpasupplicant/branches/upstream/current/src/drivers/driver_broadcom.c
    wpasupplicant/branches/upstream/current/src/drivers/driver_bsd.c
    wpasupplicant/branches/upstream/current/src/drivers/driver_madwifi.c
    wpasupplicant/branches/upstream/current/src/drivers/driver_nl80211.c
    wpasupplicant/branches/upstream/current/src/drivers/driver_privsep.c
    wpasupplicant/branches/upstream/current/src/drivers/driver_roboswitch.c
    wpasupplicant/branches/upstream/current/src/drivers/driver_wext.c
    wpasupplicant/branches/upstream/current/src/drivers/scan_helpers.c
    wpasupplicant/branches/upstream/current/src/eap_common/Makefile
    wpasupplicant/branches/upstream/current/src/eap_peer/Makefile
    wpasupplicant/branches/upstream/current/src/eap_peer/eap.c
    wpasupplicant/branches/upstream/current/src/eap_peer/eap_aka.c
    wpasupplicant/branches/upstream/current/src/eap_peer/eap_i.h
    wpasupplicant/branches/upstream/current/src/eap_peer/eap_tls_common.c
    wpasupplicant/branches/upstream/current/src/eap_peer/eap_wsc.c
    wpasupplicant/branches/upstream/current/src/eap_server/Makefile
    wpasupplicant/branches/upstream/current/src/eap_server/eap_aka.c
    wpasupplicant/branches/upstream/current/src/eap_server/eap_wsc.c
    wpasupplicant/branches/upstream/current/src/eapol_supp/Makefile
    wpasupplicant/branches/upstream/current/src/eapol_supp/eapol_supp_sm.c
    wpasupplicant/branches/upstream/current/src/hlr_auc_gw/Makefile
    wpasupplicant/branches/upstream/current/src/hlr_auc_gw/hlr_auc_gw.milenage_db
    wpasupplicant/branches/upstream/current/src/l2_packet/Makefile
    wpasupplicant/branches/upstream/current/src/radius/Makefile
    wpasupplicant/branches/upstream/current/src/radius/radius.c
    wpasupplicant/branches/upstream/current/src/radius/radius.h
    wpasupplicant/branches/upstream/current/src/radius/radius_client.c
    wpasupplicant/branches/upstream/current/src/radius/radius_client.h
    wpasupplicant/branches/upstream/current/src/rsn_supp/Makefile
    wpasupplicant/branches/upstream/current/src/tls/Makefile
    wpasupplicant/branches/upstream/current/src/utils/Makefile
    wpasupplicant/branches/upstream/current/src/utils/os_unix.c
    wpasupplicant/branches/upstream/current/src/utils/wpabuf.c
    wpasupplicant/branches/upstream/current/src/utils/wpabuf.h
    wpasupplicant/branches/upstream/current/src/wps/Makefile
    wpasupplicant/branches/upstream/current/src/wps/wps.c
    wpasupplicant/branches/upstream/current/src/wps/wps.h
    wpasupplicant/branches/upstream/current/src/wps/wps_attr_build.c
    wpasupplicant/branches/upstream/current/src/wps/wps_attr_process.c
    wpasupplicant/branches/upstream/current/src/wps/wps_common.c
    wpasupplicant/branches/upstream/current/src/wps/wps_enrollee.c
    wpasupplicant/branches/upstream/current/src/wps/wps_i.h
    wpasupplicant/branches/upstream/current/src/wps/wps_registrar.c
    wpasupplicant/branches/upstream/current/wpa_supplicant/ChangeLog
    wpasupplicant/branches/upstream/current/wpa_supplicant/Makefile
    wpasupplicant/branches/upstream/current/wpa_supplicant/README
    wpasupplicant/branches/upstream/current/wpa_supplicant/README-WPS
    wpasupplicant/branches/upstream/current/wpa_supplicant/config.h
    wpasupplicant/branches/upstream/current/wpa_supplicant/config_file.c
    wpasupplicant/branches/upstream/current/wpa_supplicant/config_winreg.c
    wpasupplicant/branches/upstream/current/wpa_supplicant/ctrl_iface_dbus.c
    wpasupplicant/branches/upstream/current/wpa_supplicant/ctrl_iface_dbus.h
    wpasupplicant/branches/upstream/current/wpa_supplicant/defconfig
    wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_background.8
    wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_cli.8
    wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_gui.8
    wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_passphrase.8
    wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_priv.8
    wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_supplicant.8
    wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5
    wpasupplicant/branches/upstream/current/wpa_supplicant/eapol_test.c
    wpasupplicant/branches/upstream/current/wpa_supplicant/events.c
    wpasupplicant/branches/upstream/current/wpa_supplicant/mlme.c
    wpasupplicant/branches/upstream/current/wpa_supplicant/nmake.mak
    wpasupplicant/branches/upstream/current/wpa_supplicant/scan.c
    wpasupplicant/branches/upstream/current/wpa_supplicant/tests/test_sha256.c
    wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_cli.c
    wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/main.cpp
    wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
    wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/wpagui.h
    wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/wpagui.ui
    wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_priv.c
    wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_supplicant.c
    wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_supplicant.conf
    wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_supplicant_i.h
    wpasupplicant/branches/upstream/current/wpa_supplicant/wps_supplicant.c
    wpasupplicant/branches/upstream/current/wpa_supplicant/wps_supplicant.h

Modified: wpasupplicant/branches/upstream/current/src/Makefile
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/Makefile?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/Makefile (original)
+++ wpasupplicant/branches/upstream/current/src/Makefile Sun Feb 15 19:38:55 2009
@@ -6,3 +6,6 @@
 clean:
 	for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d clean; done
 	rm -f *~
+
+install:
+	for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d install; done

Modified: wpasupplicant/branches/upstream/current/src/common/Makefile
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/common/Makefile?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/common/Makefile (original)
+++ wpasupplicant/branches/upstream/current/src/common/Makefile Sun Feb 15 19:38:55 2009
@@ -4,3 +4,6 @@
 clean:
 	for d in $(SUBDIRS); do make -C $$d clean; done
 	rm -f *~ *.o *.d
+
+install:
+	@echo Nothing to be made.

Modified: wpasupplicant/branches/upstream/current/src/common/ieee802_11_common.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/common/ieee802_11_common.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/common/ieee802_11_common.c (original)
+++ wpasupplicant/branches/upstream/current/src/common/ieee802_11_common.c Sun Feb 15 19:38:55 2009
@@ -116,6 +116,14 @@
 }
 
 
+/**
+ * ieee802_11_parse_elems - Parse information elements in management frames
+ * @start: Pointer to the start of IEs
+ * @len: Length of IE buffer in octets
+ * @elems: Data structure for parsed elements
+ * @show_errors: Whether to show parsing errors in debug log
+ * Returns: Parsing result
+ */
 ParseRes ieee802_11_parse_elems(u8 *start, size_t len,
 				struct ieee802_11_elems *elems,
 				int show_errors)
@@ -211,6 +219,10 @@
 			elems->ftie = pos;
 			elems->ftie_len = elen;
 			break;
+		case WLAN_EID_TIMEOUT_INTERVAL:
+			elems->timeout_int = pos;
+			elems->timeout_int_len = elen;
+			break;
 		case WLAN_EID_HT_CAP:
 			elems->ht_capabilities = pos;
 			elems->ht_capabilities_len = elen;
@@ -218,10 +230,6 @@
 		case WLAN_EID_HT_OPERATION:
 			elems->ht_operation = pos;
 			elems->ht_operation_len = elen;
-			break;
-		case WLAN_EID_ASSOC_COMEBACK_TIME:
-			elems->assoc_comeback = pos;
-			elems->assoc_comeback_len = elen;
 			break;
 		default:
 			unknown++;

Modified: wpasupplicant/branches/upstream/current/src/common/ieee802_11_common.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/common/ieee802_11_common.h?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/common/ieee802_11_common.h (original)
+++ wpasupplicant/branches/upstream/current/src/common/ieee802_11_common.h Sun Feb 15 19:38:55 2009
@@ -55,12 +55,12 @@
 	u8 mdie_len;
 	u8 *ftie;
 	u8 ftie_len;
+	u8 *timeout_int;
+	u8 timeout_int_len;
 	u8 *ht_capabilities;
 	u8 ht_capabilities_len;
 	u8 *ht_operation;
 	u8 ht_operation_len;
-	u8 *assoc_comeback;
-	u8 assoc_comeback_len;
 	u8 *vendor_ht_cap;
 	u8 vendor_ht_cap_len;
 };

Modified: wpasupplicant/branches/upstream/current/src/common/ieee802_11_defs.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/common/ieee802_11_defs.h?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/common/ieee802_11_defs.h (original)
+++ wpasupplicant/branches/upstream/current/src/common/ieee802_11_defs.h Sun Feb 15 19:38:55 2009
@@ -203,7 +203,6 @@
 #define WLAN_EID_20_40_BSS_INTOLERANT 73
 #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
 #define WLAN_EID_MMIE 76
-#define WLAN_EID_ASSOC_COMEBACK_TIME 77
 #define WLAN_EID_VENDOR_SPECIFIC 221
 
 
@@ -222,6 +221,11 @@
 #define WLAN_SA_QUERY_RESPONSE 1
 
 #define WLAN_SA_QUERY_TR_ID_LEN 16
+
+/* Timeout Interval Type */
+#define WLAN_TIMEOUT_REASSOC_DEADLINE 1
+#define WLAN_TIMEOUT_KEY_LIFETIME 2
+#define WLAN_TIMEOUT_ASSOC_COMEBACK 3
 
 
 #ifdef _MSC_VER

Modified: wpasupplicant/branches/upstream/current/src/common/nl80211_copy.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/common/nl80211_copy.h?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/common/nl80211_copy.h (original)
+++ wpasupplicant/branches/upstream/current/src/common/nl80211_copy.h Sun Feb 15 19:38:55 2009
@@ -47,7 +47,7 @@
  * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or
  *	%NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME,
  *	%NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, and/or
- *	%NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET.
+ *	%NL80211_ATTR_WIPHY_CHANNEL_TYPE.
  * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request
  *	or rename notification. Has attributes %NL80211_ATTR_WIPHY and
  *	%NL80211_ATTR_WIPHY_NAME.
@@ -84,7 +84,7 @@
  *	%NL80222_CMD_NEW_BEACON message)
  * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface
  *	using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD,
- *	%NL80211_BEACON_HEAD and %NL80211_BEACON_TAIL attributes.
+ *	%NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes.
  * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface,
  *	parameters are like for %NL80211_CMD_SET_BEACON.
  * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it
@@ -113,6 +113,8 @@
  * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by
  *	%NL80211_ATTR_IFINDEX.
  *
+ * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set
+ * 	regulatory domain.
  * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command
  *	after being queried by the kernel. CRDA replies by sending a regulatory
  *	domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our
@@ -133,6 +135,21 @@
  * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the
  *      interface identified by %NL80211_ATTR_IFINDEX
  *
+ * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The
+ *	interface is identified with %NL80211_ATTR_IFINDEX and the management
+ *	frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be
+ *	added to the end of the specified management frame is specified with
+ *	%NL80211_ATTR_IE. If the command succeeds, the requested data will be
+ *	added to all specified management frames generated by
+ *	kernel/firmware/driver.
+ *
+ * @NL80211_CMD_GET_SCAN: get scan results
+ * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
+ * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to
+ *	NL80211_CMD_GET_SCAN and on the "scan" multicast group)
+ * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons,
+ *	partial scan results may be available
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -178,6 +195,15 @@
 	NL80211_CMD_GET_MESH_PARAMS,
 	NL80211_CMD_SET_MESH_PARAMS,
 
+	NL80211_CMD_SET_MGMT_EXTRA_IE,
+
+	NL80211_CMD_GET_REG,
+
+	NL80211_CMD_GET_SCAN,
+	NL80211_CMD_TRIGGER_SCAN,
+	NL80211_CMD_NEW_SCAN_RESULTS,
+	NL80211_CMD_SCAN_ABORTED,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -190,6 +216,7 @@
  * here
  */
 #define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS
+#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE
 
 /**
  * enum nl80211_attrs - nl80211 netlink attributes
@@ -284,6 +311,24 @@
  *	supported interface types, each a flag attribute with the number
  *	of the interface mode.
  *
+ * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for
+ *	%NL80211_CMD_SET_MGMT_EXTRA_IE.
+ *
+ * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with
+ *	%NL80211_CMD_SET_MGMT_EXTRA_IE).
+ *
+ * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with
+ *	a single scan request, a wiphy attribute.
+ *
+ * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz)
+ * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive
+ *	scanning and include a zero-length SSID (wildcard) for wildcard scan
+ * @NL80211_ATTR_SCAN_GENERATION: the scan generation increases whenever the
+ *	scan result list changes (BSS expired or added) so that applications
+ *	can verify that they got a single, consistent snapshot (when all dump
+ *	messages carried the same generation number)
+ * @NL80211_ATTR_BSS: scan result BSS
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -348,6 +393,16 @@
 
 	NL80211_ATTR_KEY_DEFAULT_MGMT,
 
+	NL80211_ATTR_MGMT_SUBTYPE,
+	NL80211_ATTR_IE,
+
+	NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
+
+	NL80211_ATTR_SCAN_FREQUENCIES,
+	NL80211_ATTR_SCAN_SSIDS,
+	NL80211_ATTR_SCAN_GENERATION,
+	NL80211_ATTR_BSS,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -362,7 +417,9 @@
 #define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES
 #define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS
 #define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ
-#define NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET
+#define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE
+#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE
+#define NL80211_ATTR_IE NL80211_ATTR_IE
 
 #define NL80211_MAX_SUPP_RATES			32
 #define NL80211_MAX_SUPP_REG_RULES		32
@@ -815,4 +872,38 @@
 	NL80211_CHAN_HT40MINUS,
 	NL80211_CHAN_HT40PLUS
 };
+
+/**
+ * enum nl80211_bss - netlink attributes for a BSS
+ *
+ * @__NL80211_BSS_INVALID: invalid
+ * @NL80211_BSS_FREQUENCY: frequency in MHz (u32)
+ * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64)
+ * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16)
+ * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16)
+ * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the
+ *	raw information elements from the probe response/beacon (bin)
+ * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon
+ *	in mBm (100 * dBm) (s32)
+ * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon
+ *	in unspecified units, scaled to 0..100 (u8)
+ * @__NL80211_BSS_AFTER_LAST: internal
+ * @NL80211_BSS_MAX: highest BSS attribute
+ */
+enum nl80211_bss {
+	__NL80211_BSS_INVALID,
+	NL80211_BSS_BSSID,
+	NL80211_BSS_FREQUENCY,
+	NL80211_BSS_TSF,
+	NL80211_BSS_BEACON_INTERVAL,
+	NL80211_BSS_CAPABILITY,
+	NL80211_BSS_INFORMATION_ELEMENTS,
+	NL80211_BSS_SIGNAL_MBM,
+	NL80211_BSS_SIGNAL_UNSPEC,
+
+	/* keep last */
+	__NL80211_BSS_AFTER_LAST,
+	NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1
+};
+
 #endif /* __LINUX_NL80211_H */

Modified: wpasupplicant/branches/upstream/current/src/common/privsep_commands.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/common/privsep_commands.h?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/common/privsep_commands.h (original)
+++ wpasupplicant/branches/upstream/current/src/common/privsep_commands.h Sun Feb 15 19:38:55 2009
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - privilege separation commands
- * Copyright (c) 2007, Jouni Malinen <j at w1.fi>
+ * Copyright (c) 2007-2009, Jouni Malinen <j at w1.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
@@ -31,6 +31,7 @@
 	PRIVSEP_CMD_L2_NOTIFY_AUTH_START,
 	PRIVSEP_CMD_L2_SEND,
 	PRIVSEP_CMD_SET_MODE,
+	PRIVSEP_CMD_SET_COUNTRY,
 };
 
 struct privsep_cmd_associate
@@ -71,6 +72,7 @@
 	PRIVSEP_EVENT_STKSTART,
 	PRIVSEP_EVENT_FT_RESPONSE,
 	PRIVSEP_EVENT_RX_EAPOL,
+	PRIVSEP_EVENT_STA_RX,
 };
 
 #endif /* PRIVSEP_COMMANDS_H */

Modified: wpasupplicant/branches/upstream/current/src/common/version.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/common/version.h?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/common/version.h (original)
+++ wpasupplicant/branches/upstream/current/src/common/version.h Sun Feb 15 19:38:55 2009
@@ -1,6 +1,6 @@
 #ifndef VERSION_H
 #define VERSION_H
 
-#define VERSION_STR "0.6.7"
+#define VERSION_STR "0.6.8"
 
 #endif /* VERSION_H */

Modified: wpasupplicant/branches/upstream/current/src/common/wpa_ctrl.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/common/wpa_ctrl.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/common/wpa_ctrl.c (original)
+++ wpasupplicant/branches/upstream/current/src/common/wpa_ctrl.c Sun Feb 15 19:38:55 2009
@@ -64,6 +64,7 @@
 	static int counter = 0;
 	int ret;
 	size_t res;
+	int tries = 0;
 
 	ctrl = os_malloc(sizeof(*ctrl));
 	if (ctrl == NULL)
@@ -77,15 +78,28 @@
 	}
 
 	ctrl->local.sun_family = AF_UNIX;
+	counter++;
+try_again:
 	ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path),
-			  "/tmp/wpa_ctrl_%d-%d", getpid(), counter++);
+			  "/tmp/wpa_ctrl_%d-%d", getpid(), counter);
 	if (ret < 0 || (size_t) ret >= sizeof(ctrl->local.sun_path)) {
 		close(ctrl->s);
 		os_free(ctrl);
 		return NULL;
 	}
+	tries++;
 	if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
 		    sizeof(ctrl->local)) < 0) {
+		if (errno == EADDRINUSE && tries < 2) {
+			/*
+			 * getpid() returns unique identifier for this instance
+			 * of wpa_ctrl, so the existing socket file must have
+			 * been left by unclean termination of an earlier run.
+			 * Remove the file and try again.
+			 */
+			unlink(ctrl->local.sun_path);
+			goto try_again;
+		}
 		close(ctrl->s);
 		os_free(ctrl);
 		return NULL;

Modified: wpasupplicant/branches/upstream/current/src/common/wpa_ctrl.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/common/wpa_ctrl.h?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/common/wpa_ctrl.h (original)
+++ wpasupplicant/branches/upstream/current/src/common/wpa_ctrl.h Sun Feb 15 19:38:55 2009
@@ -66,10 +66,14 @@
 #define WPS_EVENT_FAIL "WPS-FAIL "
 /** WPS registration completed successfully */
 #define WPS_EVENT_SUCCESS "WPS-SUCCESS "
+/** WPS enrollment attempt timed out and was terminated */
+#define WPS_EVENT_TIMEOUT "WPS-TIMEOUT "
 
 /* hostapd control interface - fixed message prefixes */
 #define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
 #define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS "
+#define WPS_EVENT_REG_SUCCESS "WPS-REG-SUCCESS "
+#define WPS_EVENT_AP_SETUP_LOCKED "WPS-AP-SETUP-LOCKED "
 
 
 /* wpa_supplicant/hostapd control interface access */

Modified: wpasupplicant/branches/upstream/current/src/crypto/Makefile
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/crypto/Makefile?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/crypto/Makefile (original)
+++ wpasupplicant/branches/upstream/current/src/crypto/Makefile Sun Feb 15 19:38:55 2009
@@ -4,3 +4,6 @@
 clean:
 	for d in $(SUBDIRS); do make -C $$d clean; done
 	rm -f *~ *.o *.d
+
+install:
+	@echo Nothing to be made.

Modified: wpasupplicant/branches/upstream/current/src/crypto/aes_wrap.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/crypto/aes_wrap.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/crypto/aes_wrap.c (original)
+++ wpasupplicant/branches/upstream/current/src/crypto/aes_wrap.c Sun Feb 15 19:38:55 2009
@@ -86,6 +86,8 @@
 #endif /* CONFIG_NO_AES_WRAP */
 
 
+#ifndef CONFIG_NO_AES_UNWRAP
+
 /**
  * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394)
  * @kek: Key encryption key (KEK)
@@ -145,6 +147,8 @@
 	return 0;
 }
 
+#endif /* CONFIG_NO_AES_UNWRAP */
+
 
 #define BLOCK_SIZE 16
 

Modified: wpasupplicant/branches/upstream/current/src/crypto/crypto_gnutls.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/crypto/crypto_gnutls.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/crypto/crypto_gnutls.c (original)
+++ wpasupplicant/branches/upstream/current/src/crypto/crypto_gnutls.c Sun Feb 15 19:38:55 2009
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / wrapper functions for libgcrypt
- * Copyright (c) 2004-2005, Jouni Malinen <j at w1.fi>
+ * Copyright (c) 2004-2009, Jouni Malinen <j at w1.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
@@ -163,3 +163,151 @@
 	gcry_cipher_close(hd);
 }
 #endif /* EAP_TLS_FUNCS */
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+		   const u8 *power, size_t power_len,
+		   const u8 *modulus, size_t modulus_len,
+		   u8 *result, size_t *result_len)
+{
+	gcry_mpi_t bn_base = NULL, bn_exp = NULL, bn_modulus = NULL,
+		bn_result = NULL;
+	int ret = -1;
+
+	if (gcry_mpi_scan(&bn_base, GCRYMPI_FMT_USG, base, base_len, NULL) !=
+	    GPG_ERR_NO_ERROR ||
+	    gcry_mpi_scan(&bn_exp, GCRYMPI_FMT_USG, power, power_len, NULL) !=
+	    GPG_ERR_NO_ERROR ||
+	    gcry_mpi_scan(&bn_modulus, GCRYMPI_FMT_USG, modulus, modulus_len,
+			  NULL) != GPG_ERR_NO_ERROR)
+		goto error;
+	bn_result = gcry_mpi_new(modulus_len * 8);
+
+	gcry_mpi_powm(bn_result, bn_base, bn_exp, bn_modulus);
+
+	if (gcry_mpi_print(GCRYMPI_FMT_USG, result, *result_len, result_len,
+			   bn_result) != GPG_ERR_NO_ERROR)
+		goto error;
+
+	ret = 0;
+
+error:
+	gcry_mpi_release(bn_base);
+	gcry_mpi_release(bn_exp);
+	gcry_mpi_release(bn_modulus);
+	gcry_mpi_release(bn_result);
+	return ret;
+}
+
+
+struct crypto_cipher {
+	gcry_cipher_hd_t enc;
+	gcry_cipher_hd_t dec;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len)
+{
+	struct crypto_cipher *ctx;
+	gcry_error_t res;
+	enum gcry_cipher_algos a;
+	int ivlen;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+
+	switch (alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		a = GCRY_CIPHER_ARCFOUR;
+		res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_STREAM,
+				       0);
+		gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_STREAM, 0);
+		break;
+	case CRYPTO_CIPHER_ALG_AES:
+		if (key_len == 24)
+			a = GCRY_CIPHER_AES192;
+		else if (key_len == 32)
+			a = GCRY_CIPHER_AES256;
+		else
+			a = GCRY_CIPHER_AES;
+		res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+		gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+		break;
+	case CRYPTO_CIPHER_ALG_3DES:
+		a = GCRY_CIPHER_3DES;
+		res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+		gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+		break;
+	case CRYPTO_CIPHER_ALG_DES:
+		a = GCRY_CIPHER_DES;
+		res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+		gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+		break;
+	case CRYPTO_CIPHER_ALG_RC2:
+		if (key_len == 5)
+			a = GCRY_CIPHER_RFC2268_40;
+		else
+			a = GCRY_CIPHER_RFC2268_128;
+		res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+		gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+		break;
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	if (res != GPG_ERR_NO_ERROR) {
+		os_free(ctx);
+		return NULL;
+	}
+
+	if (gcry_cipher_setkey(ctx->enc, key, key_len) != GPG_ERR_NO_ERROR ||
+	    gcry_cipher_setkey(ctx->dec, key, key_len) != GPG_ERR_NO_ERROR) {
+		gcry_cipher_close(ctx->enc);
+		gcry_cipher_close(ctx->dec);
+		os_free(ctx);
+		return NULL;
+	}
+
+	ivlen = gcry_cipher_get_algo_blklen(a);
+	if (gcry_cipher_setiv(ctx->enc, iv, ivlen) != GPG_ERR_NO_ERROR ||
+	    gcry_cipher_setiv(ctx->dec, iv, ivlen) != GPG_ERR_NO_ERROR) {
+		gcry_cipher_close(ctx->enc);
+		gcry_cipher_close(ctx->dec);
+		os_free(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+			  u8 *crypt, size_t len)
+{
+	if (gcry_cipher_encrypt(ctx->enc, crypt, len, plain, len) !=
+	    GPG_ERR_NO_ERROR)
+		return -1;
+	return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+			  u8 *plain, size_t len)
+{
+	if (gcry_cipher_decrypt(ctx->dec, plain, len, crypt, len) !=
+	    GPG_ERR_NO_ERROR)
+		return -1;
+	return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+	gcry_cipher_close(ctx->enc);
+	gcry_cipher_close(ctx->dec);
+	os_free(ctx);
+}

Modified: wpasupplicant/branches/upstream/current/src/crypto/crypto_openssl.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/crypto/crypto_openssl.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/crypto/crypto_openssl.c (original)
+++ wpasupplicant/branches/upstream/current/src/crypto/crypto_openssl.c Sun Feb 15 19:38:55 2009
@@ -283,6 +283,7 @@
 			cipher = EVP_aes_256_cbc();
 			break;
 		default:
+			os_free(ctx);
 			return NULL;
 		}
 		break;
@@ -301,6 +302,7 @@
 		break;
 #endif /* OPENSSL_NO_RC2 */
 	default:
+		os_free(ctx);
 		return NULL;
 	}
 

Modified: wpasupplicant/branches/upstream/current/src/crypto/tls_gnutls.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/crypto/tls_gnutls.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/crypto/tls_gnutls.c (original)
+++ wpasupplicant/branches/upstream/current/src/crypto/tls_gnutls.c Sun Feb 15 19:38:55 2009
@@ -254,8 +254,9 @@
 		conn->pull_buf = conn->pull_buf_offset = NULL;
 		conn->pull_buf_len = 0;
 	} else {
-		wpa_printf(MSG_DEBUG, "%s - %d bytes remaining in pull_buf",
-			   __func__, end - conn->pull_buf_offset);
+		wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf",
+			   __func__,
+			   (unsigned long) (end - conn->pull_buf_offset));
 	}
 	return len;
 }
@@ -943,8 +944,9 @@
 
 	if (in_data && in_len) {
 		if (conn->pull_buf) {
-			wpa_printf(MSG_DEBUG, "%s - %d bytes remaining in "
-				   "pull_buf", __func__, conn->pull_buf_len);
+			wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
+				   "pull_buf", __func__,
+				   (unsigned long) conn->pull_buf_len);
 			os_free(conn->pull_buf);
 		}
 		conn->pull_buf = os_malloc(in_len);
@@ -1058,6 +1060,14 @@
 		return -1;
 	if (conn->push_buf_len < out_len)
 		out_len = conn->push_buf_len;
+	else if (conn->push_buf_len > out_len) {
+		wpa_printf(MSG_INFO, "GnuTLS: Not enough buffer space for "
+			   "encrypted message (in_len=%lu push_buf_len=%lu "
+			   "out_len=%lu",
+			   (unsigned long) in_len,
+			   (unsigned long) conn->push_buf_len,
+			   (unsigned long) out_len);
+	}
 	os_memcpy(out_data, conn->push_buf, out_len);
 	os_free(conn->push_buf);
 	conn->push_buf = NULL;
@@ -1073,8 +1083,9 @@
 	ssize_t res;
 
 	if (conn->pull_buf) {
-		wpa_printf(MSG_DEBUG, "%s - %d bytes remaining in "
-			   "pull_buf", __func__, conn->pull_buf_len);
+		wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
+			   "pull_buf", __func__,
+			   (unsigned long) conn->pull_buf_len);
 		os_free(conn->pull_buf);
 	}
 	conn->pull_buf = os_malloc(in_len);
@@ -1132,7 +1143,7 @@
 
 		if (res < 0) {
 			wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d "
-				   "(%s)", __func__, res,
+				   "(%s)", __func__, (int) res,
 				   gnutls_strerror(res));
 		}
 		return res;
@@ -1142,7 +1153,7 @@
 	res = gnutls_record_recv(conn->session, out_data, out_len);
 	if (res < 0) {
 		wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
-			   "(%s)", __func__, res, gnutls_strerror(res));
+			   "(%s)", __func__, (int) res, gnutls_strerror(res));
 	}
 
 	return res;

Modified: wpasupplicant/branches/upstream/current/src/drivers/Makefile
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/drivers/Makefile?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/drivers/Makefile (original)
+++ wpasupplicant/branches/upstream/current/src/drivers/Makefile Sun Feb 15 19:38:55 2009
@@ -4,3 +4,6 @@
 clean:
 	for d in $(SUBDIRS); do make -C $$d clean; done
 	rm -f *~ *.o *.d
+
+install:
+	@echo Nothing to be made.

Modified: wpasupplicant/branches/upstream/current/src/drivers/driver_broadcom.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/drivers/driver_broadcom.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/drivers/driver_broadcom.c (original)
+++ wpasupplicant/branches/upstream/current/src/drivers/driver_broadcom.c Sun Feb 15 19:38:55 2009
@@ -1,5 +1,5 @@
 /*
- * WPA Supplicant - driver interaction with Broadcom wl.o driver
+ * WPA Supplicant - driver interaction with old Broadcom wl.o driver
  * Copyright (c) 2004, Nikki Chumkov <nikki at gattaca.ru>
  * Copyright (c) 2004, Jouni Malinen <j at w1.fi>
  *
@@ -11,6 +11,10 @@
  * license.
  *
  * See README and COPYING for more details.
+ *
+ * Please note that the newer Broadcom driver ("hybrid Linux driver") supports
+ * Linux wireless extensions and does not need (or even work) with this old
+ * driver wrapper. Use driver_wext.c with that driver.
  */
 
 #include "includes.h"

Modified: wpasupplicant/branches/upstream/current/src/drivers/driver_bsd.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/drivers/driver_bsd.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/drivers/driver_bsd.c (original)
+++ wpasupplicant/branches/upstream/current/src/drivers/driver_bsd.c Sun Feb 15 19:38:55 2009
@@ -21,7 +21,13 @@
 #include "ieee802_11_defs.h"
 
 #include <net/if.h>
+
+#ifdef __NetBSD__
+#include <net/if_ether.h>
+#define COMPAT_FREEBSD_NET80211
+#else
 #include <net/ethernet.h>
+#endif
 
 #include <net80211/ieee80211.h>
 #include <net80211/ieee80211_crypto.h>
@@ -449,7 +455,12 @@
 }
 
 #include <net/route.h>
+#if __FreeBSD__
 #include <net80211/ieee80211_freebsd.h>
+#endif
+#if __NetBSD__
+#include <net80211/ieee80211_netbsd.h>
+#endif
 
 static void
 wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)

Modified: wpasupplicant/branches/upstream/current/src/drivers/driver_madwifi.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/drivers/driver_madwifi.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/drivers/driver_madwifi.c (original)
+++ wpasupplicant/branches/upstream/current/src/drivers/driver_madwifi.c Sun Feb 15 19:38:55 2009
@@ -59,7 +59,8 @@
 
 	os_memset(&iwr, 0, sizeof(iwr));
 	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
-	if (len < IFNAMSIZ) {
+	if (len < IFNAMSIZ &&
+	    op != IEEE80211_IOCTL_SET_APPIEBUF) {
 		/*
 		 * Argument data fits inline; put it there.
 		 */
@@ -90,7 +91,7 @@
 				"ioctl[IEEE80211_IOCTL_GETCHANLIST]",
 				"ioctl[IEEE80211_IOCTL_CHANSWITCH]",
 				NULL,
-				NULL,
+				"ioctl[IEEE80211_IOCTL_SET_APPIEBUF]",
 				"ioctl[IEEE80211_IOCTL_GETSCANRESULTS]",
 				NULL,
 				"ioctl[IEEE80211_IOCTL_GETCHANINFO]",
@@ -482,6 +483,30 @@
 }
 
 
+static int wpa_driver_madwifi_set_probe_req_ie(void *priv, const u8 *ies,
+					       size_t ies_len)
+{
+	struct ieee80211req_getset_appiebuf *probe_req_ie;
+	int ret;
+
+	probe_req_ie = os_malloc(sizeof(*probe_req_ie) + ies_len);
+	if (probe_req_ie == NULL)
+		return -1;
+
+	probe_req_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_REQ;
+	probe_req_ie->app_buflen = ies_len;
+	os_memcpy(probe_req_ie->app_buf, ies, ies_len);
+
+	ret = set80211priv(priv, IEEE80211_IOCTL_SET_APPIEBUF, probe_req_ie,
+			   sizeof(struct ieee80211req_getset_appiebuf) +
+			   ies_len, 1);
+
+	os_free(probe_req_ie);
+
+	return ret;
+}
+
+
 static void * wpa_driver_madwifi_init(void *ctx, const char *ifname)
 {
 	struct wpa_driver_madwifi_data *drv;
@@ -568,4 +593,5 @@
 	.associate		= wpa_driver_madwifi_associate,
 	.set_auth_alg		= wpa_driver_madwifi_set_auth_alg,
 	.set_operstate		= wpa_driver_madwifi_set_operstate,
+	.set_probe_req_ie	= wpa_driver_madwifi_set_probe_req_ie,
 };

Modified: wpasupplicant/branches/upstream/current/src/drivers/driver_nl80211.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/drivers/driver_nl80211.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/drivers/driver_nl80211.c (original)
+++ wpasupplicant/branches/upstream/current/src/drivers/driver_nl80211.c Sun Feb 15 19:38:55 2009
@@ -49,7 +49,7 @@
 
 struct wpa_driver_nl80211_data {
 	void *ctx;
-	int event_sock;
+	int wext_event_sock;
 	int ioctl_sock;
 	char ifname[IFNAMSIZ + 1];
 	int ifindex;
@@ -151,6 +151,68 @@
 }
 
 
+struct family_data {
+	const char *group;
+	int id;
+};
+
+
+static int family_handler(struct nl_msg *msg, void *arg)
+{
+	struct family_data *res = arg;
+	struct nlattr *tb[CTRL_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *mcgrp;
+	int i;
+
+	nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb[CTRL_ATTR_MCAST_GROUPS])
+		return NL_SKIP;
+
+	nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) {
+		struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1];
+		nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp),
+			  nla_len(mcgrp), NULL);
+		if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] ||
+		    !tb2[CTRL_ATTR_MCAST_GRP_ID] ||
+		    os_strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]),
+			       res->group,
+			       nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0)
+			continue;
+		res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]);
+		break;
+	};
+
+	return NL_SKIP;
+}
+
+
+static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv,
+			       const char *family, const char *group)
+{
+	struct nl_msg *msg;
+	int ret = -1;
+	struct family_data res = { group, -ENOENT };
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+	genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"),
+		    0, 0, CTRL_CMD_GETFAMILY, 0);
+	NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
+
+	ret = send_and_recv_msgs(drv, msg, family_handler, &res);
+	msg = NULL;
+	if (ret == 0)
+		ret = res.id;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
 static int wpa_driver_nl80211_send_oper_ifla(
 	struct wpa_driver_nl80211_data *drv,
 	int linkmode, int operstate)
@@ -200,7 +262,7 @@
 	wpa_printf(MSG_DEBUG, "WEXT: Operstate: linkmode=%d, operstate=%d",
 		   linkmode, operstate);
 
-	ret = send(drv->event_sock, &req, req.hdr.nlmsg_len, 0);
+	ret = send(drv->wext_event_sock, &req, req.hdr.nlmsg_len, 0);
 	if (ret < 0) {
 		wpa_printf(MSG_DEBUG, "WEXT: Sending operstate IFLA failed: "
 			   "%s (assume operstate is not supported)",
@@ -631,12 +693,6 @@
 			wpa_driver_nl80211_event_wireless_custom(ctx, buf);
 			os_free(buf);
 			break;
-		case SIOCGIWSCAN:
-			drv->scan_complete_events = 1;
-			eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
-					     drv, ctx);
-			wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL);
-			break;
 		case IWEVASSOCREQIE:
 			wpa_driver_nl80211_event_wireless_assocreqie(
 				drv, custom, iwe->u.data.length);
@@ -832,8 +888,8 @@
 }
 
 
-static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
-					  void *sock_ctx)
+static void wpa_driver_nl80211_event_receive_wext(int sock, void *eloop_ctx,
+						  void *sock_ctx)
 {
 	char buf[8192];
 	int left;
@@ -899,6 +955,77 @@
 }
 
 
+static int no_seq_check(struct nl_msg *msg, void *arg)
+{
+	return NL_OK;
+}
+
+
+static int process_event(struct nl_msg *msg, void *arg)
+{
+	struct wpa_driver_nl80211_data *drv = arg;
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL80211_ATTR_IFINDEX]) {
+		int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+		if (ifindex != drv->ifindex) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)"
+				   " for foreign interface (ifindex %d)",
+				   gnlh->cmd, ifindex);
+			return NL_SKIP;
+		}
+	}
+
+	switch (gnlh->cmd) {
+	case NL80211_CMD_NEW_SCAN_RESULTS:
+		wpa_printf(MSG_DEBUG, "nl80211: New scan results available");
+		drv->scan_complete_events = 1;
+		eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+				     drv->ctx);
+		wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
+		break;
+	case NL80211_CMD_SCAN_ABORTED:
+		wpa_printf(MSG_DEBUG, "nl80211: Scan aborted");
+		/*
+		 * Need to indicate that scan results are available in order
+		 * not to make wpa_supplicant stop its scanning.
+		 */
+		eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+				     drv->ctx);
+		wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "nl0211: Ignored unknown event (cmd=%d)",
+			   gnlh->cmd);
+		break;
+	}
+
+	return NL_SKIP;
+}
+
+
+static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
+					     void *sock_ctx)
+{
+	struct nl_cb *cb;
+	struct wpa_driver_nl80211_data *drv = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Event message available");
+
+	cb = nl_cb_clone(drv->nl_cb);
+	if (!cb)
+		return;
+	nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, process_event, drv);
+	nl_recvmsgs(drv->nl_handle, cb);
+	nl_cb_put(cb);
+}
+
+
 static int wpa_driver_nl80211_get_ifflags_ifname(struct wpa_driver_nl80211_data *drv,
 					      const char *ifname, int *flags)
 {
@@ -990,6 +1117,36 @@
 	return 0;
 nla_put_failure:
 	return -EINVAL;
+}
+
+
+static int wpa_driver_nl80211_set_probe_req_ie(void *priv, const u8 *ies,
+					       size_t ies_len)
+{
+	struct wpa_driver_nl80211_data *drv = priv;
+	struct nl_msg *msg;
+	int ret = -1;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+		    NL80211_CMD_SET_MGMT_EXTRA_IE, 0);
+
+	NLA_PUT_U8(msg, NL80211_ATTR_MGMT_SUBTYPE, 4 /* ProbeReq */);
+	if (ies)
+		NLA_PUT(msg, NL80211_ATTR_IE, ies_len, ies);
+
+	ret = 0;
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	return ret;
+
+nla_put_failure:
+	return -ENOBUFS;
 }
 
 
@@ -1254,7 +1411,7 @@
  */
 static void * wpa_driver_nl80211_init(void *ctx, const char *ifname)
 {
-	int s;
+	int s, ret;
 	struct sockaddr_nl local;
 	struct wpa_driver_nl80211_data *drv;
 
@@ -1297,6 +1454,18 @@
 			   "found");
 		goto err4;
 	}
+
+	ret = nl_get_multicast_id(drv, "nl80211", "scan");
+	if (ret >= 0)
+		ret = nl_socket_add_membership(drv->nl_handle, ret);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
+			   "membership for scan events: %d (%s)",
+			   ret, strerror(-ret));
+		goto err4;
+	}
+	eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle),
+				 wpa_driver_nl80211_event_receive, drv, ctx);
 
 	drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
 	if (drv->ioctl_sock < 0) {
@@ -1319,9 +1488,9 @@
 		goto err6;
 	}
 
-	eloop_register_read_sock(s, wpa_driver_nl80211_event_receive, drv,
+	eloop_register_read_sock(s, wpa_driver_nl80211_event_receive_wext, drv,
 				 ctx);
-	drv->event_sock = s;
+	drv->wext_event_sock = s;
 
 	wpa_driver_nl80211_finish_drv_init(drv);
 
@@ -1354,17 +1523,6 @@
 		if (wpa_driver_nl80211_set_ifflags(drv, flags | IFF_UP) != 0) {
 			printf("Could not set interface '%s' UP\n",
 			       drv->ifname);
-		} else {
-			/*
-			 * Wait some time to allow driver to initialize before
-			 * starting configuring the driver. This seems to be
-			 * needed at least some drivers that load firmware etc.
-			 * when the interface is set up.
-			 */
-			wpa_printf(MSG_DEBUG, "Interface %s set UP - waiting "
-				   "a second for the driver to complete "
-				   "initialization", drv->ifname);
-			sleep(1);
 		}
 	}
 
@@ -1419,16 +1577,17 @@
 
 	wpa_driver_nl80211_send_oper_ifla(priv, 0, IF_OPER_UP);
 
-	eloop_unregister_read_sock(drv->event_sock);
+	eloop_unregister_read_sock(drv->wext_event_sock);
 
 	if (wpa_driver_nl80211_get_ifflags(drv, &flags) == 0)
 		(void) wpa_driver_nl80211_set_ifflags(drv, flags & ~IFF_UP);
 
-	close(drv->event_sock);
+	close(drv->wext_event_sock);
 	close(drv->ioctl_sock);
 	os_free(drv->assoc_req_ies);
 	os_free(drv->assoc_resp_ies);
 
+	eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle));
 	genl_family_put(drv->nl80211);
 	nl_cache_free(drv->nl_cache);
 	nl_handle_destroy(drv->nl_handle);
@@ -1465,38 +1624,42 @@
 static int wpa_driver_nl80211_scan(void *priv, const u8 *ssid, size_t ssid_len)
 {
 	struct wpa_driver_nl80211_data *drv = priv;
-	struct iwreq iwr;
 	int ret = 0, timeout;
-	struct iw_scan_req req;
-
-	if (ssid_len > IW_ESSID_MAX_SIZE) {
-		wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)",
-			   __FUNCTION__, (unsigned long) ssid_len);
+	struct nl_msg *msg, *ssids;
+
+	msg = nlmsg_alloc();
+	ssids = nlmsg_alloc();
+	if (!msg || !ssids) {
+		nlmsg_free(msg);
+		nlmsg_free(ssids);
 		return -1;
 	}
 
-	os_memset(&iwr, 0, sizeof(iwr));
-	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+		    NL80211_CMD_TRIGGER_SCAN, 0);
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
 
 	if (ssid && ssid_len) {
-		os_memset(&req, 0, sizeof(req));
-		req.essid_len = ssid_len;
-		req.bssid.sa_family = ARPHRD_ETHER;
-		os_memset(req.bssid.sa_data, 0xff, ETH_ALEN);
-		os_memcpy(req.essid, ssid, ssid_len);
-		iwr.u.data.pointer = (caddr_t) &req;
-		iwr.u.data.length = sizeof(req);
-		iwr.u.data.flags = IW_SCAN_THIS_ESSID;
-	}
-
-	if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) {
-		perror("ioctl[SIOCSIWSCAN]");
-		ret = -1;
+		/* Request an active scan for a specific SSID */
+		NLA_PUT(ssids, 1, ssid_len, ssid);
+	} else {
+		/* Request an active scan for wildcard SSID */
+		NLA_PUT(ssids, 1, 0, "");
+	}
+	nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
+			   "(%s)", ret, strerror(-ret));
+		goto nla_put_failure;
 	}
 
 	/* Not all drivers generate "scan completed" wireless event, so try to
 	 * read results after a timeout. */
-	timeout = 5;
+	timeout = 10;
 	if (drv->scan_complete_events) {
 		/*
 		 * The driver seems to deliver SIOCGIWSCAN events to notify
@@ -1508,341 +1671,86 @@
 	wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
 		   "seconds", ret, timeout);
 	eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
-	eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, drv,
-			       drv->ctx);
-
+	eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
+			       drv, drv->ctx);
+
+nla_put_failure:
+	nlmsg_free(ssids);
+	nlmsg_free(msg);
 	return ret;
 }
 
 
-static u8 * wpa_driver_nl80211_giwscan(struct wpa_driver_nl80211_data *drv,
-				    size_t *len)
-{
-	struct iwreq iwr;
-	u8 *res_buf;
-	size_t res_buf_len;
-
-	res_buf_len = IW_SCAN_MAX_DATA;
-	for (;;) {
-		res_buf = os_malloc(res_buf_len);
-		if (res_buf == NULL)
-			return NULL;
-		os_memset(&iwr, 0, sizeof(iwr));
-		os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
-		iwr.u.data.pointer = res_buf;
-		iwr.u.data.length = res_buf_len;
-
-		if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0)
-			break;
-
-		if (errno == E2BIG && res_buf_len < 100000) {
-			os_free(res_buf);
-			res_buf = NULL;
-			res_buf_len *= 2;
-			wpa_printf(MSG_DEBUG, "Scan results did not fit - "
-				   "trying larger buffer (%lu bytes)",
-				   (unsigned long) res_buf_len);
-		} else {
-			perror("ioctl[SIOCGIWSCAN]");
-			os_free(res_buf);
-			return NULL;
-		}
-	}
-
-	if (iwr.u.data.length > res_buf_len) {
-		os_free(res_buf);
-		return NULL;
-	}
-	*len = iwr.u.data.length;
-
-	return res_buf;
-}
-
-
-/*
- * Data structure for collecting WEXT scan results. This is needed to allow
- * the various methods of reporting IEs to be combined into a single IE buffer.
- */
-struct wext_scan_data {
-	struct wpa_scan_res res;
-	u8 *ie;
-	size_t ie_len;
-	u8 ssid[32];
-	size_t ssid_len;
-	int maxrate;
-};
-
-
-static void wext_get_scan_mode(struct iw_event *iwe,
-			       struct wext_scan_data *res)
-{
-	if (iwe->u.mode == IW_MODE_ADHOC)
-		res->res.caps |= IEEE80211_CAP_IBSS;
-	else if (iwe->u.mode == IW_MODE_MASTER || iwe->u.mode == IW_MODE_INFRA)
-		res->res.caps |= IEEE80211_CAP_ESS;
-}
-
-
-static void wext_get_scan_ssid(struct iw_event *iwe,
-			       struct wext_scan_data *res, char *custom,
-			       char *end)
-{
-	int ssid_len = iwe->u.essid.length;
-	if (custom + ssid_len > end)
-		return;
-	if (iwe->u.essid.flags &&
-	    ssid_len > 0 &&
-	    ssid_len <= IW_ESSID_MAX_SIZE) {
-		os_memcpy(res->ssid, custom, ssid_len);
-		res->ssid_len = ssid_len;
-	}
-}
-
-
-static void wext_get_scan_freq(struct iw_event *iwe,
-			       struct wext_scan_data *res)
-{
-	int divi = 1000000, i;
-
-	if (iwe->u.freq.e == 0) {
-		/*
-		 * Some drivers do not report frequency, but a channel.
-		 * Try to map this to frequency by assuming they are using
-		 * IEEE 802.11b/g.  But don't overwrite a previously parsed
-		 * frequency if the driver sends both frequency and channel,
-		 * since the driver may be sending an A-band channel that we
-		 * don't handle here.
-		 */
-
-		if (res->res.freq)
-			return;
-
-		if (iwe->u.freq.m >= 1 && iwe->u.freq.m <= 13) {
-			res->res.freq = 2407 + 5 * iwe->u.freq.m;
-			return;
-		} else if (iwe->u.freq.m == 14) {
-			res->res.freq = 2484;
-			return;
-		}
-	}
-
-	if (iwe->u.freq.e > 6) {
-		wpa_printf(MSG_DEBUG, "Invalid freq in scan results (BSSID="
-			   MACSTR " m=%d e=%d)",
-			   MAC2STR(res->res.bssid), iwe->u.freq.m,
-			   iwe->u.freq.e);
-		return;
-	}
-
-	for (i = 0; i < iwe->u.freq.e; i++)
-		divi /= 10;
-	res->res.freq = iwe->u.freq.m / divi;
-}
-
-
-static void wext_get_scan_qual(struct iw_event *iwe,
-			       struct wext_scan_data *res)
-{
-	res->res.qual = iwe->u.qual.qual;
-	res->res.noise = iwe->u.qual.noise;
-	res->res.level = iwe->u.qual.level;
-}
-
-
-static void wext_get_scan_encode(struct iw_event *iwe,
-				 struct wext_scan_data *res)
-{
-	if (!(iwe->u.data.flags & IW_ENCODE_DISABLED))
-		res->res.caps |= IEEE80211_CAP_PRIVACY;
-}
-
-
-static void wext_get_scan_rate(struct iw_event *iwe,
-			       struct wext_scan_data *res, char *pos,
-			       char *end)
-{
-	int maxrate;
-	char *custom = pos + IW_EV_LCP_LEN;
-	struct iw_param p;
-	size_t clen;
-
-	clen = iwe->len;
-	if (custom + clen > end)
-		return;
-	maxrate = 0;
-	while (((ssize_t) clen) >= (ssize_t) sizeof(struct iw_param)) {
-		/* Note: may be misaligned, make a local, aligned copy */
-		os_memcpy(&p, custom, sizeof(struct iw_param));
-		if (p.value > maxrate)
-			maxrate = p.value;
-		clen -= sizeof(struct iw_param);
-		custom += sizeof(struct iw_param);
-	}
-
-	/* Convert the maxrate from WE-style (b/s units) to
-	 * 802.11 rates (500000 b/s units).
-	 */
-	res->maxrate = maxrate / 500000;
-}
-
-
-static void wext_get_scan_iwevgenie(struct iw_event *iwe,
-				    struct wext_scan_data *res, char *custom,
-				    char *end)
-{
-	char *genie, *gpos, *gend;
-	u8 *tmp;
-
-	if (iwe->u.data.length == 0)
-		return;
-
-	gpos = genie = custom;
-	gend = genie + iwe->u.data.length;
-	if (gend > end) {
-		wpa_printf(MSG_INFO, "IWEVGENIE overflow");
-		return;
-	}
-
-	tmp = os_realloc(res->ie, res->ie_len + gend - gpos);
-	if (tmp == NULL)
-		return;
-	os_memcpy(tmp + res->ie_len, gpos, gend - gpos);
-	res->ie = tmp;
-	res->ie_len += gend - gpos;
-}
-
-
-static void wext_get_scan_custom(struct iw_event *iwe,
-				 struct wext_scan_data *res, char *custom,
-				 char *end)
-{
-	size_t clen;
-	u8 *tmp;
-
-	clen = iwe->u.data.length;
-	if (custom + clen > end)
-		return;
-
-	if (clen > 7 && os_strncmp(custom, "wpa_ie=", 7) == 0) {
-		char *spos;
-		int bytes;
-		spos = custom + 7;
-		bytes = custom + clen - spos;
-		if (bytes & 1 || bytes == 0)
-			return;
-		bytes /= 2;
-		tmp = os_realloc(res->ie, res->ie_len + bytes);
-		if (tmp == NULL)
-			return;
-		hexstr2bin(spos, tmp + res->ie_len, bytes);
-		res->ie = tmp;
-		res->ie_len += bytes;
-	} else if (clen > 7 && os_strncmp(custom, "rsn_ie=", 7) == 0) {
-		char *spos;
-		int bytes;
-		spos = custom + 7;
-		bytes = custom + clen - spos;
-		if (bytes & 1 || bytes == 0)
-			return;
-		bytes /= 2;
-		tmp = os_realloc(res->ie, res->ie_len + bytes);
-		if (tmp == NULL)
-			return;
-		hexstr2bin(spos, tmp + res->ie_len, bytes);
-		res->ie = tmp;
-		res->ie_len += bytes;
-	} else if (clen > 4 && os_strncmp(custom, "tsf=", 4) == 0) {
-		char *spos;
-		int bytes;
-		u8 bin[8];
-		spos = custom + 4;
-		bytes = custom + clen - spos;
-		if (bytes != 16) {
-			wpa_printf(MSG_INFO, "Invalid TSF length (%d)", bytes);
-			return;
-		}
-		bytes /= 2;
-		hexstr2bin(spos, bin, bytes);
-		res->res.tsf += WPA_GET_BE64(bin);
-	}
-}
-
-
-static int wext_19_iw_point(struct wpa_driver_nl80211_data *drv, u16 cmd)
-{
-	return drv->we_version_compiled > 18 &&
-		(cmd == SIOCGIWESSID || cmd == SIOCGIWENCODE ||
-		 cmd == IWEVGENIE || cmd == IWEVCUSTOM);
-}
-
-
-static void wpa_driver_nl80211_add_scan_entry(struct wpa_scan_results *res,
-					   struct wext_scan_data *data)
-{
+static int bss_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *bss[NL80211_BSS_MAX + 1];
+	static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+		[NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
+		[NL80211_BSS_TSF] = { .type = NLA_U64 },
+		[NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
+		[NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
+		[NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
+		[NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
+	};
+	struct wpa_scan_results *res = arg;
 	struct wpa_scan_res **tmp;
 	struct wpa_scan_res *r;
-	size_t extra_len;
-	u8 *pos, *end, *ssid_ie = NULL, *rate_ie = NULL;
-
-	/* Figure out whether we need to fake any IEs */
-	pos = data->ie;
-	end = pos + data->ie_len;
-	while (pos && pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
-			break;
-		if (pos[0] == WLAN_EID_SSID)
-			ssid_ie = pos;
-		else if (pos[0] == WLAN_EID_SUPP_RATES)
-			rate_ie = pos;
-		else if (pos[0] == WLAN_EID_EXT_SUPP_RATES)
-			rate_ie = pos;
-		pos += 2 + pos[1];
-	}
-
-	extra_len = 0;
-	if (ssid_ie == NULL)
-		extra_len += 2 + data->ssid_len;
-	if (rate_ie == NULL && data->maxrate)
-		extra_len += 3;
-
-	r = os_zalloc(sizeof(*r) + extra_len + data->ie_len);
+	const u8 *ie;
+	size_t ie_len;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb[NL80211_ATTR_BSS])
+		return NL_SKIP;
+	if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
+			     bss_policy))
+		return NL_SKIP;
+	if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
+		ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+		ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+	} else {
+		ie = NULL;
+		ie_len = 0;
+	}
+
+	r = os_zalloc(sizeof(*r) + ie_len);
 	if (r == NULL)
-		return;
-	os_memcpy(r, &data->res, sizeof(*r));
-	r->ie_len = extra_len + data->ie_len;
-	pos = (u8 *) (r + 1);
-	if (ssid_ie == NULL) {
-		/*
-		 * Generate a fake SSID IE since the driver did not report
-		 * a full IE list.
-		 */
-		*pos++ = WLAN_EID_SSID;
-		*pos++ = data->ssid_len;
-		os_memcpy(pos, data->ssid, data->ssid_len);
-		pos += data->ssid_len;
-	}
-	if (rate_ie == NULL && data->maxrate) {
-		/*
-		 * Generate a fake Supported Rates IE since the driver did not
-		 * report a full IE list.
-		 */
-		*pos++ = WLAN_EID_SUPP_RATES;
-		*pos++ = 1;
-		*pos++ = data->maxrate;
-	}
-	if (data->ie)
-		os_memcpy(pos, data->ie, data->ie_len);
+		return NL_SKIP;
+	if (bss[NL80211_BSS_BSSID])
+		os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]),
+			  ETH_ALEN);
+	if (bss[NL80211_BSS_FREQUENCY])
+		r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+	if (bss[NL80211_BSS_BEACON_INTERVAL])
+		r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]);
+	if (bss[NL80211_BSS_CAPABILITY])
+		r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
+	if (bss[NL80211_BSS_SIGNAL_UNSPEC])
+		r->qual = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
+	if (bss[NL80211_BSS_SIGNAL_MBM])
+		r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
+	if (bss[NL80211_BSS_TSF])
+		r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]);
+	r->ie_len = ie_len;
+	if (ie)
+		os_memcpy(r + 1, ie, ie_len);
 
 	tmp = os_realloc(res->res,
 			 (res->num + 1) * sizeof(struct wpa_scan_res *));
 	if (tmp == NULL) {
 		os_free(r);
-		return;
+		return NL_SKIP;
 	}
 	tmp[res->num++] = r;
 	res->res = tmp;
-}
-				      
+
+	return NL_SKIP;
+}
+
 
 /**
  * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
@@ -1853,98 +1761,34 @@
 wpa_driver_nl80211_get_scan_results(void *priv)
 {
 	struct wpa_driver_nl80211_data *drv = priv;
-	size_t ap_num = 0, len;
-	int first;
-	u8 *res_buf;
-	struct iw_event iwe_buf, *iwe = &iwe_buf;
-	char *pos, *end, *custom;
+	struct nl_msg *msg;
 	struct wpa_scan_results *res;
-	struct wext_scan_data data;
-
-	res_buf = wpa_driver_nl80211_giwscan(drv, &len);
-	if (res_buf == NULL)
-		return NULL;
-
-	ap_num = 0;
-	first = 1;
+	int ret;
 
 	res = os_zalloc(sizeof(*res));
-	if (res == NULL) {
-		os_free(res_buf);
-		return NULL;
-	}
-
-	pos = (char *) res_buf;
-	end = (char *) res_buf + len;
-	os_memset(&data, 0, sizeof(data));
-
-	while (pos + IW_EV_LCP_LEN <= end) {
-		/* Event data may be unaligned, so make a local, aligned copy
-		 * before processing. */
-		os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
-		if (iwe->len <= IW_EV_LCP_LEN)
-			break;
-
-		custom = pos + IW_EV_POINT_LEN;
-		if (wext_19_iw_point(drv, iwe->cmd)) {
-			/* WE-19 removed the pointer from struct iw_point */
-			char *dpos = (char *) &iwe_buf.u.data.length;
-			int dlen = dpos - (char *) &iwe_buf;
-			os_memcpy(dpos, pos + IW_EV_LCP_LEN,
-				  sizeof(struct iw_event) - dlen);
-		} else {
-			os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
-			custom += IW_EV_POINT_OFF;
-		}
-
-		switch (iwe->cmd) {
-		case SIOCGIWAP:
-			if (!first)
-				wpa_driver_nl80211_add_scan_entry(res, &data);
-			first = 0;
-			os_free(data.ie);
-			os_memset(&data, 0, sizeof(data));
-			os_memcpy(data.res.bssid,
-				  iwe->u.ap_addr.sa_data, ETH_ALEN);
-			break;
-		case SIOCGIWMODE:
-			wext_get_scan_mode(iwe, &data);
-			break;
-		case SIOCGIWESSID:
-			wext_get_scan_ssid(iwe, &data, custom, end);
-			break;
-		case SIOCGIWFREQ:
-			wext_get_scan_freq(iwe, &data);
-			break;
-		case IWEVQUAL:
-			wext_get_scan_qual(iwe, &data);
-			break;
-		case SIOCGIWENCODE:
-			wext_get_scan_encode(iwe, &data);
-			break;
-		case SIOCGIWRATE:
-			wext_get_scan_rate(iwe, &data, pos, end);
-			break;
-		case IWEVGENIE:
-			wext_get_scan_iwevgenie(iwe, &data, custom, end);
-			break;
-		case IWEVCUSTOM:
-			wext_get_scan_custom(iwe, &data, custom, end);
-			break;
-		}
-
-		pos += iwe->len;
-	}
-	os_free(res_buf);
-	res_buf = NULL;
-	if (!first)
-		wpa_driver_nl80211_add_scan_entry(res, &data);
-	os_free(data.ie);
-
-	wpa_printf(MSG_DEBUG, "Received %lu bytes of scan results (%lu BSSes)",
-		   (unsigned long) len, (unsigned long) res->num);
-
-	return res;
+	if (res == NULL)
+		return 0;
+	msg = nlmsg_alloc();
+	if (!msg)
+		goto nla_put_failure;
+
+	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP,
+		    NL80211_CMD_GET_SCAN, 0);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+	ret = send_and_recv_msgs(drv, msg, bss_info_handler, res);
+	msg = NULL;
+	if (ret == 0) {
+		wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)",
+			   (unsigned long) res->num);
+		return res;
+	}
+	wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+		   "(%s)", ret, strerror(-ret));
+nla_put_failure:
+	nlmsg_free(msg);
+	wpa_scan_results_free(res);
+	return NULL;
 }
 
 
@@ -2895,6 +2739,7 @@
 	.get_capa = wpa_driver_nl80211_get_capa,
 	.set_operstate = wpa_driver_nl80211_set_operstate,
 	.set_country = wpa_driver_nl80211_set_country,
+	.set_probe_req_ie = wpa_driver_nl80211_set_probe_req_ie,
 #ifdef CONFIG_CLIENT_MLME
 	.get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data,
 	.set_channel = wpa_driver_nl80211_set_channel,

Modified: wpasupplicant/branches/upstream/current/src/drivers/driver_privsep.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/drivers/driver_privsep.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/drivers/driver_privsep.c (original)
+++ wpasupplicant/branches/upstream/current/src/drivers/driver_privsep.c Sun Feb 15 19:38:55 2009
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - privilege separated driver interface
- * Copyright (c) 2007, Jouni Malinen <j at w1.fi>
+ * Copyright (c) 2007-2009, Jouni Malinen <j at w1.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
@@ -443,6 +443,22 @@
 }
 
 
+static void wpa_driver_privsep_event_sta_rx(void *ctx, u8 *buf, size_t len)
+{
+#ifdef CONFIG_CLIENT_MLME
+	struct ieee80211_rx_status *rx_status;
+
+	if (len < sizeof(*rx_status))
+		return;
+	rx_status = (struct ieee80211_rx_status *) buf;
+	buf += sizeof(*rx_status);
+	len -= sizeof(*rx_status);
+
+	wpa_supplicant_sta_rx(ctx, buf, len, rx_status);
+#endif /* CONFIG_CLIENT_MLME */
+}
+
+
 static void wpa_driver_privsep_receive(int sock, void *eloop_ctx,
 				       void *sock_ctx)
 {
@@ -476,8 +492,8 @@
 	os_memcpy(&event, buf, sizeof(int));
 	event_buf = &buf[sizeof(int)];
 	event_len = res - sizeof(int);
-	wpa_printf(MSG_DEBUG, "privsep: Event %d received (len=%d)",
-		   event, event_len);
+	wpa_printf(MSG_DEBUG, "privsep: Event %d received (len=%lu)",
+		   event, (unsigned long) event_len);
 
 	e = event;
 	switch (e) {
@@ -518,6 +534,11 @@
 	case PRIVSEP_EVENT_RX_EAPOL:
 		wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf,
 						  event_len);
+		break;
+	case PRIVSEP_EVENT_STA_RX:
+		wpa_driver_privsep_event_sta_rx(drv->ctx, event_buf,
+						event_len);
+		break;
 	}
 
 	os_free(buf);
@@ -732,6 +753,15 @@
 	wpa_printf(MSG_DEBUG, "%s mode=%d", __func__, mode);
 	return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_MODE, &mode, sizeof(mode),
 			    NULL, NULL);
+}
+
+
+static int wpa_driver_privsep_set_country(void *priv, const char *alpha2)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	wpa_printf(MSG_DEBUG, "%s country='%s'", __func__, alpha2);
+	return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_COUNTRY, alpha2,
+			    os_strlen(alpha2), NULL, NULL);
 }
 
 
@@ -775,7 +805,7 @@
 	wpa_driver_privsep_get_scan_results2,
 	NULL /* set_probe_req_ie */,
 	wpa_driver_privsep_set_mode,
-	NULL /* set_country */,
+	wpa_driver_privsep_set_country,
 	NULL /* global_init */,
 	NULL /* global_deinit */,
 	NULL /* init2 */,

Modified: wpasupplicant/branches/upstream/current/src/drivers/driver_roboswitch.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/drivers/driver_roboswitch.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/drivers/driver_roboswitch.c (original)
+++ wpasupplicant/branches/upstream/current/src/drivers/driver_roboswitch.c Sun Feb 15 19:38:55 2009
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - roboswitch driver interface
- * Copyright (c) 2008 Jouke Witteveen
+ * Copyright (c) 2008-2009 Jouke Witteveen
  *
  * 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
@@ -47,7 +47,9 @@
 
 /* VLAN page registers */
 #define ROBO_VLAN_ACCESS	0x06	/* VLAN table Access register */
+#define ROBO_VLAN_ACCESS_5365	0x08	/* VLAN table Access register (5365) */
 #define ROBO_VLAN_READ		0x0C	/* VLAN read register */
+#define ROBO_VLAN_MAX		0xFF	/* Maximum number of VLANs */
 
 
 static const u8 pae_group_addr[ETH_ALEN] =
@@ -240,32 +242,44 @@
 				       const u8 *addr)
 {
 	int i;
-	u8 mport[4] = { ROBO_ARLCTRL_VEC_1, ROBO_ARLCTRL_ADDR_1,
-			ROBO_ARLCTRL_VEC_2, ROBO_ARLCTRL_ADDR_2 };
 	u16 _read[3], zero = 0;
-	/* same as at join */
-	u16 addr_word[ETH_ALEN / 2];
+	u16 addr_word[ETH_ALEN / 2]; /* same as at join */
 
 	for (i = 0; i < ETH_ALEN; i += 2)
 		addr_word[(ETH_ALEN - i) / 2 - 1] = WPA_GET_BE16(addr + i);
 
-	/* find our address/vector pair */
-	for (i = 0; i < 4; i += 2) {
-		wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, mport[i],
-					   _read, 1);
-		if (_read[0] == drv->ports) {
-			wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
-						   mport[i + 1], _read, 3);
-			if (os_memcmp(_read, addr_word, 6) == 0)
-				break;
+	/* check if multiport address 1 was used */
+	wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1,
+				   _read, 1);
+	if (_read[0] == drv->ports) {
+		wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					   ROBO_ARLCTRL_ADDR_1, _read, 3);
+		if (os_memcmp(_read, addr_word, 6) == 0) {
+			wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+						    ROBO_ARLCTRL_VEC_1, &zero,
+						    1);
+			goto clean_up;
 		}
 	}
-	/* check if we found our address/vector pair and deactivate it */
-	if (i == 4)
-		return -1;
-	wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, mport[i], &zero,
-				    1);
-
+
+	/* check if multiport address 2 was used */
+	wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_2,
+				   _read, 1);
+	if (_read[0] == drv->ports) {
+		wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					   ROBO_ARLCTRL_ADDR_2, _read, 3);
+		if (os_memcmp(_read, addr_word, 6) == 0) {
+			wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+						    ROBO_ARLCTRL_VEC_2, &zero,
+						    1);
+			goto clean_up;
+		}
+	}
+
+	/* used multiport address not found */
+	return -1;
+
+clean_up:
 	/* leave the multiport registers in a sane state */
 	wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1,
 				   _read, 1);
@@ -306,7 +320,7 @@
 {
 	struct wpa_driver_roboswitch_data *drv;
 	int len = -1, sep = -1;
-	u16 vlan = 0, vlan_read[2];
+	u16 vlan_max = ROBO_VLAN_MAX, vlan = 0, vlan_read[2];
 
 	drv = os_zalloc(sizeof(*drv));
 	if (drv == NULL) return NULL;
@@ -339,7 +353,7 @@
 		}
 		vlan *= 10;
 		vlan += ifname[sep] - '0';
-		if (vlan > 255) {
+		if (vlan > ROBO_VLAN_MAX) {
 			wpa_printf(MSG_INFO, "%s: VLAN out of range in "
 				   "interface name %s", __func__, ifname);
 			os_free(drv);
@@ -368,10 +382,19 @@
 		return NULL;
 	}
 
+	/* set the read bit */
 	vlan |= 1 << 13;
-	/* The BCM5365 uses a different register and is not accounted for. */
+	/* set and read back to see if the register can be used */
 	wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS,
-				    &vlan, 1);
+				    &vlan_max, 1);
+	wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS,
+				   &vlan_max, 1);
+	if (vlan_max == ROBO_VLAN_MAX) /* pre-5365 */
+		wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE,
+					    ROBO_VLAN_ACCESS, &vlan, 1);
+	else /* 5365 uses a different register */
+		wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE,
+					    ROBO_VLAN_ACCESS_5365, &vlan, 1);
 	wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_READ,
 				   vlan_read, 2);
 	if (!(vlan_read[1] & (1 << 4))) {

Modified: wpasupplicant/branches/upstream/current/src/drivers/driver_wext.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/drivers/driver_wext.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/drivers/driver_wext.c (original)
+++ wpasupplicant/branches/upstream/current/src/drivers/driver_wext.c Sun Feb 15 19:38:55 2009
@@ -1140,10 +1140,12 @@
 		if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0)
 			break;
 
-		if (errno == E2BIG && res_buf_len < 100000) {
+		if (errno == E2BIG && res_buf_len < 65535) {
 			os_free(res_buf);
 			res_buf = NULL;
 			res_buf_len *= 2;
+			if (res_buf_len > 65535)
+				res_buf_len = 65535; /* 16-bit length field */
 			wpa_printf(MSG_DEBUG, "Scan results did not fit - "
 				   "trying larger buffer (%lu bytes)",
 				   (unsigned long) res_buf_len);
@@ -1690,6 +1692,7 @@
 	os_memset(&iwr, 0, sizeof(iwr));
 	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
 	iwr.u.encoding.flags = key_idx + 1;
+	iwr.u.encoding.flags |= IW_ENCODE_TEMP;
 	if (alg == WPA_ALG_NONE)
 		iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
 	iwr.u.encoding.pointer = (caddr_t) ext;
@@ -1819,6 +1822,7 @@
 	os_memset(&iwr, 0, sizeof(iwr));
 	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
 	iwr.u.encoding.flags = key_idx + 1;
+	iwr.u.encoding.flags |= IW_ENCODE_TEMP;
 	if (alg == WPA_ALG_NONE)
 		iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
 	iwr.u.encoding.pointer = (caddr_t) key;
@@ -1833,6 +1837,7 @@
 		os_memset(&iwr, 0, sizeof(iwr));
 		os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
 		iwr.u.encoding.flags = key_idx + 1;
+		iwr.u.encoding.flags |= IW_ENCODE_TEMP;
 		iwr.u.encoding.pointer = (caddr_t) NULL;
 		iwr.u.encoding.length = 0;
 		if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
@@ -1893,12 +1898,35 @@
 }
 
 
+static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv)
+{
+	const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+	u8 ssid[32];
+	int i;
+
+	/*
+	 * Clear the BSSID selection and set a random SSID to make sure the
+	 * driver will not be trying to associate with something even if it
+	 * does not understand SIOCSIWMLME commands (or tries to associate
+	 * automatically after deauth/disassoc).
+	 */
+	wpa_driver_wext_set_bssid(drv, null_bssid);
+
+	for (i = 0; i < 32; i++)
+		ssid[i] = rand() & 0xFF;
+	wpa_driver_wext_set_ssid(drv, ssid, 32);
+}
+
+
 static int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr,
 					  int reason_code)
 {
 	struct wpa_driver_wext_data *drv = priv;
+	int ret;
 	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
-	return wpa_driver_wext_mlme(drv, addr, IW_MLME_DEAUTH, reason_code);
+	wpa_driver_wext_disconnect(drv);
+	ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DEAUTH, reason_code);
+	return ret;
 }
 
 
@@ -1906,9 +1934,11 @@
 					int reason_code)
 {
 	struct wpa_driver_wext_data *drv = priv;
+	int ret;
 	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
-	return wpa_driver_wext_mlme(drv, addr, IW_MLME_DISASSOC,
-				    reason_code);
+	ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DISASSOC, reason_code);
+	wpa_driver_wext_disconnect(drv);
+	return ret;
 }
 
 

Modified: wpasupplicant/branches/upstream/current/src/drivers/scan_helpers.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/drivers/scan_helpers.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/drivers/scan_helpers.c (original)
+++ wpasupplicant/branches/upstream/current/src/drivers/scan_helpers.c Sun Feb 15 19:38:55 2009
@@ -156,10 +156,13 @@
 		return -1;
 
 	/* best/max rate preferred if signal level close enough XXX */
-	maxrate_a = wpa_scan_get_max_rate(wa);
-	maxrate_b = wpa_scan_get_max_rate(wb);
-	if (maxrate_a != maxrate_b && abs(wb->level - wa->level) < 5)
-		return maxrate_b - maxrate_a;
+	if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) ||
+	    (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
+		maxrate_a = wpa_scan_get_max_rate(wa);
+		maxrate_b = wpa_scan_get_max_rate(wb);
+		if (maxrate_a != maxrate_b)
+			return maxrate_b - maxrate_a;
+	}
 
 	/* use freq for channel preference */
 

Modified: wpasupplicant/branches/upstream/current/src/eap_common/Makefile
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/eap_common/Makefile?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/eap_common/Makefile (original)
+++ wpasupplicant/branches/upstream/current/src/eap_common/Makefile Sun Feb 15 19:38:55 2009
@@ -4,3 +4,6 @@
 clean:
 	for d in $(SUBDIRS); do make -C $$d clean; done
 	rm -f *~ *.o *.d
+
+install:
+	@echo Nothing to be made.

Modified: wpasupplicant/branches/upstream/current/src/eap_peer/Makefile
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/eap_peer/Makefile?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/eap_peer/Makefile (original)
+++ wpasupplicant/branches/upstream/current/src/eap_peer/Makefile Sun Feb 15 19:38:55 2009
@@ -3,4 +3,10 @@
 
 clean:
 	for d in $(SUBDIRS); do make -C $$d clean; done
-	rm -f *~ *.o *.d
+	rm -f *~ *.o *.so *.d
+
+install:
+	if ls *.so >/dev/null 2>&1; then \
+		install -d $(DESTDIR)$(LIBDIR)/wpa_supplicant && \
+		cp *.so $(DESTDIR)$(LIBDIR)/wpa_supplicant \
+	; fi

Modified: wpasupplicant/branches/upstream/current/src/eap_peer/eap.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/eap_peer/eap.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/eap_peer/eap.c (original)
+++ wpasupplicant/branches/upstream/current/src/eap_peer/eap.c Sun Feb 15 19:38:55 2009
@@ -134,7 +134,8 @@
 {
 	SM_ENTRY(EAP, INITIALIZE);
 	if (sm->fast_reauth && sm->m && sm->m->has_reauth_data &&
-	    sm->m->has_reauth_data(sm, sm->eap_method_priv)) {
+	    sm->m->has_reauth_data(sm, sm->eap_method_priv) &&
+	    !sm->prev_failure) {
 		wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for "
 			   "fast reauthentication");
 		sm->m->deinit_for_reauth(sm, sm->eap_method_priv);
@@ -165,6 +166,7 @@
 	eapol_set_bool(sm, EAPOL_eapResp, FALSE);
 	eapol_set_bool(sm, EAPOL_eapNoResp, FALSE);
 	sm->num_rounds = 0;
+	sm->prev_failure = 0;
 }
 
 
@@ -505,6 +507,8 @@
 
 	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
 		"EAP authentication failed");
+
+	sm->prev_failure = 1;
 }
 
 

Modified: wpasupplicant/branches/upstream/current/src/eap_peer/eap_aka.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/eap_peer/eap_aka.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/eap_peer/eap_aka.c (original)
+++ wpasupplicant/branches/upstream/current/src/eap_peer/eap_aka.c Sun Feb 15 19:38:55 2009
@@ -60,6 +60,7 @@
 	u8 *network_name;
 	size_t network_name_len;
 	u16 kdf;
+	int kdf_negotiation;
 };
 
 
@@ -665,6 +666,7 @@
 {
 	struct eap_sim_msg *msg;
 
+	data->kdf_negotiation = 1;
 	data->kdf = kdf;
 	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d) (KDF "
 		   "select)", id);
@@ -704,7 +706,7 @@
 	/* The only allowed (and required) duplication of a KDF is the addition
 	 * of the selected KDF into the beginning of the list. */
 
-	if (data->kdf) {
+	if (data->kdf_negotiation) {
 		if (attr->kdf[0] != data->kdf) {
 			wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
 				   "accept the selected KDF");
@@ -840,7 +842,13 @@
 #ifdef EAP_AKA_PRIME
 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
 		/* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the
-		 * needed 6-octet SQN ^AK for CK',IK' derivation */
+		 * needed 6-octet SQN ^ AK for CK',IK' derivation */
+		u16 amf = WPA_GET_BE16(data->autn + 6);
+		if (!(amf & 0x8000)) {
+			wpa_printf(MSG_WARNING, "EAP-AKA': AMF separation bit "
+				   "not set (AMF=0x%4x)", amf);
+			return eap_aka_authentication_reject(data, id);
+		}
 		eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik,
 						 data->autn,
 						 data->network_name,
@@ -1245,6 +1253,7 @@
 	wpabuf_free(data->id_msgs);
 	data->id_msgs = NULL;
 	data->use_result_ind = 0;
+	data->kdf_negotiation = 0;
 }
 
 

Modified: wpasupplicant/branches/upstream/current/src/eap_peer/eap_i.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/eap_peer/eap_i.h?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/eap_peer/eap_i.h (original)
+++ wpasupplicant/branches/upstream/current/src/eap_peer/eap_i.h Sun Feb 15 19:38:55 2009
@@ -333,6 +333,8 @@
 	int force_disabled;
 
 	struct wps_context *wps;
+
+	int prev_failure;
 };
 
 const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);

Modified: wpasupplicant/branches/upstream/current/src/eap_peer/eap_tls_common.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/eap_peer/eap_tls_common.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/eap_peer/eap_tls_common.c (original)
+++ wpasupplicant/branches/upstream/current/src/eap_peer/eap_tls_common.c Sun Feb 15 19:38:55 2009
@@ -904,7 +904,7 @@
 
 	if (in_data) {
 		eap_peer_tls_reset_output(data);
-		len = wpabuf_len(in_data) + 100;
+		len = wpabuf_len(in_data) + 300;
 		data->tls_out = os_malloc(len);
 		if (data->tls_out == NULL)
 			return -1;

Modified: wpasupplicant/branches/upstream/current/src/eap_peer/eap_wsc.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/eap_peer/eap_wsc.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/eap_peer/eap_wsc.c (original)
+++ wpasupplicant/branches/upstream/current/src/eap_peer/eap_wsc.c Sun Feb 15 19:38:55 2009
@@ -405,6 +405,7 @@
 		eap_wsc_state(data, MESG);
 		break;
 	case WPS_FAILURE:
+	case WPS_PENDING:
 		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
 		eap_wsc_state(data, FAIL);
 		break;

Modified: wpasupplicant/branches/upstream/current/src/eap_server/Makefile
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/eap_server/Makefile?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/eap_server/Makefile (original)
+++ wpasupplicant/branches/upstream/current/src/eap_server/Makefile Sun Feb 15 19:38:55 2009
@@ -4,3 +4,6 @@
 clean:
 	for d in $(SUBDIRS); do make -C $$d clean; done
 	rm -f *~ *.o *.d
+
+install:
+	@echo Nothing to be made.

Modified: wpasupplicant/branches/upstream/current/src/eap_server/eap_aka.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/eap_server/eap_aka.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/eap_server/eap_aka.c (original)
+++ wpasupplicant/branches/upstream/current/src/eap_server/eap_aka.c Sun Feb 15 19:38:55 2009
@@ -369,15 +369,19 @@
 			       EAP_AKA_SUBTYPE_CHALLENGE);
 	wpa_printf(MSG_DEBUG, "   AT_RAND");
 	eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, data->rand, EAP_AKA_RAND_LEN);
+	wpa_printf(MSG_DEBUG, "   AT_AUTN");
 	eap_sim_msg_add(msg, EAP_SIM_AT_AUTN, 0, data->autn, EAP_AKA_AUTN_LEN);
 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
 		if (data->kdf) {
 			/* Add the selected KDF into the beginning */
+			wpa_printf(MSG_DEBUG, "   AT_KDF");
 			eap_sim_msg_add(msg, EAP_SIM_AT_KDF, data->kdf,
 					NULL, 0);
 		}
+		wpa_printf(MSG_DEBUG, "   AT_KDF");
 		eap_sim_msg_add(msg, EAP_SIM_AT_KDF, EAP_AKA_PRIME_KDF,
 				NULL, 0);
+		wpa_printf(MSG_DEBUG, "   AT_KDF_INPUT");
 		eap_sim_msg_add(msg, EAP_SIM_AT_KDF_INPUT,
 				data->network_name_len,
 				data->network_name, data->network_name_len);

Modified: wpasupplicant/branches/upstream/current/src/eap_server/eap_wsc.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/eap_server/eap_wsc.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/eap_server/eap_wsc.c (original)
+++ wpasupplicant/branches/upstream/current/src/eap_server/eap_wsc.c Sun Feb 15 19:38:55 2009
@@ -15,6 +15,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "eloop.h"
 #include "eap_i.h"
 #include "eap_common/eap_wsc_common.h"
 #include "wps/wps.h"
@@ -29,6 +30,7 @@
 	size_t out_used;
 	size_t fragment_size;
 	struct wps_data *wps;
+	int ext_reg_timeout;
 };
 
 
@@ -62,6 +64,21 @@
 }
 
 
+static void eap_wsc_ext_reg_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct eap_sm *sm = eloop_ctx;
+	struct eap_wsc_data *data = timeout_ctx;
+
+	if (sm->method_pending != METHOD_PENDING_WAIT)
+		return;
+
+	wpa_printf(MSG_DEBUG, "EAP-WSC: Timeout while waiting for an External "
+		   "Registrar");
+	data->ext_reg_timeout = 1;
+	eap_sm_pending_cb(sm);
+}
+
+
 static void * eap_wsc_init(struct eap_sm *sm)
 {
 	struct eap_wsc_data *data;
@@ -123,6 +140,7 @@
 static void eap_wsc_reset(struct eap_sm *sm, void *priv)
 {
 	struct eap_wsc_data *data = priv;
+	eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
 	wpabuf_free(data->in_buf);
 	wpabuf_free(data->out_buf);
 	wps_deinit(data->wps);
@@ -324,6 +342,12 @@
 	enum wps_process_res res;
 	struct wpabuf tmpbuf;
 
+	eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
+	if (data->ext_reg_timeout) {
+		eap_wsc_state(data, FAIL);
+		return;
+	}
+
 	pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
 			       respData, &len);
 	if (pos == NULL || len < 2)
@@ -408,6 +432,13 @@
 	case WPS_FAILURE:
 		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
 		eap_wsc_state(data, FAIL);
+		break;
+	case WPS_PENDING:
+		eap_wsc_state(data, MSG);
+		sm->method_pending = METHOD_PENDING_WAIT;
+		eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
+		eloop_register_timeout(5, 0, eap_wsc_ext_reg_timeout,
+				       sm, data);
 		break;
 	}
 

Modified: wpasupplicant/branches/upstream/current/src/eapol_supp/Makefile
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/eapol_supp/Makefile?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/eapol_supp/Makefile (original)
+++ wpasupplicant/branches/upstream/current/src/eapol_supp/Makefile Sun Feb 15 19:38:55 2009
@@ -4,3 +4,6 @@
 clean:
 	for d in $(SUBDIRS); do make -C $$d clean; done
 	rm -f *~ *.o *.d
+
+install:
+	@echo Nothing to be made.

Modified: wpasupplicant/branches/upstream/current/src/eapol_supp/eapol_supp_sm.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/eapol_supp/eapol_supp_sm.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/eapol_supp/eapol_supp_sm.c (original)
+++ wpasupplicant/branches/upstream/current/src/eapol_supp/eapol_supp_sm.c Sun Feb 15 19:38:55 2009
@@ -1165,6 +1165,31 @@
 		sm->dot1xSuppEapLengthErrorFramesRx++;
 		return 0;
 	}
+#ifdef CONFIG_WPS
+	if (sm->conf.workaround &&
+	    plen < len - sizeof(*hdr) &&
+	    hdr->type == IEEE802_1X_TYPE_EAP_PACKET &&
+	    len - sizeof(*hdr) > sizeof(struct eap_hdr)) {
+		const struct eap_hdr *ehdr =
+			(const struct eap_hdr *) (hdr + 1);
+		u16 elen;
+
+		elen = be_to_host16(ehdr->length);
+		if (elen > plen && elen <= len - sizeof(*hdr)) {
+			/*
+			 * Buffalo WHR-G125 Ver.1.47 seems to send EAP-WPS
+			 * packets with too short EAPOL header length field
+			 * (14 octets). This is fixed in firmware Ver.1.49.
+			 * As a workaround, fix the EAPOL header based on the
+			 * correct length in the EAP packet.
+			 */
+			wpa_printf(MSG_DEBUG, "EAPOL: Workaround - fix EAPOL "
+				   "payload length based on EAP header: "
+				   "%d -> %d", (int) plen, elen);
+			plen = elen;
+		}
+	}
+#endif /* CONFIG_WPS */
 	data_len = plen + sizeof(*hdr);
 
 	switch (hdr->type) {
@@ -1349,6 +1374,7 @@
 	sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys;
 	sm->conf.required_keys = conf->required_keys;
 	sm->conf.fast_reauth = conf->fast_reauth;
+	sm->conf.workaround = conf->workaround;
 	if (sm->eap) {
 		eap_set_fast_reauth(sm->eap, conf->fast_reauth);
 		eap_set_workaround(sm->eap, conf->workaround);

Modified: wpasupplicant/branches/upstream/current/src/hlr_auc_gw/Makefile
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/hlr_auc_gw/Makefile?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/hlr_auc_gw/Makefile (original)
+++ wpasupplicant/branches/upstream/current/src/hlr_auc_gw/Makefile Sun Feb 15 19:38:55 2009
@@ -4,3 +4,6 @@
 clean:
 	for d in $(SUBDIRS); do make -C $$d clean; done
 	rm -f *~ *.o *.d
+
+install:
+	@echo Nothing to be made.

Modified: wpasupplicant/branches/upstream/current/src/hlr_auc_gw/hlr_auc_gw.milenage_db
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/hlr_auc_gw/hlr_auc_gw.milenage_db?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/hlr_auc_gw/hlr_auc_gw.milenage_db (original)
+++ wpasupplicant/branches/upstream/current/src/hlr_auc_gw/hlr_auc_gw.milenage_db Sun Feb 15 19:38:55 2009
@@ -7,3 +7,7 @@
 
 # IMSI Ki OPc AMF SQN
 232010000000000 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000
+
+# These values are from Test Set 19 which has the AMF separation bit set to 1
+# and as such, is suitable for EAP-AKA' test.
+555444333222111 5122250214c33e723a5dd523fc145fc0 981d464c7c52eb6e5036234984ad0bcf c3ab 16f3b3f70fc1

Modified: wpasupplicant/branches/upstream/current/src/l2_packet/Makefile
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/l2_packet/Makefile?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/l2_packet/Makefile (original)
+++ wpasupplicant/branches/upstream/current/src/l2_packet/Makefile Sun Feb 15 19:38:55 2009
@@ -4,3 +4,6 @@
 clean:
 	for d in $(SUBDIRS); do make -C $$d clean; done
 	rm -f *~ *.o *.d
+
+install:
+	@echo Nothing to be made.

Modified: wpasupplicant/branches/upstream/current/src/radius/Makefile
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/radius/Makefile?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/radius/Makefile (original)
+++ wpasupplicant/branches/upstream/current/src/radius/Makefile Sun Feb 15 19:38:55 2009
@@ -4,3 +4,6 @@
 clean:
 	for d in $(SUBDIRS); do make -C $$d clean; done
 	rm -f *~ *.o *.d
+
+install:
+	@echo Nothing to be made.

Modified: wpasupplicant/branches/upstream/current/src/radius/radius.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/radius/radius.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/radius/radius.c (original)
+++ wpasupplicant/branches/upstream/current/src/radius/radius.c Sun Feb 15 19:38:55 2009
@@ -293,7 +293,8 @@
 }
 
 
-int radius_msg_finish(struct radius_msg *msg, u8 *secret, size_t secret_len)
+int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
+		      size_t secret_len)
 {
 	if (secret) {
 		u8 auth[MD5_MAC_LEN];
@@ -364,7 +365,7 @@
 }
 
 
-void radius_msg_finish_acct(struct radius_msg *msg, u8 *secret,
+void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
 			    size_t secret_len)
 {
 	const u8 *addr[2];
@@ -899,7 +900,7 @@
 
 struct radius_ms_mppe_keys *
 radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
-		       u8 *secret, size_t secret_len)
+		       const u8 *secret, size_t secret_len)
 {
 	u8 *key;
 	size_t keylen;
@@ -940,7 +941,7 @@
 
 struct radius_ms_mppe_keys *
 radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
-			  u8 *secret, size_t secret_len)
+			  const u8 *secret, size_t secret_len)
 {
 	u8 *key;
 	size_t keylen;
@@ -1043,8 +1044,8 @@
  * in RFC 2865, Chap. 5.2 */
 struct radius_attr_hdr *
 radius_msg_add_attr_user_password(struct radius_msg *msg,
-				  u8 *data, size_t data_len,
-				  u8 *secret, size_t secret_len)
+				  const u8 *data, size_t data_len,
+				  const u8 *secret, size_t secret_len)
 {
 	u8 buf[128];
 	int padlen, i;

Modified: wpasupplicant/branches/upstream/current/src/radius/radius.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/radius/radius.h?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/radius/radius.h (original)
+++ wpasupplicant/branches/upstream/current/src/radius/radius.h Sun Feb 15 19:38:55 2009
@@ -207,10 +207,11 @@
 void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier);
 void radius_msg_free(struct radius_msg *msg);
 void radius_msg_dump(struct radius_msg *msg);
-int radius_msg_finish(struct radius_msg *msg, u8 *secret, size_t secret_len);
+int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
+		      size_t secret_len);
 int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
 			  size_t secret_len, const u8 *req_authenticator);
-void radius_msg_finish_acct(struct radius_msg *msg, u8 *secret,
+void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
 			    size_t secret_len);
 struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
 					    const u8 *data, size_t data_len);
@@ -229,10 +230,10 @@
 				   const u8 *data, size_t len);
 struct radius_ms_mppe_keys *
 radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
-		       u8 *secret, size_t secret_len);
+		       const u8 *secret, size_t secret_len);
 struct radius_ms_mppe_keys *
 radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
-			  u8 *secret, size_t secret_len);
+			  const u8 *secret, size_t secret_len);
 int radius_msg_add_mppe_keys(struct radius_msg *msg,
 			     const u8 *req_authenticator,
 			     const u8 *secret, size_t secret_len,
@@ -240,8 +241,8 @@
 			     const u8 *recv_key, size_t recv_key_len);
 struct radius_attr_hdr *
 radius_msg_add_attr_user_password(struct radius_msg *msg,
-				  u8 *data, size_t data_len,
-				  u8 *secret, size_t secret_len);
+				  const u8 *data, size_t data_len,
+				  const u8 *secret, size_t secret_len);
 int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len);
 int radius_msg_get_vlanid(struct radius_msg *msg);
 

Modified: wpasupplicant/branches/upstream/current/src/radius/radius_client.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/radius/radius_client.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/radius/radius_client.c (original)
+++ wpasupplicant/branches/upstream/current/src/radius/radius_client.c Sun Feb 15 19:38:55 2009
@@ -35,7 +35,8 @@
 struct radius_rx_handler {
 	RadiusRxResult (*handler)(struct radius_msg *msg,
 				  struct radius_msg *req,
-				  u8 *shared_secret, size_t shared_secret_len,
+				  const u8 *shared_secret,
+				  size_t shared_secret_len,
 				  void *data);
 	void *data;
 };
@@ -106,7 +107,7 @@
 			   RadiusType msg_type,
 			   RadiusRxResult (*handler)(struct radius_msg *msg,
 						     struct radius_msg *req,
-						     u8 *shared_secret,
+						     const u8 *shared_secret,
 						     size_t shared_secret_len,
 						     void *data),
 			   void *data)

Modified: wpasupplicant/branches/upstream/current/src/radius/radius_client.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/radius/radius_client.h?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/radius/radius_client.h (original)
+++ wpasupplicant/branches/upstream/current/src/radius/radius_client.h Sun Feb 15 19:38:55 2009
@@ -85,7 +85,7 @@
 			   RadiusType msg_type,
 			   RadiusRxResult (*handler)
 			   (struct radius_msg *msg, struct radius_msg *req,
-			    u8 *shared_secret, size_t shared_secret_len,
+			    const u8 *shared_secret, size_t shared_secret_len,
 			    void *data),
 			   void *data);
 int radius_client_send(struct radius_client_data *radius,

Modified: wpasupplicant/branches/upstream/current/src/rsn_supp/Makefile
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/rsn_supp/Makefile?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/rsn_supp/Makefile (original)
+++ wpasupplicant/branches/upstream/current/src/rsn_supp/Makefile Sun Feb 15 19:38:55 2009
@@ -4,3 +4,6 @@
 clean:
 	for d in $(SUBDIRS); do make -C $$d clean; done
 	rm -f *~ *.o *.d
+
+install:
+	@echo Nothing to be made.

Modified: wpasupplicant/branches/upstream/current/src/tls/Makefile
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/tls/Makefile?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/tls/Makefile (original)
+++ wpasupplicant/branches/upstream/current/src/tls/Makefile Sun Feb 15 19:38:55 2009
@@ -4,3 +4,6 @@
 clean:
 	for d in $(SUBDIRS); do make -C $$d clean; done
 	rm -f *~ *.o *.d
+
+install:
+	@echo Nothing to be made.

Modified: wpasupplicant/branches/upstream/current/src/utils/Makefile
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/utils/Makefile?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/utils/Makefile (original)
+++ wpasupplicant/branches/upstream/current/src/utils/Makefile Sun Feb 15 19:38:55 2009
@@ -4,3 +4,6 @@
 clean:
 	for d in $(SUBDIRS); do make -C $$d clean; done
 	rm -f *~ *.o *.d
+
+install:
+	@echo Nothing to be made.

Modified: wpasupplicant/branches/upstream/current/src/utils/os_unix.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/utils/os_unix.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/utils/os_unix.c (original)
+++ wpasupplicant/branches/upstream/current/src/utils/os_unix.c Sun Feb 15 19:38:55 2009
@@ -76,12 +76,47 @@
 }
 
 
+#ifdef __APPLE__
+#include <fcntl.h>
+static int os_daemon(int nochdir, int noclose)
+{
+	int devnull;
+
+	if (chdir("/") < 0)
+		return -1;
+
+	devnull = open("/dev/null", O_RDWR);
+	if (devnull < 0)
+		return -1;
+
+	if (dup2(devnull, STDIN_FILENO) < 0) {
+		close(devnull);
+		return -1;
+	}
+
+	if (dup2(devnull, STDOUT_FILENO) < 0) {
+		close(devnull);
+		return -1;
+	}
+
+	if (dup2(devnull, STDERR_FILENO) < 0) {
+		close(devnull);
+		return -1;
+	}
+
+	return 0;
+}
+#else /* __APPLE__ */
+#define os_daemon daemon
+#endif /* __APPLE__ */
+
+
 int os_daemonize(const char *pid_file)
 {
 #ifdef __uClinux__
 	return -1;
 #else /* __uClinux__ */
-	if (daemon(0, 0)) {
+	if (os_daemon(0, 0)) {
 		perror("daemon");
 		return -1;
 	}

Modified: wpasupplicant/branches/upstream/current/src/utils/wpabuf.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/utils/wpabuf.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/utils/wpabuf.c (original)
+++ wpasupplicant/branches/upstream/current/src/utils/wpabuf.c Sun Feb 15 19:38:55 2009
@@ -1,6 +1,6 @@
 /*
  * Dynamic data buffer
- * Copyright (c) 2007-2008, Jouni Malinen <j at w1.fi>
+ * Copyright (c) 2007-2009, Jouni Malinen <j at w1.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
@@ -160,3 +160,53 @@
 
 	return n;
 }
+
+
+/**
+ * wpabuf_zeropad - Pad buffer with 0x00 octets (prefix) to specified length
+ * @buf: Buffer to be padded
+ * @len: Length for the padded buffer
+ * Returns: wpabuf padded to len octets or %NULL on failure
+ *
+ * If buf is longer than len octets or of same size, it will be returned as-is.
+ * Otherwise a new buffer is allocated and prefixed with 0x00 octets followed
+ * by the source data. The source buffer will be freed on error, i.e., caller
+ * will only be responsible on freeing the returned buffer. If buf is %NULL,
+ * %NULL will be returned.
+ */
+struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len)
+{
+	struct wpabuf *ret;
+	size_t blen;
+
+	if (buf == NULL)
+		return NULL;
+
+	blen = wpabuf_len(buf);
+	if (blen >= len)
+		return buf;
+
+	ret = wpabuf_alloc(len);
+	if (ret) {
+		os_memset(wpabuf_put(ret, len - blen), 0, len - blen);
+		wpabuf_put_buf(ret, buf);
+	}
+	wpabuf_free(buf);
+
+	return ret;
+}
+
+
+void wpabuf_printf(struct wpabuf *buf, char *fmt, ...)
+{
+	va_list ap;
+	void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
+	int res;
+
+	va_start(ap, fmt);
+	res = vsnprintf(tmp, buf->size - buf->used, fmt, ap);
+	va_end(ap);
+	if (res < 0 || (size_t) res >= buf->size - buf->used)
+		wpabuf_overflow(buf, res);
+	buf->used += res;
+}

Modified: wpasupplicant/branches/upstream/current/src/utils/wpabuf.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/utils/wpabuf.h?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/utils/wpabuf.h (original)
+++ wpasupplicant/branches/upstream/current/src/utils/wpabuf.h Sun Feb 15 19:38:55 2009
@@ -1,6 +1,6 @@
 /*
  * Dynamic data buffer
- * Copyright (c) 2007, Jouni Malinen <j at w1.fi>
+ * Copyright (c) 2007-2009, Jouni Malinen <j at w1.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
@@ -37,6 +37,8 @@
 void wpabuf_free(struct wpabuf *buf);
 void * wpabuf_put(struct wpabuf *buf, size_t len);
 struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b);
+struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len);
+void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3);
 
 
 /**
@@ -146,4 +148,9 @@
 	buf->size = buf->used = len;
 }
 
+static inline void wpabuf_put_str(struct wpabuf *dst, const char *str)
+{
+	wpabuf_put_data(dst, str, os_strlen(str));
+}
+
 #endif /* WPABUF_H */

Modified: wpasupplicant/branches/upstream/current/src/wps/Makefile
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/Makefile?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/Makefile (original)
+++ wpasupplicant/branches/upstream/current/src/wps/Makefile Sun Feb 15 19:38:55 2009
@@ -4,3 +4,6 @@
 clean:
 	for d in $(SUBDIRS); do make -C $$d clean; done
 	rm -f *~ *.o *.d
+
+install:
+	@echo Nothing to be made.

Added: wpasupplicant/branches/upstream/current/src/wps/httpread.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/httpread.c?rev=1324&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/httpread.c (added)
+++ wpasupplicant/branches/upstream/current/src/wps/httpread.c Sun Feb 15 19:38:55 2009
@@ -1,0 +1,858 @@
+/**
+ * httpread - Manage reading file(s) from HTTP/TCP socket
+ * Author: Ted Merrill
+ * Copyright 2008 Atheros Communications
+ *
+ * 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.
+ *
+ * The files are buffered via internal callbacks from eloop, then presented to
+ * an application callback routine when completely read into memory. May also
+ * be used if no file is expected but just to get the header, including HTTP
+ * replies (e.g. HTTP/1.1 200 OK etc.).
+ *
+ * This does not attempt to be an optimally efficient implementation, but does
+ * attempt to be of reasonably small size and memory consumption; assuming that
+ * only small files are to be read. A maximum file size is provided by
+ * application and enforced.
+ *
+ * It is assumed that the application does not expect any of the following:
+ * -- transfer encoding other than chunked
+ * -- trailer fields
+ * It is assumed that, even if the other side requested that the connection be
+ * kept open, that we will close it (thus HTTP messages sent by application
+ * should have the connection closed field); this is allowed by HTTP/1.1 and
+ * simplifies things for us.
+ *
+ * Other limitations:
+ * -- HTTP header may not exceed a hard-coded size.
+ *
+ * Notes:
+ * This code would be massively simpler without some of the new features of
+ * HTTP/1.1, especially chunked data.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "httpread.h"
+
+
+/* Tunable parameters */
+#define HTTPREAD_READBUF_SIZE 1024      /* read in chunks of this size */
+#define HTTPREAD_HEADER_MAX_SIZE 4096   /* max allowed for headers */
+#define HTTPREAD_BODYBUF_DELTA 4096     /* increase allocation by this */
+
+#if 0
+/* httpread_debug -- set this global variable > 0 e.g. from debugger
+ * to enable debugs (larger numbers for more debugs)
+ * Make this a #define of 0 to eliminate the debugging code.
+ */
+int httpread_debug = 99;
+#else
+#define httpread_debug 0        /* eliminates even the debugging code */
+#endif
+
+
+/* control instance -- actual definition (opaque to application)
+ */
+struct httpread {
+	/* information from creation */
+	int sd;         /* descriptor of TCP socket to read from */
+	void (*cb)(struct httpread *handle, void *cookie,
+		    enum httpread_event e);  /* call on event */
+	void *cookie;   /* pass to callback */
+	int max_bytes;          /* maximum file size else abort it */
+	int timeout_seconds;            /* 0 or total duration timeout period */
+
+	/* dynamically used information follows */
+	int sd_registered;      /* nonzero if we need to unregister socket */
+	int to_registered;      /* nonzero if we need to unregister timeout */
+
+	int got_hdr;            /* nonzero when header is finalized */
+	char hdr[HTTPREAD_HEADER_MAX_SIZE+1];   /* headers stored here */
+	int hdr_nbytes;
+
+	enum httpread_hdr_type hdr_type;
+	int version;            /* 1 if we've seen 1.1 */
+	int reply_code;         /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */
+	int got_content_length; /* true if we know content length for sure */
+	int content_length;     /* body length,  iff got_content_length */
+	int chunked;            /* nonzero for chunked data */
+	char *uri;
+
+	int got_body;           /* nonzero when body is finalized */
+	char *body;
+	int body_nbytes;
+	int body_alloc_nbytes;  /* amount allocated */
+
+	int got_file;           /* here when we are done */
+
+	/* The following apply if data is chunked: */
+	int in_chunk_data;      /* 0=in/at header, 1=in the data or tail*/
+	int chunk_start;        /* offset in body of chunk hdr or data */
+	int chunk_size;         /* data of chunk (not hdr or ending CRLF)*/
+	int in_trailer;         /* in header fields after data (chunked only)*/
+	enum trailer_state {
+		trailer_line_begin = 0,
+		trailer_empty_cr,       /* empty line + CR */
+		trailer_nonempty,
+		trailer_nonempty_cr,
+	} trailer_state;
+};
+
+
+/* Check words for equality, where words consist of graphical characters
+ * delimited by whitespace
+ * Returns nonzero if "equal" doing case insensitive comparison.
+ */
+static int word_eq(char *s1, char *s2)
+{
+	int c1;
+	int c2;
+	int end1 = 0;
+	int end2 = 0;
+	for (;;) {
+		c1 = *s1++;
+		c2 = *s2++;
+		if (isalpha(c1) && isupper(c1))
+			c1 = tolower(c1);
+		if (isalpha(c2) && isupper(c2))
+			c2 = tolower(c2);
+		end1 = !isgraph(c1);
+		end2 = !isgraph(c2);
+		if (end1 || end2 || c1 != c2)
+			break;
+	}
+	return end1 && end2;  /* reached end of both words? */
+}
+
+
+/* convert hex to binary
+ * Requires that c have been previously tested true with isxdigit().
+ */
+static int hex_value(int c)
+{
+	if (isdigit(c))
+		return c - '0';
+	if (islower(c))
+		return 10 + c - 'a';
+	return 10 + c - 'A';
+}
+
+
+static void httpread_timeout_handler(void *eloop_data, void *user_ctx);
+
+/* httpread_destroy -- if h is non-NULL, clean up
+ * This must eventually be called by the application following
+ * call of the application's callback and may be called
+ * earlier if desired.
+ */
+void httpread_destroy(struct httpread *h)
+{
+	if (httpread_debug >= 10)
+		wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h);
+	if (!h)
+		return;
+
+	if (h->to_registered)
+		eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
+	h->to_registered = 0;
+	if (h->sd_registered)
+		eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
+	h->sd_registered = 0;
+	os_free(h->body);
+	os_free(h->uri);
+	os_memset(h, 0, sizeof(*h));  /* aid debugging */
+	h->sd = -1;     /* aid debugging */
+	os_free(h);
+}
+
+
+/* httpread_timeout_handler -- called on excessive total duration
+ */
+static void httpread_timeout_handler(void *eloop_data, void *user_ctx)
+{
+	struct httpread *h = user_ctx;
+	wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
+	h->to_registered = 0;   /* is self-cancelling */
+	(*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
+}
+
+
+/* Analyze options only so far as is needed to correctly obtain the file.
+ * The application can look at the raw header to find other options.
+ */
+static int httpread_hdr_option_analyze(
+	struct httpread *h,
+	char *hbp       /* pointer to current line in header buffer */
+	)
+{
+	if (word_eq(hbp, "CONTENT-LENGTH:")) {
+		while (isgraph(*hbp))
+			hbp++;
+		while (*hbp == ' ' || *hbp == '\t')
+			hbp++;
+		if (!isdigit(*hbp))
+			return -1;
+		h->content_length = atol(hbp);
+		h->got_content_length = 1;
+		return 0;
+	}
+	if (word_eq(hbp, "TRANSFER_ENCODING:")) {
+		while (isgraph(*hbp))
+			hbp++;
+		while (*hbp == ' ' || *hbp == '\t')
+			hbp++;
+		/* There should (?) be no encodings of interest
+		 * other than chunked...
+		 */
+		if (os_strncmp(hbp, "CHUNKED", 7)) {
+			h->chunked = 1;
+			h->in_chunk_data = 0;
+			/* ignore possible ;<parameters> */
+		}
+		return 0;
+	}
+	/* skip anything we don't know, which is a lot */
+	return 0;
+}
+
+
+static int httpread_hdr_analyze(struct httpread *h)
+{
+	char *hbp = h->hdr;      /* pointer into h->hdr */
+	int standard_first_line = 1;
+
+	/* First line is special */
+	h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
+	if (!isgraph(*hbp))
+		goto bad;
+	if (os_strncmp(hbp, "HTTP/", 5) == 0) {
+		h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
+		standard_first_line = 0;
+		hbp += 5;
+		if (hbp[0] == '1' && hbp[1] == '.' &&
+		    isdigit(hbp[2]) && hbp[2] != '0')
+			h->version = 1;
+		while (isgraph(*hbp))
+			hbp++;
+		while (*hbp == ' ' || *hbp == '\t')
+			hbp++;
+		if (!isdigit(*hbp))
+			goto bad;
+		h->reply_code = atol(hbp);
+	} else if (word_eq(hbp, "GET"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_GET;
+	else if (word_eq(hbp, "HEAD"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
+	else if (word_eq(hbp, "POST"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_POST;
+	else if (word_eq(hbp, "PUT"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
+	else if (word_eq(hbp, "DELETE"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
+	else if (word_eq(hbp, "TRACE"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
+	else if (word_eq(hbp, "CONNECT"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
+	else if (word_eq(hbp, "NOTIFY"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
+	else if (word_eq(hbp, "M-SEARCH"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
+	else if (word_eq(hbp, "M-POST"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
+	else if (word_eq(hbp, "SUBSCRIBE"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
+	else if (word_eq(hbp, "UNSUBSCRIBE"))
+		h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
+	else {
+	}
+
+	if (standard_first_line) {
+		char *rawuri;
+		char *uri;
+		/* skip type */
+		while (isgraph(*hbp))
+			hbp++;
+		while (*hbp == ' ' || *hbp == '\t')
+			hbp++;
+		/* parse uri.
+		 * Find length, allocate memory for translated
+		 * copy, then translate by changing %<hex><hex>
+		 * into represented value.
+		 */
+		rawuri = hbp;
+		while (isgraph(*hbp))
+			hbp++;
+		h->uri = os_malloc((hbp - rawuri) + 1);
+		if (h->uri == NULL)
+			goto bad;
+		uri = h->uri;
+		while (rawuri < hbp) {
+			int c = *rawuri;
+			if (c == '%' &&
+			    isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
+				*uri++ = (hex_value(rawuri[1]) << 4) |
+					hex_value(rawuri[2]);
+				rawuri += 3;
+			} else {
+				*uri++ = c;
+				rawuri++;
+			}
+		}
+		*uri = 0;       /* null terminate */
+		while (isgraph(*hbp))
+			hbp++;
+		while (*hbp == ' ' || *hbp == '\t')
+			hbp++;
+		/* get version */
+		if (0 == strncmp(hbp, "HTTP/", 5)) {
+			hbp += 5;
+			if (hbp[0] == '1' && hbp[1] == '.' &&
+			    isdigit(hbp[2]) && hbp[2] != '0')
+				h->version = 1;
+		}
+	}
+	/* skip rest of line */
+	while (*hbp)
+		if (*hbp++ == '\n')
+			break;
+
+	/* Remainder of lines are options, in any order;
+	 * or empty line to terminate
+	 */
+	for (;;) {
+		/* Empty line to terminate */
+		if (hbp[0] == '\n' ||
+		    (hbp[0] == '\r' && hbp[1] == '\n'))
+			break;
+		if (!isgraph(*hbp))
+			goto bad;
+		if (httpread_hdr_option_analyze(h, hbp))
+			goto bad;
+		/* skip line */
+		while (*hbp)
+			if (*hbp++ == '\n')
+				break;
+	}
+
+	/* chunked overrides content-length always */
+	if (h->chunked)
+		h->got_content_length = 0;
+
+	/* For some types, we should not try to read a body
+	 * This is in addition to the application determining
+	 * that we should not read a body.
+	 */
+	switch (h->hdr_type) {
+	case HTTPREAD_HDR_TYPE_REPLY:
+		/* Some codes can have a body and some not.
+		 * For now, just assume that any other than 200
+		 * do not...
+		 */
+		if (h->reply_code != 200)
+			h->max_bytes = 0;
+		break;
+	case HTTPREAD_HDR_TYPE_GET:
+	case HTTPREAD_HDR_TYPE_HEAD:
+		/* in practice it appears that it is assumed
+		 * that GETs have a body length of 0... ?
+		 */
+		if (h->chunked == 0 && h->got_content_length == 0)
+			h->max_bytes = 0;
+		break;
+	case HTTPREAD_HDR_TYPE_POST:
+	case HTTPREAD_HDR_TYPE_PUT:
+	case HTTPREAD_HDR_TYPE_DELETE:
+	case HTTPREAD_HDR_TYPE_TRACE:
+	case HTTPREAD_HDR_TYPE_CONNECT:
+	case HTTPREAD_HDR_TYPE_NOTIFY:
+	case HTTPREAD_HDR_TYPE_M_SEARCH:
+	case HTTPREAD_HDR_TYPE_M_POST:
+	case HTTPREAD_HDR_TYPE_SUBSCRIBE:
+	case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
+	default:
+		break;
+	}
+
+	return 0;
+
+bad:
+	/* Error */
+	return -1;
+}
+
+
+/* httpread_read_handler -- called when socket ready to read
+ *
+ * Note: any extra data we read past end of transmitted file is ignored;
+ * if we were to support keeping connections open for multiple files then
+ * this would have to be addressed.
+ */
+static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
+{
+	struct httpread *h = sock_ctx;
+	int nread;
+	char *rbp;      /* pointer into read buffer */
+	char *hbp;      /* pointer into header buffer */
+	char *bbp;      /* pointer into body buffer */
+	char readbuf[HTTPREAD_READBUF_SIZE];  /* temp use to read into */
+
+	if (httpread_debug >= 20)
+		wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h);
+
+	/* read some at a time, then search for the interal
+	 * boundaries between header and data and etc.
+	 */
+	nread = read(h->sd, readbuf, sizeof(readbuf));
+	if (nread < 0)
+		goto bad;
+	if (nread == 0) {
+		/* end of transmission... this may be normal
+		 * or may be an error... in some cases we can't
+		 * tell which so we must assume it is normal then.
+		 */
+		if (!h->got_hdr) {
+			/* Must at least have completed header */
+			wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
+			goto bad;
+		}
+		if (h->chunked || h->got_content_length) {
+			/* Premature EOF; e.g. dropped connection */
+			wpa_printf(MSG_DEBUG,
+				   "httpread premature eof(%p) %d/%d",
+				   h, h->body_nbytes,
+				   h->content_length);
+			goto bad;
+		}
+		/* No explicit length, hopefully we have all the data
+		 * although dropped connections can cause false
+		 * end
+		 */
+		if (httpread_debug >= 10)
+			wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
+			h->got_body = 1;
+			goto got_file;
+	}
+	rbp = readbuf;
+
+	/* Header consists of text lines (terminated by both CR and LF)
+	 * and an empty line (CR LF only).
+	 */
+	if (!h->got_hdr) {
+		hbp = h->hdr + h->hdr_nbytes;
+		/* add to headers until:
+		 *      -- we run out of data in read buffer
+		 *      -- or, we run out of header buffer room
+		 *      -- or, we get double CRLF in headers
+		 */
+		for (;;) {
+			if (nread == 0)
+				goto get_more;
+			if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
+				goto bad;
+			}
+			*hbp++ = *rbp++;
+			nread--;
+			h->hdr_nbytes++;
+			if (h->hdr_nbytes >= 4 &&
+			    hbp[-1] == '\n' &&
+			    hbp[-2] == '\r' &&
+			    hbp[-3] == '\n' &&
+			    hbp[-4] == '\r' ) {
+				h->got_hdr = 1;
+				*hbp = 0;       /* null terminate */
+				break;
+			}
+		}
+		/* here we've just finished reading the header */
+		if (httpread_hdr_analyze(h)) {
+			wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
+			goto bad;
+		}
+		if (h->max_bytes == 0) {
+			if (httpread_debug >= 10)
+				wpa_printf(MSG_DEBUG,
+					   "httpread no body hdr end(%p)", h);
+			goto got_file;
+		}
+		if (h->got_content_length && h->content_length == 0) {
+			if (httpread_debug >= 10)
+				wpa_printf(MSG_DEBUG,
+					   "httpread zero content length(%p)",
+					   h);
+			goto got_file;
+		}
+	}
+
+	/* Certain types of requests never have data and so
+	 * must be specially recognized.
+	 */
+	if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
+	    !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
+	    !os_strncasecmp(h->hdr, "HEAD", 4) ||
+	    !os_strncasecmp(h->hdr, "GET", 3)) {
+		if (!h->got_body) {
+			if (httpread_debug >= 10)
+				wpa_printf(MSG_DEBUG,
+					   "httpread NO BODY for sp. type");
+		}
+		h->got_body = 1;
+		goto got_file;
+	}
+
+	/* Data can be just plain binary data, or if "chunked"
+	 * consists of chunks each with a header, ending with
+	 * an ending header.
+	 */
+	if (!h->got_body) {
+		/* Here to get (more of) body */
+		/* ensure we have enough room for worst case for body
+		 * plus a null termination character
+		 */
+		if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
+			char *new_body;
+			int new_alloc_nbytes;
+
+			if (h->body_nbytes >= h->max_bytes)
+				goto bad;
+			new_alloc_nbytes = h->body_alloc_nbytes +
+				HTTPREAD_BODYBUF_DELTA;
+			/* For content-length case, the first time
+			 * through we allocate the whole amount
+			 * we need.
+			 */
+			if (h->got_content_length &&
+			    new_alloc_nbytes < (h->content_length + 1))
+				new_alloc_nbytes = h->content_length + 1;
+			if ((new_body = os_realloc(h->body, new_alloc_nbytes))
+			    == NULL)
+				goto bad;
+
+			h->body = new_body;
+			h->body_alloc_nbytes = new_alloc_nbytes;
+		}
+		/* add bytes */
+		bbp = h->body + h->body_nbytes;
+		for (;;) {
+			int ncopy;
+			/* See if we need to stop */
+			if (h->chunked && h->in_chunk_data == 0) {
+				/* in chunk header */
+				char *cbp = h->body + h->chunk_start;
+				if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
+				    bbp[-1] == '\n') {
+					/* end of chunk hdr line */
+					/* hdr line consists solely
+					 * of a hex numeral and CFLF
+					 */
+					if (!isxdigit(*cbp))
+						goto bad;
+					h->chunk_size = strtoul(cbp, NULL, 16);
+					/* throw away chunk header
+					 * so we have only real data
+					 */
+					h->body_nbytes = h->chunk_start;
+					bbp = cbp;
+					if (h->chunk_size == 0) {
+						/* end of chunking */
+						/* trailer follows */
+						h->in_trailer = 1;
+						if (httpread_debug >= 20)
+							wpa_printf(
+								MSG_DEBUG,
+								"httpread end chunks(%p)", h);
+						break;
+					}
+					h->in_chunk_data = 1;
+					/* leave chunk_start alone */
+				}
+			} else if (h->chunked) {
+				/* in chunk data */
+				if ((h->body_nbytes - h->chunk_start) ==
+				    (h->chunk_size + 2)) {
+					/* end of chunk reached,
+					 * new chunk starts
+					 */
+					/* check chunk ended w/ CRLF
+					 * which we'll throw away
+					 */
+					if (bbp[-1] == '\n' &&
+					    bbp[-2] == '\r') {
+					} else
+						goto bad;
+					h->body_nbytes -= 2;
+					bbp -= 2;
+					h->chunk_start = h->body_nbytes;
+					h->in_chunk_data = 0;
+					h->chunk_size = 0; /* just in case */
+				}
+			} else if (h->got_content_length &&
+				   h->body_nbytes >= h->content_length) {
+				h->got_body = 1;
+				if (httpread_debug >= 10)
+					wpa_printf(
+						MSG_DEBUG,
+						"httpread got content(%p)", h);
+				goto got_file;
+			}
+			if (nread <= 0)
+				break;
+			/* Now transfer. Optimize using memcpy where we can. */
+			if (h->chunked && h->in_chunk_data) {
+				/* copy up to remainder of chunk data
+				 * plus the required CR+LF at end
+				 */
+				ncopy = (h->chunk_start + h->chunk_size + 2) -
+					h->body_nbytes;
+			} else if (h->chunked) {
+				/*in chunk header -- don't optimize */
+				*bbp++ = *rbp++;
+				nread--;
+				h->body_nbytes++;
+				continue;
+			} else if (h->got_content_length) {
+				ncopy = h->content_length - h->body_nbytes;
+			} else {
+				ncopy = nread;
+			}
+			/* Note: should never be 0 */
+			if (ncopy > nread)
+				ncopy = nread;
+			os_memcpy(bbp, rbp, ncopy);
+			bbp += ncopy;
+			h->body_nbytes += ncopy;
+			rbp += ncopy;
+			nread -= ncopy;
+		}       /* body copy loop */
+	}       /* !got_body */
+	if (h->chunked && h->in_trailer) {
+		/* If "chunked" then there is always a trailer,
+		 * consisting of zero or more non-empty lines
+		 * ending with CR LF and then an empty line w/ CR LF.
+		 * We do NOT support trailers except to skip them --
+		 * this is supported (generally) by the http spec.
+		 */
+		bbp = h->body + h->body_nbytes;
+		for (;;) {
+			int c;
+			if (nread <= 0)
+				break;
+			c = *rbp++;
+			nread--;
+			switch (h->trailer_state) {
+			case trailer_line_begin:
+				if (c == '\r')
+					h->trailer_state = trailer_empty_cr;
+				else
+					h->trailer_state = trailer_nonempty;
+				break;
+			case trailer_empty_cr:
+				/* end empty line */
+				if (c == '\n') {
+					h->trailer_state = trailer_line_begin;
+					h->in_trailer = 0;
+					if (httpread_debug >= 10)
+						wpa_printf(
+							MSG_DEBUG,
+							"httpread got content(%p)", h);
+					h->got_body = 1;
+					goto got_file;
+				}
+				h->trailer_state = trailer_nonempty;
+				break;
+			case trailer_nonempty:
+				if (c == '\r')
+					h->trailer_state = trailer_nonempty_cr;
+				break;
+			case trailer_nonempty_cr:
+				if (c == '\n')
+					h->trailer_state = trailer_line_begin;
+				else
+					h->trailer_state = trailer_nonempty;
+				break;
+			}
+		}
+	}
+	goto get_more;
+
+bad:
+	/* Error */
+	wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
+	(*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
+	return;
+
+get_more:
+	return;
+
+got_file:
+	if (httpread_debug >= 10)
+		wpa_printf(MSG_DEBUG,
+			   "httpread got file %d bytes type %d",
+			   h->body_nbytes, h->hdr_type);
+	/* Null terminate for convenience of some applications */
+	if (h->body)
+		h->body[h->body_nbytes] = 0; /* null terminate */
+	h->got_file = 1;
+	/* Assume that we do NOT support keeping connection alive,
+	 * and just in case somehow we don't get destroyed right away,
+	 * unregister now.
+	 */
+	if (h->sd_registered)
+		eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
+	h->sd_registered = 0;
+	/* The application can destroy us whenever they feel like...
+	 * cancel timeout.
+	 */
+	if (h->to_registered)
+		eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
+	h->to_registered = 0;
+	(*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
+}
+
+
+/* httpread_create -- start a new reading session making use of eloop.
+ * The new instance will use the socket descriptor for reading (until
+ * it gets a file and not after) but will not close the socket, even
+ * when the instance is destroyed (the application must do that).
+ * Return NULL on error.
+ *
+ * Provided that httpread_create successfully returns a handle,
+ * the callback fnc is called to handle httpread_event events.
+ * The caller should do destroy on any errors or unknown events.
+ *
+ * Pass max_bytes == 0 to not read body at all (required for e.g.
+ * reply to HEAD request).
+ */
+struct httpread * httpread_create(
+	int sd,	 /* descriptor of TCP socket to read from */
+	void (*cb)(struct httpread *handle, void *cookie,
+		   enum httpread_event e),  /* call on event */
+	void *cookie,    /* pass to callback */
+	int max_bytes,	  /* maximum body size else abort it */
+	int timeout_seconds     /* 0; or total duration timeout period */
+	)
+{
+	struct httpread *h = NULL;
+
+	h = os_zalloc(sizeof(*h));
+	if (h == NULL)
+		goto fail;
+	h->sd = sd;
+	h->cb = cb;
+	h->cookie = cookie;
+	h->max_bytes = max_bytes;
+	h->timeout_seconds = timeout_seconds;
+
+	if (timeout_seconds > 0) {
+		if (eloop_register_timeout(timeout_seconds, 0,
+					   httpread_timeout_handler,
+					   NULL, h)) {
+			/* No way to recover (from malloc failure) */
+			goto fail;
+		}
+		h->to_registered = 1;
+	}
+	if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
+				NULL, h)) {
+		/* No way to recover (from malloc failure) */
+		goto fail;
+	}
+	h->sd_registered = 1;
+	return h;
+
+fail:
+
+	/* Error */
+	httpread_destroy(h);
+	return NULL;
+}
+
+
+/* httpread_hdr_type_get -- When file is ready, returns header type. */
+enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
+{
+	return h->hdr_type;
+}
+
+
+/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
+ * or possibly NULL (which would be an error).
+ */
+char * httpread_uri_get(struct httpread *h)
+{
+	return h->uri;
+}
+
+
+/* httpread_reply_code_get -- When reply is ready, returns reply code */
+int httpread_reply_code_get(struct httpread *h)
+{
+	return h->reply_code;
+}
+
+
+/* httpread_length_get -- When file is ready, returns file length. */
+int httpread_length_get(struct httpread *h)
+{
+	return h->body_nbytes;
+}
+
+
+/* httpread_data_get -- When file is ready, returns file content
+ * with null byte appened.
+ * Might return NULL in some error condition.
+ */
+void * httpread_data_get(struct httpread *h)
+{
+	return h->body ? h->body : "";
+}
+
+
+/* httpread_hdr_get -- When file is ready, returns header content
+ * with null byte appended.
+ * Might return NULL in some error condition.
+ */
+char * httpread_hdr_get(struct httpread *h)
+{
+	return h->hdr;
+}
+
+
+/* httpread_hdr_line_get -- When file is ready, returns pointer
+ * to line within header content matching the given tag
+ * (after the tag itself and any spaces/tabs).
+ *
+ * The tag should end with a colon for reliable matching.
+ *
+ * If not found, returns NULL;
+ */
+char * httpread_hdr_line_get(struct httpread *h, const char *tag)
+{
+	int tag_len = os_strlen(tag);
+	char *hdr = h->hdr;
+	hdr = os_strchr(hdr, '\n');
+	if (hdr == NULL)
+		return NULL;
+	hdr++;
+	for (;;) {
+		if (!os_strncasecmp(hdr, tag, tag_len)) {
+			hdr += tag_len;
+			while (*hdr == ' ' || *hdr == '\t')
+				hdr++;
+			return hdr;
+		}
+		hdr = os_strchr(hdr, '\n');
+		if (hdr == NULL)
+			return NULL;
+		hdr++;
+	}
+}

Added: wpasupplicant/branches/upstream/current/src/wps/httpread.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/httpread.h?rev=1324&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/httpread.h (added)
+++ wpasupplicant/branches/upstream/current/src/wps/httpread.h Sun Feb 15 19:38:55 2009
@@ -1,0 +1,123 @@
+/**
+ * httpread - Manage reading file(s) from HTTP/TCP socket
+ * Author: Ted Merrill
+ * Copyright 2008 Atheros Communications
+ *
+ * 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 HTTPREAD_H
+#define HTTPREAD_H
+
+/* event types (passed to callback) */
+enum httpread_event {
+	HTTPREAD_EVENT_FILE_READY = 1,        /* including reply ready */
+	HTTPREAD_EVENT_TIMEOUT = 2,
+	HTTPREAD_EVENT_ERROR = 3      /* misc. error, esp malloc error */
+};
+
+
+/* header type detected
+ * available to callback via call to httpread_reply_code_get()
+ */
+enum httpread_hdr_type {
+	HTTPREAD_HDR_TYPE_UNKNOWN = 0,      /* none of the following */
+	HTTPREAD_HDR_TYPE_REPLY = 1,        /* hdr begins w/ HTTP/ */
+	HTTPREAD_HDR_TYPE_GET = 2,          /* hdr begins with GET<sp> */
+	HTTPREAD_HDR_TYPE_HEAD = 3,         /* hdr begins with HEAD<sp> */
+	HTTPREAD_HDR_TYPE_POST = 4,         /* hdr begins with POST<sp> */
+	HTTPREAD_HDR_TYPE_PUT = 5,          /* hdr begins with ... */
+	HTTPREAD_HDR_TYPE_DELETE = 6,       /* hdr begins with ... */
+	HTTPREAD_HDR_TYPE_TRACE = 7,        /* hdr begins with ... */
+	HTTPREAD_HDR_TYPE_CONNECT = 8,      /* hdr begins with ... */
+	HTTPREAD_HDR_TYPE_NOTIFY = 9,       /* hdr begins with ... */
+	HTTPREAD_HDR_TYPE_M_SEARCH = 10,    /* hdr begins with ... */
+	HTTPREAD_HDR_TYPE_M_POST = 11,      /* hdr begins with ... */
+	HTTPREAD_HDR_TYPE_SUBSCRIBE = 12,   /* hdr begins with ... */
+	HTTPREAD_HDR_TYPE_UNSUBSCRIBE = 13, /* hdr begins with ... */
+
+	HTTPREAD_N_HDR_TYPES    /* keep last */
+};
+
+
+/* control instance -- opaque struct declaration
+ */
+struct httpread;
+
+
+/* httpread_destroy -- if h is non-NULL, clean up
+ * This must eventually be called by the application following
+ * call of the application's callback and may be called
+ * earlier if desired.
+ */
+void httpread_destroy(struct httpread *h);
+
+/* httpread_create -- start a new reading session making use of eloop.
+ * The new instance will use the socket descriptor for reading (until
+ * it gets a file and not after) but will not close the socket, even
+ * when the instance is destroyed (the application must do that).
+ * Return NULL on error.
+ *
+ * Provided that httpread_create successfully returns a handle,
+ * the callback fnc is called to handle httpread_event events.
+ * The caller should do destroy on any errors or unknown events.
+ *
+ * Pass max_bytes == 0 to not read body at all (required for e.g.
+ * reply to HEAD request).
+ */
+struct httpread * httpread_create(
+	int sd,         /* descriptor of TCP socket to read from */
+	void (*cb)(struct httpread *handle, void *cookie,
+		    enum httpread_event e),  /* call on event */
+	void *cookie,    /* pass to callback */
+	int max_bytes,          /* maximum file size else abort it */
+	int timeout_seconds     /* 0; or total duration timeout period */
+	);
+
+/* httpread_hdr_type_get -- When file is ready, returns header type.
+ */
+enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h);
+
+
+/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
+ * or possibly NULL (which would be an error).
+ */
+char *httpread_uri_get(struct httpread *h);
+
+/* httpread_reply_code_get -- When reply is ready, returns reply code */
+int httpread_reply_code_get(struct httpread *h);
+
+
+/* httpread_length_get -- When file is ready, returns file length. */
+int httpread_length_get(struct httpread *h);
+
+/* httpread_data_get -- When file is ready, returns file content
+ * with null byte appened.
+ * Might return NULL in some error condition.
+ */
+void * httpread_data_get(struct httpread *h);
+
+/* httpread_hdr_get -- When file is ready, returns header content
+ * with null byte appended.
+ * Might return NULL in some error condition.
+ */
+char * httpread_hdr_get(struct httpread *h);
+
+/* httpread_hdr_line_get -- When file is ready, returns pointer
+ * to line within header content matching the given tag
+ * (after the tag itself and any spaces/tabs).
+ *
+ * The tag should end with a colon for reliable matching.
+ *
+ * If not found, returns NULL;
+ */
+char * httpread_hdr_line_get(struct httpread *h, const char *tag);
+
+#endif /* HTTPREAD_H */

Modified: wpasupplicant/branches/upstream/current/src/wps/wps.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps.c (original)
+++ wpasupplicant/branches/upstream/current/src/wps/wps.c Sun Feb 15 19:38:55 2009
@@ -186,7 +186,7 @@
 
 
 /**
- * wps_is_selected_pbc_registrar - Check whether WPS IE indicates active PIN
+ * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN
  * @msg: WPS IE contents from Beacon or Probe Response frame
  * Returns: 1 if PIN Registrar is active, 0 if not
  */
@@ -202,9 +202,13 @@
 	 * Device Password ID here.
 	 */
 
-	if (wps_parse_msg(msg, &attr) < 0 ||
-	    !attr.selected_registrar || *attr.selected_registrar == 0 ||
-	    !attr.dev_password_id ||
+	if (wps_parse_msg(msg, &attr) < 0)
+		return 0;
+
+	if (!attr.selected_registrar || *attr.selected_registrar == 0)
+		return 0;
+
+	if (attr.dev_password_id != NULL &&
 	    WPA_GET_BE16(attr.dev_password_id) == DEV_PW_PUSHBUTTON)
 		return 0;
 
@@ -316,3 +320,16 @@
 
 	return ie;
 }
+
+
+void wps_free_pending_msgs(struct upnp_pending_message *msgs)
+{
+	struct upnp_pending_message *p, *prev;
+	p = msgs;
+	while (p) {
+		prev = p;
+		p = p->next;
+		wpabuf_free(prev->msg);
+		os_free(prev);
+	}
+}

Modified: wpasupplicant/branches/upstream/current/src/wps/wps.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps.h?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps.h (original)
+++ wpasupplicant/branches/upstream/current/src/wps/wps.h Sun Feb 15 19:38:55 2009
@@ -21,6 +21,7 @@
  * enum wsc_op_code - EAP-WSC OP-Code values
  */
 enum wsc_op_code {
+	WSC_UPnP = 0 /* No OP Code in UPnP transport */,
 	WSC_Start = 0x01,
 	WSC_ACK = 0x02,
 	WSC_NACK = 0x03,
@@ -30,6 +31,7 @@
 };
 
 struct wps_registrar;
+struct upnp_wps_device_sm;
 
 /**
  * struct wps_credential - WPS Credential
@@ -41,6 +43,9 @@
  * @key: Key
  * @key_len: Key length in octets
  * @mac_addr: MAC address of the peer
+ * @cred_attr: Unparsed Credential attribute data (used only in cred_cb());
+ *	this may be %NULL, if not used
+ * @cred_attr_len: Length of cred_attr in octets
  */
 struct wps_credential {
 	u8 ssid[32];
@@ -51,6 +56,8 @@
 	u8 key[64];
 	size_t key_len;
 	u8 mac_addr[ETH_ALEN];
+	const u8 *cred_attr;
+	size_t cred_attr_len;
 };
 
 /**
@@ -137,7 +144,13 @@
 	/**
 	 * WPS_FAILURE - Processing failed
 	 */
-	WPS_FAILURE
+	WPS_FAILURE,
+
+	/**
+	 * WPS_PENDING - Processing continues, but waiting for an external
+	 *	event (e.g., UPnP message from an external Registrar)
+	 */
+	WPS_PENDING
 };
 enum wps_process_res wps_process_msg(struct wps_data *wps,
 				     enum wsc_op_code op_code,
@@ -201,9 +214,58 @@
 			      const struct wps_device_data *dev);
 
 	/**
+	 * reg_success_cb - Callback for reporting successful registration
+	 * @ctx: Higher layer context data (cb_ctx)
+	 * @mac_addr: MAC address of the Enrollee
+	 * @uuid_e: UUID-E of the Enrollee
+	 *
+	 * This callback is called whenever an Enrollee completes registration
+	 * successfully.
+	 */
+	void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
+			       const u8 *uuid_e);
+
+	/**
 	 * cb_ctx: Higher layer context data for Registrar callbacks
 	 */
 	void *cb_ctx;
+
+	/**
+	 * skip_cred_build: Do not build credential
+	 *
+	 * This option can be used to disable internal code that builds
+	 * Credential attribute into M8 based on the current network
+	 * configuration and Enrollee capabilities. The extra_cred data will
+	 * then be used as the Credential(s).
+	 */
+	int skip_cred_build;
+
+	/**
+	 * extra_cred: Additional Credential attribute(s)
+	 *
+	 * This optional data (set to %NULL to disable) can be used to add
+	 * Credential attribute(s) for other networks into M8. If
+	 * skip_cred_build is set, this will also override the automatically
+	 * generated Credential attribute.
+	 */
+	const u8 *extra_cred;
+
+	/**
+	 * extra_cred_len: Length of extra_cred in octets
+	 */
+	size_t extra_cred_len;
+
+	/**
+	 * disable_auto_conf - Disable auto-configuration on first registration
+	 *
+	 * By default, the AP that is started in not configured state will
+	 * generate a random PSK and move to configured state when the first
+	 * registration protocol run is completed successfully. This option can
+	 * be used to disable this functionality and leave it up to an external
+	 * program to take care of configuration. This requires the extra_cred
+	 * to be set with a suitable Credential and skip_cred_build being used.
+	 */
+	int disable_auto_conf;
 };
 
 
@@ -224,7 +286,12 @@
 	/**
 	 * WPS_EV_SUCCESS - Registration succeeded
 	 */
-	WPS_EV_SUCCESS
+	WPS_EV_SUCCESS,
+
+	/**
+	 * WPS_EV_PWD_AUTH_FAIL - Password authentication failed
+	 */
+	WPS_EV_PWD_AUTH_FAIL
 };
 
 /**
@@ -258,6 +325,25 @@
 	struct wps_event_fail {
 		int msg;
 	} fail;
+
+	struct wps_event_pwd_auth_fail {
+		int enrollee;
+		int part;
+	} pwd_auth_fail;
+};
+
+/**
+ * struct upnp_pending_message - Pending PutWLANResponse messages
+ * @next: Pointer to next pending message or %NULL
+ * @addr: NewWLANEventMAC
+ * @msg: NewMessage
+ * @type: Message Type
+ */
+struct upnp_pending_message {
+	struct upnp_pending_message *next;
+	u8 addr[ETH_ALEN];
+	struct wpabuf *msg;
+	enum wps_msg_type type;
 };
 
 /**
@@ -343,6 +429,44 @@
 	size_t network_key_len;
 
 	/**
+	 * ap_settings - AP Settings override for M7 (only used at AP)
+	 *
+	 * If %NULL, AP Settings attributes will be generated based on the
+	 * current network configuration.
+	 */
+	u8 *ap_settings;
+
+	/**
+	 * ap_settings_len - Length of ap_settings in octets
+	 */
+	size_t ap_settings_len;
+
+	/**
+	 * friendly_name - Friendly Name (required for UPnP)
+	 */
+	char *friendly_name;
+
+	/**
+	 * manufacturer_url - Manufacturer URL (optional for UPnP)
+	 */
+	char *manufacturer_url;
+
+	/**
+	 * model_description - Model Description (recommended for UPnP)
+	 */
+	char *model_description;
+
+	/**
+	 * model_url - Model URL (optional for UPnP)
+	 */
+	char *model_url;
+
+	/**
+	 * upc - Universal Product Code (optional for UPnP)
+	 */
+	char *upc;
+
+	/**
 	 * cred_cb - Callback to notify that new Credentials were received
 	 * @ctx: Higher layer context data (cb_ctx)
 	 * @cred: The received Credential
@@ -363,6 +487,11 @@
 	 * cb_ctx: Higher layer context data for callbacks
 	 */
 	void *cb_ctx;
+
+	struct upnp_wps_device_sm *wps_upnp;
+
+	/* Pending messages from UPnP PutWLANResponse */
+	struct upnp_pending_message *upnp_msgs;
 };
 
 
@@ -377,9 +506,13 @@
 int wps_registrar_button_pushed(struct wps_registrar *reg);
 void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
 				const struct wpabuf *wps_data);
+int wps_registrar_update_ie(struct wps_registrar *reg);
+int wps_registrar_set_selected_registrar(struct wps_registrar *reg,
+					 const struct wpabuf *msg);
 
 unsigned int wps_pin_checksum(unsigned int pin);
 unsigned int wps_pin_valid(unsigned int pin);
 unsigned int wps_generate_pin(void);
+void wps_free_pending_msgs(struct upnp_pending_message *msgs);
 
 #endif /* WPS_H */

Modified: wpasupplicant/branches/upstream/current/src/wps/wps_attr_build.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_attr_build.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_attr_build.c (original)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_attr_build.c Sun Feb 15 19:38:55 2009
@@ -27,6 +27,7 @@
 
 	wpa_printf(MSG_DEBUG, "WPS:  * Public Key");
 	pubkey = dh_init(dh_groups_get(WPS_DH_GROUP), &wps->dh_privkey);
+	pubkey = wpabuf_zeropad(pubkey, 192);
 	if (pubkey == NULL) {
 		wpa_printf(MSG_DEBUG, "WPS: Failed to initialize "
 			   "Diffie-Hellman handshake");

Modified: wpasupplicant/branches/upstream/current/src/wps/wps_attr_process.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_attr_process.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_attr_process.c (original)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_attr_process.c Sun Feb 15 19:38:55 2009
@@ -254,6 +254,23 @@
 	wpa_printf(MSG_DEBUG, "WPS: 802.1X Enabled: %d", *dot1x_enabled);
 
 	return 0;
+}
+
+
+static void wps_workaround_cred_key(struct wps_credential *cred)
+{
+	if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) &&
+	    cred->key_len > 8 && cred->key_len < 64 &&
+	    cred->key[cred->key_len - 1] == 0) {
+		/*
+		 * A deployed external registrar is known to encode ASCII
+		 * passphrases incorrectly. Remove the extra NULL termination
+		 * to fix the encoding.
+		 */
+		wpa_printf(MSG_DEBUG, "WPS: Workaround - remove NULL "
+			   "termination from ASCII passphrase");
+		cred->key_len--;
+	}
 }
 
 
@@ -279,6 +296,8 @@
 	    wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled))
 		return -1;
 
+	wps_workaround_cred_key(cred);
+
 	return 0;
 }
 
@@ -298,5 +317,7 @@
 	    wps_process_cred_mac_addr(cred, attr->mac_addr))
 		return -1;
 
-	return 0;
-}
+	wps_workaround_cred_key(cred);
+
+	return 0;
+}

Modified: wpasupplicant/branches/upstream/current/src/wps/wps_common.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_common.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_common.c (original)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_common.c Sun Feb 15 19:38:55 2009
@@ -82,6 +82,7 @@
 
 	dh_shared = dh_derive_shared(pubkey, wps->dh_privkey,
 				     dh_groups_get(WPS_DH_GROUP));
+	dh_shared = wpabuf_zeropad(dh_shared, 192);
 	if (dh_shared == NULL) {
 		wpa_printf(MSG_DEBUG, "WPS: Failed to derive DH shared key");
 		return -1;
@@ -320,3 +321,17 @@
 
 	wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, NULL);
 }
+
+
+void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part)
+{
+	union wps_event_data data;
+
+	if (wps->event_cb == NULL)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	data.pwd_auth_fail.enrollee = enrollee;
+	data.pwd_auth_fail.part = part;
+	wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, &data);
+}

Modified: wpasupplicant/branches/upstream/current/src/wps/wps_enrollee.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_enrollee.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_enrollee.c (original)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_enrollee.c Sun Feb 15 19:38:55 2009
@@ -32,10 +32,16 @@
 
 static int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg)
 {
-	wpa_printf(MSG_DEBUG, "WPS:  * Wi-Fi Protected Setup State");
+	u8 state;
+	if (wps->wps->ap)
+		state = wps->wps->wps_state;
+	else
+		state = WPS_STATE_NOT_CONFIGURED;
+	wpa_printf(MSG_DEBUG, "WPS:  * Wi-Fi Protected Setup State (%d)",
+		   state);
 	wpabuf_put_be16(msg, ATTR_WPS_STATE);
 	wpabuf_put_be16(msg, 1);
-	wpabuf_put_u8(msg, WPS_STATE_CONFIGURED);
+	wpabuf_put_u8(msg, WPS_STATE_NOT_CONFIGURED);
 	return 0;
 }
 
@@ -268,17 +274,34 @@
 }
 
 
+static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain)
+{
+	if (wps->wps->ap_settings) {
+		wpa_printf(MSG_DEBUG, "WPS:  * AP Settings (pre-configured)");
+		wpabuf_put_data(plain, wps->wps->ap_settings,
+				wps->wps->ap_settings_len);
+		return 0;
+	}
+
+	return wps_build_cred_ssid(wps, plain) ||
+		wps_build_cred_mac_addr(wps, plain) ||
+		wps_build_cred_auth_type(wps, plain) ||
+		wps_build_cred_encr_type(wps, plain) ||
+		wps_build_cred_network_key(wps, plain);
+}
+
+
 static struct wpabuf * wps_build_m7(struct wps_data *wps)
 {
 	struct wpabuf *msg, *plain;
 
 	wpa_printf(MSG_DEBUG, "WPS: Building Message M7");
 
-	plain = wpabuf_alloc(500);
+	plain = wpabuf_alloc(500 + wps->wps->ap_settings_len);
 	if (plain == NULL)
 		return NULL;
 
-	msg = wpabuf_alloc(1000);
+	msg = wpabuf_alloc(1000 + wps->wps->ap_settings_len);
 	if (msg == NULL) {
 		wpabuf_free(plain);
 		return NULL;
@@ -288,12 +311,7 @@
 	    wps_build_msg_type(msg, WPS_M7) ||
 	    wps_build_registrar_nonce(wps, msg) ||
 	    wps_build_e_snonce2(wps, plain) ||
-	    (wps->wps->ap &&
-	     (wps_build_cred_ssid(wps, plain) ||
-	      wps_build_cred_mac_addr(wps, plain) ||
-	      wps_build_cred_auth_type(wps, plain) ||
-	      wps_build_cred_encr_type(wps, plain) ||
-	      wps_build_cred_network_key(wps, plain))) ||
+	    (wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
 	    wps_build_key_wrap_auth(wps, plain) ||
 	    wps_build_encr_settings(wps, msg, plain) ||
 	    wps_build_authenticator(wps, msg)) {
@@ -568,6 +586,7 @@
 		wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does "
 			   "not match with the pre-committed value");
 		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+		wps_pwd_auth_fail_event(wps->wps, 1, 1);
 		return -1;
 	}
 
@@ -607,6 +626,7 @@
 		wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does "
 			   "not match with the pre-committed value");
 		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+		wps_pwd_auth_fail_event(wps->wps, 1, 2);
 		return -1;
 	}
 
@@ -630,8 +650,13 @@
 	    wps_process_cred(&attr, &wps->cred))
 		return -1;
 
-	if (wps->wps->cred_cb)
+	if (wps->wps->cred_cb) {
+		wps->cred.cred_attr = cred - 4;
+		wps->cred.cred_attr_len = cred_len + 4;
 		wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
+		wps->cred.cred_attr = NULL;
+		wps->cred.cred_attr_len = 0;
+	}
 
 	return 0;
 }
@@ -661,7 +686,8 @@
 
 
 static int wps_process_ap_settings_e(struct wps_data *wps,
-				     struct wps_parse_attr *attr)
+				     struct wps_parse_attr *attr,
+				     struct wpabuf *attrs)
 {
 	struct wps_credential cred;
 
@@ -674,8 +700,11 @@
 	wpa_printf(MSG_INFO, "WPS: Received new AP configuration from "
 		   "Registrar");
 
-	if (wps->wps->cred_cb)
+	if (wps->wps->cred_cb) {
+		cred.cred_attr = wpabuf_head(attrs);
+		cred.cred_attr_len = wpabuf_len(attrs);
 		wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
+	}
 
 	return 0;
 }
@@ -904,7 +933,7 @@
 	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
 	    wps_process_creds(wps, eattr.cred, eattr.cred_len,
 			      eattr.num_cred) ||
-	    wps_process_ap_settings_e(wps, &eattr)) {
+	    wps_process_ap_settings_e(wps, &eattr, decrypted)) {
 		wpabuf_free(decrypted);
 		wps->state = SEND_WSC_NACK;
 		return WPS_CONTINUE;
@@ -927,7 +956,7 @@
 	if (wps_parse_msg(msg, &attr) < 0)
 		return WPS_FAILURE;
 
-	if (attr.version == NULL || *attr.version != WPS_VERSION) {
+	if (!wps_version_supported(attr.version)) {
 		wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
 			   attr.version ? *attr.version : 0);
 		return WPS_FAILURE;
@@ -972,7 +1001,14 @@
 		return WPS_FAILURE;
 	}
 
-	if (ret == WPS_CONTINUE) {
+	/*
+	 * Save a copy of the last message for Authenticator derivation if we
+	 * are continuing. However, skip M2D since it is not authenticated and
+	 * neither is the ACK/NACK response frame. This allows the possibly
+	 * following M2 to be processed correctly by using the previously sent
+	 * M1 in Authenticator derivation.
+	 */
+	if (ret == WPS_CONTINUE && *attr.msg_type != WPS_M2D) {
 		/* Save a copy of the last message for Authenticator derivation
 		 */
 		wpabuf_free(wps->last_msg);
@@ -993,7 +1029,7 @@
 	if (wps_parse_msg(msg, &attr) < 0)
 		return WPS_FAILURE;
 
-	if (attr.version == NULL || *attr.version != WPS_VERSION) {
+	if (!wps_version_supported(attr.version)) {
 		wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
 			   attr.version ? *attr.version : 0);
 		return WPS_FAILURE;
@@ -1045,7 +1081,7 @@
 	if (wps_parse_msg(msg, &attr) < 0)
 		return WPS_FAILURE;
 
-	if (attr.version == NULL || *attr.version != WPS_VERSION) {
+	if (!wps_version_supported(attr.version)) {
 		wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
 			   attr.version ? *attr.version : 0);
 		return WPS_FAILURE;
@@ -1125,6 +1161,7 @@
 
 	switch (op_code) {
 	case WSC_MSG:
+	case WSC_UPnP:
 		return wps_process_wsc_msg(wps, msg);
 	case WSC_ACK:
 		return wps_process_wsc_ack(wps, msg);

Modified: wpasupplicant/branches/upstream/current/src/wps/wps_i.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_i.h?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_i.h (original)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_i.h Sun Feb 15 19:38:55 2009
@@ -101,6 +101,8 @@
 	 * config_error - Configuration Error value to be used in NACK
 	 */
 	u16 config_error;
+
+	int ext_reg;
 };
 
 
@@ -187,6 +189,7 @@
 					  size_t encr_len);
 void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg);
 void wps_success_event(struct wps_context *wps);
+void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part);
 
 /* wps_attr_parse.c */
 int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
@@ -235,4 +238,11 @@
 					       enum wsc_op_code op_code,
 					       const struct wpabuf *msg);
 
+
+static inline int wps_version_supported(const u8 *version)
+{
+	/* Require major version match, but allow minor version differences */
+	return version && (*version & 0xf0) == (WPS_VERSION & 0xf0);
+}
+
 #endif /* WPS_I_H */

Modified: wpasupplicant/branches/upstream/current/src/wps/wps_registrar.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_registrar.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_registrar.c (original)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_registrar.c Sun Feb 15 19:38:55 2009
@@ -21,6 +21,7 @@
 #include "eloop.h"
 #include "wps_i.h"
 #include "wps_dev_attr.h"
+#include "wps_upnp.h"
 
 
 struct wps_uuid_pin {
@@ -85,15 +86,25 @@
 			 const u8 *probe_resp_ie, size_t probe_resp_ie_len);
 	void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
 			      const struct wps_device_data *dev);
+	void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
+			       const u8 *uuid_e);
 	void *cb_ctx;
 
 	struct wps_uuid_pin *pins;
 	struct wps_pbc_session *pbc_sessions;
+
+	int skip_cred_build;
+	struct wpabuf *extra_cred;
+	int disable_auto_conf;
+	int sel_reg_dev_password_id_override;
+	int sel_reg_config_methods_override;
 };
 
 
 static int wps_set_ie(struct wps_registrar *reg);
 static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wps_registrar_set_selected_timeout(void *eloop_ctx,
+					       void *timeout_ctx);
 
 
 static void wps_registrar_add_pbc_session(struct wps_registrar *reg,
@@ -205,6 +216,31 @@
 }
 
 
+#ifdef CONFIG_WPS_UPNP
+static void wps_registrar_free_pending_m2(struct wps_context *wps)
+{
+	struct upnp_pending_message *p, *p2, *prev = NULL;
+	p = wps->upnp_msgs;
+	while (p) {
+		if (p->type == WPS_M2 || p->type == WPS_M2D) {
+			if (prev == NULL)
+				wps->upnp_msgs = p->next;
+			else
+				prev->next = p->next;
+			wpa_printf(MSG_DEBUG, "WPS UPnP: Drop pending M2/M2D");
+			p2 = p;
+			p = p->next;
+			wpabuf_free(p2->msg);
+			os_free(p2);
+			continue;
+		}
+		prev = p;
+		p = p->next;
+	}
+}
+#endif /* CONFIG_WPS_UPNP */
+
+
 static int wps_build_ap_setup_locked(struct wps_context *wps,
 				     struct wpabuf *msg)
 {
@@ -237,6 +273,8 @@
 	u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
 	if (!reg->selected_registrar)
 		return 0;
+	if (reg->sel_reg_dev_password_id_override >= 0)
+		id = reg->sel_reg_dev_password_id_override;
 	wpa_printf(MSG_DEBUG, "WPS:  * Device Password ID (%d)", id);
 	wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
 	wpabuf_put_be16(msg, 2);
@@ -254,6 +292,8 @@
 	methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
 	if (reg->pbc)
 		methods |= WPS_CONFIG_PUSHBUTTON;
+	if (reg->sel_reg_config_methods_override >= 0)
+		methods = reg->sel_reg_config_methods_override;
 	wpa_printf(MSG_DEBUG, "WPS:  * Selected Registrar Config Methods (%x)",
 		   methods);
 	wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
@@ -322,7 +362,20 @@
 	reg->new_psk_cb = cfg->new_psk_cb;
 	reg->set_ie_cb = cfg->set_ie_cb;
 	reg->pin_needed_cb = cfg->pin_needed_cb;
+	reg->reg_success_cb = cfg->reg_success_cb;
 	reg->cb_ctx = cfg->cb_ctx;
+	reg->skip_cred_build = cfg->skip_cred_build;
+	if (cfg->extra_cred) {
+		reg->extra_cred = wpabuf_alloc_copy(cfg->extra_cred,
+						    cfg->extra_cred_len);
+		if (reg->extra_cred == NULL) {
+			os_free(reg);
+			return NULL;
+		}
+	}
+	reg->disable_auto_conf = cfg->disable_auto_conf;
+	reg->sel_reg_dev_password_id_override = -1;
+	reg->sel_reg_config_methods_override = -1;
 
 	if (wps_set_ie(reg)) {
 		wps_registrar_deinit(reg);
@@ -342,8 +395,10 @@
 	if (reg == NULL)
 		return;
 	eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+	eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
 	wps_free_pins(reg->pins);
 	wps_free_pbc_sessions(reg->pbc_sessions);
+	wpabuf_free(reg->extra_cred);
 	os_free(reg);
 }
 
@@ -573,8 +628,9 @@
 			"WPS: Probe Request with WPS data received",
 			wps_data);
 
-	if (wps_parse_msg(wps_data, &attr) < 0 ||
-	    attr.version == NULL || *attr.version != WPS_VERSION) {
+	if (wps_parse_msg(wps_data, &attr) < 0)
+		return;
+	if (!wps_version_supported(attr.version)) {
 		wpa_printf(MSG_DEBUG, "WPS: Unsupported ProbeReq WPS IE "
 			   "version 0x%x", attr.version ? *attr.version : 0);
 		return;
@@ -614,6 +670,16 @@
 		return;
 
 	reg->pin_needed_cb(reg->cb_ctx, uuid_e, dev);
+}
+
+
+static void wps_cb_reg_success(struct wps_registrar *reg, const u8 *mac_addr,
+			       const u8 *uuid_e)
+{
+	if (reg->reg_success_cb == NULL)
+		return;
+
+	reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e);
 }
 
 
@@ -838,7 +904,7 @@
 	wpa_printf(MSG_DEBUG, "WPS:  * Network Index");
 	wpabuf_put_be16(msg, ATTR_NETWORK_INDEX);
 	wpabuf_put_be16(msg, 1);
-	wpabuf_put_u8(msg, 0);
+	wpabuf_put_u8(msg, 1);
 	return 0;
 }
 
@@ -892,7 +958,8 @@
 static int wps_build_cred_mac_addr(struct wpabuf *msg,
 				   struct wps_credential *cred)
 {
-	wpa_printf(MSG_DEBUG, "WPS:  * MAC Address");
+	wpa_printf(MSG_DEBUG, "WPS:  * MAC Address (" MACSTR ")",
+		   MAC2STR(cred->mac_addr));
 	wpabuf_put_be16(msg, ATTR_MAC_ADDR);
 	wpabuf_put_be16(msg, ETH_ALEN);
 	wpabuf_put_data(msg, cred->mac_addr, ETH_ALEN);
@@ -917,6 +984,9 @@
 static int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
 {
 	struct wpabuf *cred;
+
+	if (wps->wps->registrar->skip_cred_build)
+		goto skip_cred_build;
 
 	wpa_printf(MSG_DEBUG, "WPS:  * Credential");
 	os_memset(&wps->cred, 0, sizeof(wps->cred));
@@ -963,9 +1033,11 @@
 		}
 	}
 	wps->cred.encr_type = wps->encr_type;
-	os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN);
-
-	if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->wps->ap) {
+	/* Set MAC address in the Credential to be the AP's address (BSSID) */
+	os_memcpy(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN);
+
+	if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->wps->ap &&
+	    !wps->wps->registrar->disable_auto_conf) {
 		u8 r[16];
 		/* Generate a random passphrase */
 		if (os_get_random(r, sizeof(r)) < 0)
@@ -1021,6 +1093,12 @@
 	wpabuf_put_buf(msg, cred);
 	wpabuf_free(cred);
 
+skip_cred_build:
+	if (wps->wps->registrar->extra_cred) {
+		wpa_printf(MSG_DEBUG, "WPS:  * Credential (pre-configured)");
+		wpabuf_put_buf(msg, wps->wps->registrar->extra_cred);
+	}
+
 	return 0;
 }
 
@@ -1066,7 +1144,7 @@
 	    wps_build_rf_bands(&wps->wps->dev, msg) ||
 	    wps_build_assoc_state(wps, msg) ||
 	    wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
-	    wps_build_dev_password_id(msg, DEV_PW_DEFAULT) ||
+	    wps_build_dev_password_id(msg, wps->dev_pw_id) ||
 	    wps_build_os_version(&wps->wps->dev, msg) ||
 	    wps_build_authenticator(wps, msg)) {
 		wpabuf_free(msg);
@@ -1270,6 +1348,39 @@
 				      enum wsc_op_code *op_code)
 {
 	struct wpabuf *msg;
+
+#ifdef CONFIG_WPS_UPNP
+	if (wps->wps->wps_upnp) {
+		struct upnp_pending_message *p, *prev = NULL;
+		if (wps->ext_reg > 1)
+			wps_registrar_free_pending_m2(wps->wps);
+		p = wps->wps->upnp_msgs;
+		/* TODO: check pending message MAC address */
+		while (p && p->next) {
+			prev = p;
+			p = p->next;
+		}
+		if (p) {
+			wpa_printf(MSG_DEBUG, "WPS: Use pending message from "
+				   "UPnP");
+			if (prev)
+				prev->next = NULL;
+			else
+				wps->wps->upnp_msgs = NULL;
+			msg = p->msg;
+			os_free(p);
+			*op_code = WSC_MSG;
+			if (wps->ext_reg == 0)
+				wps->ext_reg = 1;
+			return msg;
+		}
+	}
+	if (wps->ext_reg) {
+		wpa_printf(MSG_DEBUG, "WPS: Using external Registrar, but no "
+			   "pending message available");
+		return NULL;
+	}
+#endif /* CONFIG_WPS_UPNP */
 
 	switch (wps->state) {
 	case SEND_M2:
@@ -1437,6 +1548,7 @@
 		wpa_printf(MSG_DEBUG, "WPS: E-Hash1 derived from E-S1 does "
 			   "not match with the pre-committed value");
 		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+		wps_pwd_auth_fail_event(wps->wps, 0, 1);
 		return -1;
 	}
 
@@ -1477,6 +1589,7 @@
 			   "not match with the pre-committed value");
 		wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e);
 		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+		wps_pwd_auth_fail_event(wps->wps, 0, 2);
 		return -1;
 	}
 
@@ -1864,7 +1977,7 @@
 	if (wps_parse_msg(msg, &attr) < 0)
 		return WPS_FAILURE;
 
-	if (attr.version == NULL || *attr.version != WPS_VERSION) {
+	if (!wps_version_supported(attr.version)) {
 		wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
 			   attr.version ? *attr.version : 0);
 		return WPS_FAILURE;
@@ -1885,6 +1998,17 @@
 
 	switch (*attr.msg_type) {
 	case WPS_M1:
+#ifdef CONFIG_WPS_UPNP
+		if (wps->wps->wps_upnp && attr.mac_addr) {
+			/* Remove old pending messages when starting new run */
+			wps_free_pending_msgs(wps->wps->upnp_msgs);
+			wps->wps->upnp_msgs = NULL;
+
+			upnp_wps_device_send_wlan_event(
+				wps->wps->wps_upnp, attr.mac_addr,
+				UPNP_WPS_WLANEVENT_TYPE_EAP, msg);
+		}
+#endif /* CONFIG_WPS_UPNP */
 		ret = wps_process_m1(wps, &attr);
 		break;
 	case WPS_M3:
@@ -1929,7 +2053,7 @@
 	if (wps_parse_msg(msg, &attr) < 0)
 		return WPS_FAILURE;
 
-	if (attr.version == NULL || *attr.version != WPS_VERSION) {
+	if (!wps_version_supported(attr.version)) {
 		wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
 			   attr.version ? *attr.version : 0);
 		return WPS_FAILURE;
@@ -1945,6 +2069,17 @@
 			   *attr.msg_type);
 		return WPS_FAILURE;
 	}
+
+#ifdef CONFIG_WPS_UPNP
+	if (wps->wps->wps_upnp && wps->ext_reg && wps->state == RECV_M2D_ACK &&
+	    upnp_wps_subscribers(wps->wps->wps_upnp)) {
+		if (wps->wps->upnp_msgs)
+			return WPS_CONTINUE;
+		wpa_printf(MSG_DEBUG, "WPS: Wait for response from an "
+			   "external Registrar");
+		return WPS_PENDING;
+	}
+#endif /* CONFIG_WPS_UPNP */
 
 	if (attr.registrar_nonce == NULL ||
 	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
@@ -1960,8 +2095,18 @@
 	}
 
 	if (wps->state == RECV_M2D_ACK) {
-		/* TODO: support for multiple registrars and sending of
-		 * multiple M2/M2D messages */
+#ifdef CONFIG_WPS_UPNP
+		if (wps->wps->wps_upnp &&
+		    upnp_wps_subscribers(wps->wps->wps_upnp)) {
+			if (wps->wps->upnp_msgs)
+				return WPS_CONTINUE;
+			if (wps->ext_reg == 0)
+				wps->ext_reg = 1;
+			wpa_printf(MSG_DEBUG, "WPS: Wait for response from an "
+				   "external Registrar");
+			return WPS_PENDING;
+		}
+#endif /* CONFIG_WPS_UPNP */
 
 		wpa_printf(MSG_DEBUG, "WPS: No more registrars available - "
 			   "terminate negotiation");
@@ -1985,7 +2130,7 @@
 	if (wps_parse_msg(msg, &attr) < 0)
 		return WPS_FAILURE;
 
-	if (attr.version == NULL || *attr.version != WPS_VERSION) {
+	if (!wps_version_supported(attr.version)) {
 		wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
 			   attr.version ? *attr.version : 0);
 		return WPS_FAILURE;
@@ -2001,6 +2146,14 @@
 			   *attr.msg_type);
 		return WPS_FAILURE;
 	}
+
+#ifdef CONFIG_WPS_UPNP
+	if (wps->wps->wps_upnp && wps->ext_reg) {
+		wpa_printf(MSG_DEBUG, "WPS: Negotiation using external "
+			   "Registrar terminated by the Enrollee");
+		return WPS_FAILURE;
+	}
+#endif /* CONFIG_WPS_UPNP */
 
 	if (attr.registrar_nonce == NULL ||
 	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
@@ -2052,7 +2205,8 @@
 
 	wpa_printf(MSG_DEBUG, "WPS: Received WSC_Done");
 
-	if (wps->state != RECV_DONE) {
+	if (wps->state != RECV_DONE &&
+	    (!wps->wps->wps_upnp || !wps->ext_reg)) {
 		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
 			   "receiving WSC_Done", wps->state);
 		return WPS_FAILURE;
@@ -2061,7 +2215,7 @@
 	if (wps_parse_msg(msg, &attr) < 0)
 		return WPS_FAILURE;
 
-	if (attr.version == NULL || *attr.version != WPS_VERSION) {
+	if (!wps_version_supported(attr.version)) {
 		wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
 			   attr.version ? *attr.version : 0);
 		return WPS_FAILURE;
@@ -2077,6 +2231,14 @@
 			   *attr.msg_type);
 		return WPS_FAILURE;
 	}
+
+#ifdef CONFIG_WPS_UPNP
+	if (wps->wps->wps_upnp && wps->ext_reg) {
+		wpa_printf(MSG_DEBUG, "WPS: Negotiation using external "
+			   "Registrar completed successfully");
+		return WPS_DONE;
+	}
+#endif /* CONFIG_WPS_UPNP */
 
 	if (attr.registrar_nonce == NULL ||
 	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
@@ -2094,7 +2256,7 @@
 	wpa_printf(MSG_DEBUG, "WPS: Negotiation completed successfully");
 
 	if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->new_psk &&
-	    wps->wps->ap) {
+	    wps->wps->ap && !wps->wps->registrar->disable_auto_conf) {
 		struct wps_credential cred;
 
 		wpa_printf(MSG_DEBUG, "WPS: Moving to Configured state based "
@@ -2136,6 +2298,8 @@
 		wps->new_psk = NULL;
 	}
 
+	wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e);
+
 	if (wps->pbc) {
 		wps_registrar_remove_pbc_session(wps->wps->registrar,
 						 wps->mac_addr_e, wps->uuid_e);
@@ -2157,6 +2321,39 @@
 	wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
 		   "op_code=%d)",
 		   (unsigned long) wpabuf_len(msg), op_code);
+
+#ifdef CONFIG_WPS_UPNP
+	if (wps->wps->wps_upnp && op_code == WSC_MSG && wps->ext_reg == 1) {
+		struct wps_parse_attr attr;
+		if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type &&
+		    *attr.msg_type == WPS_M3)
+			wps->ext_reg = 2; /* past M2/M2D phase */
+	}
+	if (wps->ext_reg > 1)
+		wps_registrar_free_pending_m2(wps->wps);
+	if (wps->wps->wps_upnp && wps->ext_reg &&
+	    wps->wps->upnp_msgs == NULL &&
+	    (op_code == WSC_MSG || op_code == WSC_Done)) {
+		struct wps_parse_attr attr;
+		int type;
+		if (wps_parse_msg(msg, &attr) < 0 || attr.msg_type == NULL)
+			type = -1;
+		else
+			type = *attr.msg_type;
+		wpa_printf(MSG_DEBUG, "WPS: Sending received message (type %d)"
+			   " to external Registrar for processing", type);
+		upnp_wps_device_send_wlan_event(wps->wps->wps_upnp,
+						wps->mac_addr_e,
+						UPNP_WPS_WLANEVENT_TYPE_EAP,
+						msg);
+		if (op_code == WSC_MSG)
+			return WPS_PENDING;
+	} else if (wps->wps->wps_upnp && wps->ext_reg && op_code == WSC_MSG) {
+		wpa_printf(MSG_DEBUG, "WPS: Skip internal processing - using "
+			   "external Registrar");
+		return WPS_CONTINUE;
+	}
+#endif /* CONFIG_WPS_UPNP */
 
 	switch (op_code) {
 	case WSC_MSG:
@@ -2177,3 +2374,75 @@
 		return WPS_FAILURE;
 	}
 }
+
+
+int wps_registrar_update_ie(struct wps_registrar *reg)
+{
+	return wps_set_ie(reg);
+}
+
+
+static void wps_registrar_set_selected_timeout(void *eloop_ctx,
+					       void *timeout_ctx)
+{
+	struct wps_registrar *reg = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar timed out - "
+		   "unselect Registrar");
+	reg->selected_registrar = 0;
+	reg->pbc = 0;
+	reg->sel_reg_dev_password_id_override = -1;
+	reg->sel_reg_config_methods_override = -1;
+	wps_set_ie(reg);
+}
+
+
+/**
+ * wps_registrar_set_selected_registrar - Notification of SetSelectedRegistrar
+ * @reg: Registrar data from wps_registrar_init()
+ * @msg: Received message from SetSelectedRegistrar
+ * @msg_len: Length of msg in octets
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when an AP receives a SetSelectedRegistrar UPnP
+ * message.
+ */
+int wps_registrar_set_selected_registrar(struct wps_registrar *reg,
+					 const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+
+	wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes",
+			msg);
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return -1;
+	if (!wps_version_supported(attr.version)) {
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported SetSelectedRegistrar "
+			   "version 0x%x", attr.version ? *attr.version : 0);
+		return -1;
+	}
+
+	if (attr.selected_registrar == NULL ||
+	    *attr.selected_registrar == 0) {
+		wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar: Disable "
+			   "Selected Registrar");
+		eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg,
+				     NULL);
+		wps_registrar_set_selected_timeout(reg, NULL);
+		return 0;
+	}
+
+	reg->selected_registrar = 1;
+	reg->sel_reg_dev_password_id_override = attr.dev_password_id ?
+		WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT;
+	reg->sel_reg_config_methods_override = attr.sel_reg_config_methods ?
+		WPA_GET_BE16(attr.sel_reg_config_methods) : -1;
+	wps_set_ie(reg);
+
+	eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+	eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
+			       wps_registrar_set_selected_timeout,
+			       reg, NULL);
+	return 0;
+}

Added: wpasupplicant/branches/upstream/current/src/wps/wps_upnp.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_upnp.c?rev=1324&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_upnp.c (added)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_upnp.c Sun Feb 15 19:38:55 2009
@@ -1,0 +1,1056 @@
+/*
+ * UPnP WPS Device
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j at w1.fi>
+ *
+ * See below for more details on licensing and code history.
+ */
+
+/*
+ * This has been greatly stripped down from the original file
+ * (upnp_wps_device.c) by Ted Merrill, Atheros Communications
+ * in order to eliminate use of the bulky libupnp library etc.
+ *
+ * History:
+ * upnp_wps_device.c is/was a shim layer between wps_opt_upnp.c and
+ * the libupnp library.
+ * The layering (by Sony) was well done; only a very minor modification
+ * to API of upnp_wps_device.c was required.
+ * libupnp was found to be undesirable because:
+ * -- It consumed too much code and data space
+ * -- It uses multiple threads, making debugging more difficult
+ *      and possibly reducing reliability.
+ * -- It uses static variables and only supports one instance.
+ * The shim and libupnp are here replaced by special code written
+ * specifically for the needs of hostapd.
+ * Various shortcuts can and are taken to keep the code size small.
+ * Generally, execution time is not as crucial.
+ *
+ * BUGS:
+ * -- UPnP requires that we be able to resolve domain names.
+ * While uncommon, if we have to do it then it will stall the entire
+ * hostapd program, which is bad.
+ * This is because we use the standard linux getaddrinfo() function
+ * which is syncronous.
+ * An asyncronous solution would be to use the free "ares" library.
+ * -- Does not have a robust output buffering scheme.  Uses a single
+ * fixed size output buffer per TCP/HTTP connection, with possible (although
+ * unlikely) possibility of overflow and likely excessive use of RAM.
+ * A better solution would be to write the HTTP output as a buffered stream,
+ * using chunking: (handle header specially, then) generate data with
+ * a printf-like function into a buffer, catching buffer full condition,
+ * then send it out surrounded by http chunking.
+ * -- There is some code that could be separated out into the common
+ * library to be shared with wpa_supplicant.
+ * -- Needs renaming with module prefix to avoid polluting the debugger
+ * namespace and causing possible collisions with other static fncs
+ * and structure declarations when using the debugger.
+ * -- Just what should be in the first event message sent after subscription
+ * for the WLANEvent field? If i pass it empty, Vista replies with OK
+ * but apparently barfs on the message.
+ * -- The http error code generation is pretty bogus, hopefully noone cares.
+ *
+ * Author: Ted Merrill, Atheros Communications, based upon earlier work
+ * as explained above and below.
+ *
+ * Copyright:
+ * Copyright 2008 Atheros Communications.
+ *
+ * The original header (of upnp_wps_device.c) reads:
+ *
+ *  Copyright (c) 2006-2007 Sony Corporation. All Rights Reserved.
+ *
+ *  File Name: upnp_wps_device.c
+ *  Description: EAP-WPS UPnP device source
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Sony Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Portions from Intel libupnp files, e.g. genlib/net/http/httpreadwrite.c
+ * typical header:
+ *
+ * Copyright (c) 2000-2003 Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * Overview of WPS over UPnP:
+ *
+ * UPnP is a protocol that allows devices to discover each other and control
+ * each other. In UPnP terminology, a device is either a "device" (a server
+ * that provides information about itself and allows itself to be controlled)
+ * or a "control point" (a client that controls "devices") or possibly both.
+ * This file implements a UPnP "device".
+ *
+ * For us, we use mostly basic UPnP discovery, but the control part of interest
+ * is WPS carried via UPnP messages. There is quite a bit of basic UPnP
+ * discovery to do before we can get to WPS, however.
+ *
+ * UPnP discovery begins with "devices" send out multicast UDP packets to a
+ * certain fixed multicast IP address and port, and "control points" sending
+ * out other such UDP packets.
+ *
+ * The packets sent by devices are NOTIFY packets (not to be confused with TCP
+ * NOTIFY packets that are used later) and those sent by control points are
+ * M-SEARCH packets. These packets contain a simple HTTP style header. The
+ * packets are sent redundantly to get around packet loss. Devices respond to
+ * M-SEARCH packets with HTTP-like UDP packets containing HTTP/1.1 200 OK
+ * messages, which give similar information as the UDP NOTIFY packets.
+ *
+ * The above UDP packets advertise the (arbitrary) TCP ports that the
+ * respective parties will listen to. The control point can then do a HTTP
+ * SUBSCRIBE (something like an HTTP PUT) after which the device can do a
+ * separate HTTP NOTIFY (also like an HTTP PUT) to do event messaging.
+ *
+ * The control point will also do HTTP GET of the "device file" listed in the
+ * original UDP information from the device (see UPNP_WPS_DEVICE_XML_FILE
+ * data), and based on this will do additional GETs... HTTP POSTs are done to
+ * cause an action.
+ *
+ * Beyond some basic information in HTTP headers, additional information is in
+ * the HTTP bodies, in a format set by the SOAP and XML standards, a markup
+ * language related to HTML used for web pages. This language is intended to
+ * provide the ultimate in self-documentation by providing a universal
+ * namespace based on pseudo-URLs called URIs. Note that although a URI looks
+ * like a URL (a web address), they are never accessed as such but are used
+ * only as identifiers.
+ *
+ * The POST of a GetDeviceInfo gets information similar to what might be
+ * obtained from a probe request or response on Wi-Fi. WPS messages M1-M8
+ * are passed via a POST of a PutMessage; the M1-M8 WPS messages are converted
+ * to a bin64 ascii representation for encapsulation. When proxying messages,
+ * WLANEvent and PutWLANResponse are used.
+ *
+ * This of course glosses over a lot of details.
+ */
+
+#include "includes.h"
+
+#include <assert.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
+
+#include "common.h"
+#include "uuid.h"
+#include "base64.h"
+#include "wps.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+
+/*
+ * UPnP allows a client ("control point") to send a server like us ("device")
+ * a domain name for registration, and we are supposed to resolve it. This is
+ * bad because, using the standard Linux library, we will stall the entire
+ * hostapd waiting for resolution.
+ *
+ * The "correct" solution would be to use an event driven library for domain
+ * name resolution such as "ares". However, this would increase code size
+ * further. Since it is unlikely that we'll actually see such domain names, we
+ * can just refuse to accept them.
+ */
+#define NO_DOMAIN_NAME_RESOLUTION 1  /* 1 to allow only dotted ip addresses */
+
+
+/*
+ * UPnP does not scale well. If we were in a room with thousands of control
+ * points then potentially we could be expected to handle subscriptions for
+ * each of them, which would exhaust our memory. So we must set a limit. In
+ * practice we are unlikely to see more than one or two.
+ */
+#define MAX_SUBSCRIPTIONS 4    /* how many subscribing clients we handle */
+#define MAX_ADDR_PER_SUBSCRIPTION 8
+
+
+/* Write the current date/time per RFC */
+void format_date(struct wpabuf *buf)
+{
+	const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
+	const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
+		"Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
+	struct tm *date;
+	time_t t;
+
+	t = time(NULL);
+	date = gmtime(&t);
+	wpabuf_printf(buf, "%s, %02d %s %d %02d:%02d:%02d GMT",
+		      &weekday_str[date->tm_wday * 4], date->tm_mday,
+		      &month_str[date->tm_mon * 4], date->tm_year + 1900,
+		      date->tm_hour, date->tm_min, date->tm_sec);
+}
+
+
+/***************************************************************************
+ * UUIDs (unique identifiers)
+ *
+ * These are supposed to be unique in all the world.
+ * Sometimes permanent ones are used, sometimes temporary ones
+ * based on random numbers... there are different rules for valid content
+ * of different types.
+ * Each uuid is 16 bytes long.
+ **************************************************************************/
+
+/* uuid_make -- construct a random UUID
+ * The UPnP documents don't seem to offer any guidelines as to which method to
+ * use for constructing UUIDs for subscriptions. Presumably any method from
+ * rfc4122 is good enough; I've chosen random number method.
+ */
+static void uuid_make(u8 uuid[UUID_LEN])
+{
+	os_get_random(uuid, UUID_LEN);
+
+	/* Replace certain bits as specified in rfc4122 or X.667 */
+	uuid[6] &= 0x0f; uuid[6] |= (4 << 4);   /* version 4 == random gen */
+	uuid[8] &= 0x3f; uuid[8] |= 0x80;
+}
+
+
+/*
+ * Subscriber address handling.
+ * Since a subscriber may have an arbitrary number of addresses, we have to
+ * add a bunch of code to handle them.
+ *
+ * Addresses are passed in text, and MAY be domain names instead of the (usual
+ * and expected) dotted IP addresses. Resolving domain names consumes a lot of
+ * resources. Worse, we are currently using the standard Linux getaddrinfo()
+ * which will block the entire program until complete or timeout! The proper
+ * solution would be to use the "ares" library or similar with more state
+ * machine steps etc. or just disable domain name resolution by setting
+ * NO_DOMAIN_NAME_RESOLUTION to 1 at top of this file.
+ */
+
+/* subscr_addr_delete -- delete single unlinked subscriber address
+ * (be sure to unlink first if need be)
+ */
+static void subscr_addr_delete(struct subscr_addr *a)
+{
+	/*
+	 * Note: do NOT free domain_and_port or path because they point to
+	 * memory within the allocation of "a".
+	 */
+	os_free(a);
+}
+
+
+/* subscr_addr_unlink -- unlink subscriber address from linked list */
+static void subscr_addr_unlink(struct subscription *s, struct subscr_addr *a)
+{
+	struct subscr_addr **listp = &s->addr_list;
+	s->n_addr--;
+	a->next->prev = a->prev;
+	a->prev->next = a->next;
+	if (*listp == a) {
+		if (a == a->next) {
+			/* last in queue */
+			*listp = NULL;
+			assert(s->n_addr == 0);
+		} else {
+			*listp = a->next;
+		}
+	}
+}
+
+
+/* subscr_addr_free_all -- unlink and delete list of subscriber addresses. */
+static void subscr_addr_free_all(struct subscription *s)
+{
+	struct subscr_addr **listp = &s->addr_list;
+	struct subscr_addr *a;
+	while ((a = *listp) != NULL) {
+		subscr_addr_unlink(s, a);
+		subscr_addr_delete(a);
+	}
+}
+
+
+/* subscr_addr_link -- add subscriber address to list of addresses */
+static void subscr_addr_link(struct subscription *s, struct subscr_addr *a)
+{
+	struct subscr_addr **listp = &s->addr_list;
+	s->n_addr++;
+	if (*listp == NULL) {
+		*listp = a->next = a->prev = a;
+	} else {
+		a->next = *listp;
+		a->prev = (*listp)->prev;
+		a->prev->next = a;
+		a->next->prev = a;
+	}
+}
+
+
+/* subscr_addr_add_url -- add address(es) for one url to subscription */
+static void subscr_addr_add_url(struct subscription *s, const char *url)
+{
+	int alloc_len;
+	char *scratch_mem = NULL;
+	char *mem;
+	char *domain_and_port;
+	char *delim;
+	char *path;
+	char *domain;
+	int port = 80;  /* port to send to (default is port 80) */
+	struct addrinfo hints;
+	struct addrinfo *result = NULL;
+	struct addrinfo *rp;
+	int rerr;
+	struct subscr_addr *a = NULL;
+
+	/* url MUST begin with http: */
+	if (os_strncasecmp(url, "http://", 7))
+		goto fail;
+	url += 7;
+
+	/* allocate memory for the extra stuff we need */
+	alloc_len = (2 * (os_strlen(url) + 1));
+	scratch_mem = os_zalloc(alloc_len);
+	if (scratch_mem == NULL)
+		goto fail;
+	mem = scratch_mem;
+	strcpy(mem, url);
+	domain_and_port = mem;
+	mem += 1 + os_strlen(mem);
+	delim = os_strchr(domain_and_port, '/');
+	if (delim) {
+		*delim++ = 0;   /* null terminate domain and port */
+		path = delim;
+	} else {
+		path = domain_and_port + os_strlen(domain_and_port);
+	}
+	domain = mem;
+	strcpy(domain, domain_and_port);
+	delim = strchr(domain, ':');
+	if (delim) {
+		*delim++ = 0;   /* null terminate domain */
+		if (isdigit(*delim))
+			port = atol(delim);
+	}
+
+	/*
+	 * getaddrinfo does the right thing with dotted decimal notations, or
+	 * will resolve domain names. Resolving domain names will unfortunately
+	 * hang the entire program until it is resolved or it times out
+	 * internal to getaddrinfo; fortunately we think that the use of actual
+	 * domain names (vs. dotted decimal notations) should be uncommon.
+	 */
+	os_memset(&hints, 0, sizeof(struct addrinfo));
+	hints.ai_family = AF_INET;      /* IPv4 */
+	hints.ai_socktype = SOCK_STREAM;
+#if NO_DOMAIN_NAME_RESOLUTION
+	/* Suppress domain name resolutions that would halt
+	 * the program for periods of time
+	 */
+	hints.ai_flags = AI_NUMERICHOST;
+#else
+	/* Allow domain name resolution. */
+	hints.ai_flags = 0;
+#endif
+	hints.ai_protocol = 0;          /* Any protocol? */
+	rerr = getaddrinfo(domain, NULL /* fill in port ourselves */,
+			   &hints, &result);
+	if (rerr) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s",
+			   rerr, gai_strerror(rerr), domain);
+		goto fail;
+	}
+	for (rp = result; rp; rp = rp->ai_next) {
+		/* Limit no. of address to avoid denial of service attack */
+		if (s->n_addr >= MAX_ADDR_PER_SUBSCRIPTION) {
+			wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: "
+				   "Ignoring excessive addresses");
+			break;
+		}
+
+		a = os_zalloc(sizeof(*a) + alloc_len);
+		if (a == NULL)
+			continue;
+		a->s = s;
+		mem = (void *) (a + 1);
+		a->domain_and_port = mem;
+		strcpy(mem, domain_and_port);
+		mem += 1 + strlen(mem);
+		a->path = mem;
+		if (path[0] != '/')
+			*mem++ = '/';
+		strcpy(mem, path);
+		mem += 1 + strlen(mem);
+		os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr));
+		a->saddr.sin_port = htons(port);
+
+		subscr_addr_link(s, a);
+		a = NULL;       /* don't free it below */
+	}
+
+fail:
+	if (result)
+		freeaddrinfo(result);
+	os_free(scratch_mem);
+	os_free(a);
+}
+
+
+/* subscr_addr_list_create -- create list from urls in string.
+ *      Each url is enclosed by angle brackets.
+ */
+static void subscr_addr_list_create(struct subscription *s,
+				    const char *url_list)
+{
+	char *end;
+	for (;;) {
+		while (*url_list == ' ' || *url_list == '\t')
+			url_list++;
+		if (*url_list != '<')
+			break;
+		url_list++;
+		end = os_strchr(url_list, '>');
+		if (end == NULL)
+			break;
+		*end++ = 0;
+		subscr_addr_add_url(s, url_list);
+		url_list = end;
+	}
+}
+
+
+int send_wpabuf(int fd, struct wpabuf *buf)
+{
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Send %lu byte message",
+		   (unsigned long) wpabuf_len(buf));
+	errno = 0;
+	if (write(fd, wpabuf_head(buf), wpabuf_len(buf)) !=
+	    (int) wpabuf_len(buf)) {
+		wpa_printf(MSG_ERROR, "WPS UPnP: Failed to send buffer: "
+			   "errno=%d (%s)",
+			   errno, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void wpabuf_put_property(struct wpabuf *buf, const char *name,
+				const char *value)
+{
+	wpabuf_put_str(buf, "<e:property>");
+	wpabuf_printf(buf, "<%s>", name);
+	if (value)
+		wpabuf_put_str(buf, value);
+	wpabuf_printf(buf, "</%s>", name);
+	wpabuf_put_str(buf, "</e:property>\n");
+}
+
+
+/**
+ * upnp_wps_device_send_event - Queue event messages for subscribers
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ *
+ * This function queues the last WLANEvent to be sent for all currently
+ * subscribed UPnP control points. sm->wlanevent must have been set with the
+ * encoded data before calling this function.
+ */
+static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm)
+{
+	/* Enqueue event message for all subscribers */
+	struct wpabuf *buf; /* holds event message */
+	int buf_size = 0;
+	struct subscription *s;
+	/* Actually, utf-8 is the default, but it doesn't hurt to specify it */
+	const char *format_head =
+		"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+		"<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
+	const char *format_tail = "</e:propertyset>\n";
+
+	if (sm->subscriptions == NULL) {
+		/* optimize */
+		return;
+	}
+
+	/* Determine buffer size needed first */
+	buf_size += os_strlen(format_head);
+	buf_size += 50 + 2 * os_strlen("WLANEvent");
+	if (sm->wlanevent)
+		buf_size += os_strlen(sm->wlanevent);
+	buf_size += os_strlen(format_tail);
+
+	buf = wpabuf_alloc(buf_size);
+	if (buf == NULL)
+		return;
+	wpabuf_put_str(buf, format_head);
+	wpabuf_put_property(buf, "WLANEvent", sm->wlanevent);
+	wpabuf_put_str(buf, format_tail);
+
+	wpa_printf(MSG_MSGDUMP, "WPS UPnP: WLANEvent message:\n%s",
+		   (char *) wpabuf_head(buf));
+
+	s = sm->subscriptions;
+	do {
+		if (event_add(s, buf)) {
+			struct subscription *s_old = s;
+			wpa_printf(MSG_INFO, "WPS UPnP: Dropping "
+				   "subscriber due to event backlog");
+			s = s_old->next;
+			subscription_unlink(s_old);
+			subscription_destroy(s_old);
+		} else {
+			s = s->next;
+		}
+	} while (s != sm->subscriptions);
+
+	wpabuf_free(buf);
+}
+
+
+/*
+ * Event subscription (subscriber machines register with us to receive event
+ * messages).
+ * This is the result of an incoming HTTP over TCP SUBSCRIBE request.
+ */
+
+/* subscription_unlink -- remove from the active list */
+void subscription_unlink(struct subscription *s)
+{
+	struct upnp_wps_device_sm *sm = s->sm;
+
+	if (s->next == s) {
+		/* only one? */
+		sm->subscriptions = NULL;
+	} else  {
+		if (sm->subscriptions == s)
+			sm->subscriptions = s->next;
+		s->next->prev = s->prev;
+		s->prev->next = s->next;
+	}
+	sm->n_subscriptions--;
+}
+
+
+/* subscription_link_to_end -- link to end of active list
+ * (should have high expiry time!)
+ */
+static void subscription_link_to_end(struct subscription *s)
+{
+	struct upnp_wps_device_sm *sm = s->sm;
+
+	if (sm->subscriptions) {
+		s->next = sm->subscriptions;
+		s->prev = s->next->prev;
+		s->prev->next = s;
+		s->next->prev = s;
+	} else {
+		sm->subscriptions = s->next = s->prev = s;
+	}
+	sm->n_subscriptions++;
+}
+
+
+/* subscription_destroy -- destroy an unlinked subscription
+ * Be sure to unlink first if necessary.
+ */
+void subscription_destroy(struct subscription *s)
+{
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s);
+	if (s->addr_list)
+		subscr_addr_free_all(s);
+	event_delete_all(s);
+	os_free(s);
+}
+
+
+/* subscription_list_age -- remove expired subscriptions */
+static void subscription_list_age(struct upnp_wps_device_sm *sm, time_t now)
+{
+	struct subscription *s;
+	while ((s = sm->subscriptions) != NULL && s->timeout_time < now) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Removing aged subscription");
+		subscription_unlink(s);
+		subscription_destroy(s);
+	}
+}
+
+
+/* subscription_find -- return existing subscription matching uuid, if any
+ * returns NULL if not found
+ */
+struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
+					const u8 uuid[UUID_LEN])
+{
+	struct subscription *s0 = sm->subscriptions;
+	struct subscription *s = s0;
+
+	if (s0 == NULL)
+		return NULL;
+	do {
+		if (os_memcmp(s->uuid, uuid, UUID_LEN) == 0)
+			return s; /* Found match */
+		s = s->next;
+	} while (s != s0);
+
+	return NULL;
+}
+
+
+/* subscription_first_event -- send format/queue event that is automatically
+ * sent on a new subscription.
+ */
+static int subscription_first_event(struct subscription *s)
+{
+	/*
+	 * Actually, utf-8 is the default, but it doesn't hurt to specify it.
+	 *
+	 * APStatus is apparently a bit set,
+	 * 0x1 = configuration change (but is always set?)
+	 * 0x10 = ap is locked
+	 *
+	 * Per UPnP spec, we send out the last value of each variable, even
+	 * for WLANEvent, whatever it was.
+	 */
+	char *wlan_event;
+	struct wpabuf *buf;
+	int ap_status = 1;      /* TODO: add 0x10 if access point is locked */
+	const char *head =
+		"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+		"<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
+	const char *tail = "</e:propertyset>\n";
+	char txt[10];
+
+	wlan_event = s->sm->wlanevent;
+	if (wlan_event == NULL || *wlan_event == '\0') {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: WLANEvent not known for "
+			   "initial event message");
+		wlan_event = "";
+	}
+	buf = wpabuf_alloc(500 + os_strlen(wlan_event));
+	if (buf == NULL)
+		return 1;
+
+	wpabuf_put_str(buf, head);
+	wpabuf_put_property(buf, "STAStatus", "1");
+	os_snprintf(txt, sizeof(txt), "%d", ap_status);
+	wpabuf_put_property(buf, "APStatus", txt);
+	if (*wlan_event)
+		wpabuf_put_property(buf, "WLANEvent", wlan_event);
+	wpabuf_put_str(buf, tail);
+
+	if (event_add(s, buf)) {
+		wpabuf_free(buf);
+		return 1;
+	}
+	wpabuf_free(buf);
+
+	return 0;
+}
+
+
+/**
+ * subscription_start - Rremember a UPnP control point to send events to.
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @callback_urls: malloc' mem given to the subscription
+ * Returns: %NULL on error, or pointer to new subscription structure.
+ */
+struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
+					 char *callback_urls)
+{
+	struct subscription *s;
+	time_t now = time(NULL);
+	time_t expire = now + UPNP_SUBSCRIBE_SEC;
+
+	/* Get rid of expired subscriptions so we have room */
+	subscription_list_age(sm, now);
+
+	/* If too many subscriptions, remove oldest */
+	if (sm->n_subscriptions >= MAX_SUBSCRIPTIONS) {
+		s = sm->subscriptions;
+		wpa_printf(MSG_INFO, "WPS UPnP: Too many subscriptions, "
+			   "trashing oldest");
+		subscription_unlink(s);
+		subscription_destroy(s);
+	}
+
+	s = os_zalloc(sizeof(*s));
+	if (s == NULL)
+		return NULL;
+
+	s->sm = sm;
+	s->timeout_time = expire;
+	uuid_make(s->uuid);
+	subscr_addr_list_create(s, callback_urls);
+	/* Add to end of list, since it has the highest expiration time */
+	subscription_link_to_end(s);
+	/* Queue up immediate event message (our last event)
+	 * as required by UPnP spec.
+	 */
+	if (subscription_first_event(s)) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Dropping subscriber due to "
+			   "event backlog");
+		subscription_unlink(s);
+		subscription_destroy(s);
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s",
+		   s, callback_urls);
+	os_free(callback_urls);
+	/* Schedule sending this */
+	event_send_all_later(sm);
+	return s;
+}
+
+
+/* subscription_renew -- find subscription and reset timeout */
+struct subscription * subscription_renew(struct upnp_wps_device_sm *sm,
+					 const u8 uuid[UUID_LEN])
+{
+	time_t now = time(NULL);
+	time_t expire = now + UPNP_SUBSCRIBE_SEC;
+	struct subscription *s = subscription_find(sm, uuid);
+	if (s == NULL)
+		return NULL;
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewed");
+	subscription_unlink(s);
+	s->timeout_time = expire;
+	/* add back to end of list, since it now has highest expiry */
+	subscription_link_to_end(s);
+	return s;
+}
+
+
+/**
+ * upnp_wps_device_send_wlan_event - Event notification
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @from_mac_addr: Source (Enrollee) MAC address for the event
+ * @ev_type: Event type
+ * @msg: Event data
+ * Returns: 0 on success, -1 on failure
+ *
+ * Tell external Registrars (UPnP control points) that something happened. In
+ * particular, events include WPS messages from clients that are proxied to
+ * external Registrars.
+ */
+int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
+				    const u8 from_mac_addr[ETH_ALEN],
+				    enum upnp_wps_wlanevent_type ev_type,
+				    const struct wpabuf *msg)
+{
+	int ret = -1;
+	char type[2];
+	const u8 *mac = from_mac_addr;
+	char mac_text[18];
+	u8 *raw = NULL;
+	size_t raw_len;
+	char *val;
+	size_t val_len;
+	int pos = 0;
+
+	if (!sm)
+		goto fail;
+
+	os_snprintf(type, sizeof(type), "%1u", ev_type);
+
+	raw_len = 1 + 17 + (msg ? wpabuf_len(msg) : 0);
+	raw = os_zalloc(raw_len);
+	if (!raw)
+		goto fail;
+
+	*(raw + pos) = (u8) ev_type;
+	pos += 1;
+	os_snprintf(mac_text, sizeof(mac_text), MACSTR, MAC2STR(mac));
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Proxying WLANEvent from %s",
+		   mac_text);
+	os_memcpy(raw + pos, mac_text, 17);
+	pos += 17;
+	if (msg) {
+		os_memcpy(raw + pos, wpabuf_head(msg), wpabuf_len(msg));
+		pos += wpabuf_len(msg);
+	}
+	raw_len = pos;
+
+	val = (char *) base64_encode(raw, raw_len, &val_len);
+	if (val == NULL)
+		goto fail;
+
+	os_free(sm->wlanevent);
+	sm->wlanevent = val;
+	upnp_wps_device_send_event(sm);
+
+	ret = 0;
+
+fail:
+	os_free(raw);
+
+	return ret;
+}
+
+
+/**
+ * get_netif_info - Get hw and IP addresses for network device
+ * @net_if: Selected network interface name
+ * @ip_addr: Buffer for returning IP address in network byte order
+ * @ip_addr_text: Buffer for returning a pointer to allocated IP address text
+ * @mac: Buffer for returning MAC address
+ * @mac_addr_text: Buffer for returning allocated MAC address text
+ * Returns: 0 on success, -1 on failure
+ */
+static int get_netif_info(const char *net_if, unsigned *ip_addr,
+			  char **ip_addr_text, u8 mac[ETH_ALEN],
+			  char **mac_addr_text)
+{
+	struct ifreq req;
+	int sock = -1;
+	struct sockaddr_in *addr;
+	struct in_addr in_addr;
+
+	*ip_addr_text = os_zalloc(16);
+	*mac_addr_text = os_zalloc(18);
+	if (*ip_addr_text == NULL || *mac_addr_text == NULL)
+		goto fail;
+
+	sock = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sock < 0)
+		goto fail;
+
+	os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
+	if (ioctl(sock, SIOCGIFADDR, &req) < 0) {
+		wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFADDR failed: %d (%s)",
+			   errno, strerror(errno));
+		goto fail;
+	}
+	addr = (void *) &req.ifr_addr;
+	*ip_addr = addr->sin_addr.s_addr;
+	in_addr.s_addr = *ip_addr;
+	os_snprintf(*ip_addr_text, 16, "%s", inet_ntoa(in_addr));
+
+	os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
+	if (ioctl(sock, SIOCGIFHWADDR, &req) < 0) {
+		wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFHWADDR failed: "
+			   "%d (%s)", errno, strerror(errno));
+		goto fail;
+	}
+	os_memcpy(mac, req.ifr_addr.sa_data, 6);
+	os_snprintf(*mac_addr_text, 18, MACSTR, MAC2STR(req.ifr_addr.sa_data));
+
+	close(sock);
+	return 0;
+
+fail:
+	if (sock >= 0)
+		close(sock);
+	os_free(*ip_addr_text);
+	*ip_addr_text = NULL;
+	os_free(*mac_addr_text);
+	*mac_addr_text = NULL;
+	return -1;
+}
+
+
+/**
+ * upnp_wps_device_stop - Stop WPS UPnP operations on an interface
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ */
+void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
+{
+	if (!sm || !sm->started)
+		return;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device");
+	web_listener_stop(sm);
+	while (sm->web_connections)
+		web_connection_stop(sm->web_connections);
+	while (sm->msearch_replies)
+		msearchreply_state_machine_stop(sm->msearch_replies);
+	while (sm->subscriptions)  {
+		struct subscription *s = sm->subscriptions;
+		subscription_unlink(s);
+		subscription_destroy(s);
+	}
+
+	advertisement_state_machine_stop(sm);
+	/* TODO: send byebye notifications */
+
+	event_send_stop_all(sm);
+	os_free(sm->wlanevent);
+	sm->wlanevent = NULL;
+	os_free(sm->net_if);
+	sm->net_if = NULL;
+	os_free(sm->mac_addr_text);
+	sm->mac_addr_text = NULL;
+	os_free(sm->ip_addr_text);
+	sm->ip_addr_text = NULL;
+	if (sm->multicast_sd >= 0)
+		close(sm->multicast_sd);
+	sm->multicast_sd = -1;
+	ssdp_listener_stop(sm);
+
+	sm->started = 0;
+}
+
+
+/**
+ * upnp_wps_device_start - Start WPS UPnP operations on an interface
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @net_if: Selected network interface name
+ * Returns: 0 on success, -1 on failure
+ */
+int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
+{
+	if (!sm || !net_if)
+		return -1;
+
+	if (sm->started)
+		upnp_wps_device_stop(sm);
+
+	sm->net_if = strdup(net_if);
+	sm->multicast_sd = -1;
+	sm->ssdp_sd = -1;
+	sm->started = 1;
+	sm->advertise_count = 0;
+
+	/* Fix up linux multicast handling */
+	if (add_ssdp_network(net_if))
+		goto fail;
+
+	/* Determine which IP and mac address we're using */
+	if (get_netif_info(net_if,
+			   &sm->ip_addr, &sm->ip_addr_text,
+			   sm->mac_addr, &sm->mac_addr_text)) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
+			   "for %s. Does it have IP address?", net_if);
+		goto fail;
+	}
+
+	/* Listen for incoming TCP connections so that others
+	 * can fetch our "xml files" from us.
+	 */
+	if (web_listener_start(sm))
+		goto fail;
+
+	/* Set up for receiving discovery (UDP) packets */
+	if (ssdp_listener_start(sm))
+		goto fail;
+
+	/* Set up for sending multicast */
+	if (ssdp_open_multicast(sm) < 0)
+		goto fail;
+
+	/*
+	 * Broadcast NOTIFY messages to let the world know we exist.
+	 * This is done via a state machine since the messages should not be
+	 * all sent out at once.
+	 */
+	if (advertisement_state_machine_start(sm))
+		goto fail;
+
+	return 0;
+
+fail:
+	upnp_wps_device_stop(sm);
+	return -1;
+}
+
+
+/**
+ * upnp_wps_device_deinit - Deinitialize WPS UPnP
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ */
+void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm)
+{
+	if (!sm)
+		return;
+
+	upnp_wps_device_stop(sm);
+
+	if (sm->peer.wps)
+		wps_deinit(sm->peer.wps);
+	os_free(sm->root_dir);
+	os_free(sm->desc_url);
+	os_free(sm->ctx);
+	os_free(sm);
+}
+
+
+/**
+ * upnp_wps_device_init - Initialize WPS UPnP
+ * @ctx: callback table; we must eventually free it
+ * @wps: Pointer to longterm WPS context
+ * @priv: External context data that will be used in callbacks
+ * Returns: WPS UPnP state or %NULL on failure
+ */
+struct upnp_wps_device_sm *
+upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
+		     void *priv)
+{
+	struct upnp_wps_device_sm *sm;
+
+	sm = os_zalloc(sizeof(*sm));
+	if (!sm) {
+		wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init failed");
+		return NULL;
+	}
+
+	sm->ctx = ctx;
+	sm->wps = wps;
+	sm->priv = priv;
+
+	return sm;
+}
+
+
+/**
+ * upnp_wps_subscribers - Check whether there are any event subscribers
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 if no subscribers, 1 if subscribers
+ */
+int upnp_wps_subscribers(struct upnp_wps_device_sm *sm)
+{
+	return sm->subscriptions != NULL;
+}

Added: wpasupplicant/branches/upstream/current/src/wps/wps_upnp.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_upnp.h?rev=1324&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_upnp.h (added)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_upnp.h Sun Feb 15 19:38:55 2009
@@ -1,0 +1,67 @@
+/*
+ * UPnP WPS Device
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j at w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef WPS_UPNP_H
+#define WPS_UPNP_H
+
+struct upnp_wps_device_sm;
+struct wps_context;
+struct wps_data;
+
+struct upnp_wps_peer {
+	struct wps_data *wps;
+};
+
+enum upnp_wps_wlanevent_type {
+	UPNP_WPS_WLANEVENT_TYPE_PROBE = 1,
+	UPNP_WPS_WLANEVENT_TYPE_EAP = 2
+};
+
+struct upnp_wps_device_ctx {
+	struct wpabuf * (*rx_req_get_device_info)(
+		void *priv, struct upnp_wps_peer *peer);
+	struct wpabuf * (*rx_req_put_message)(
+		void *priv, struct upnp_wps_peer *peer,
+		const struct wpabuf *msg);
+	struct wpabuf * (*rx_req_get_ap_settings)(void *priv,
+						  const struct wpabuf *msg);
+	int (*rx_req_set_ap_settings)(void *priv, const struct wpabuf *msg);
+	int (*rx_req_del_ap_settings)(void *priv, const struct wpabuf *msg);
+	struct wpabuf * (*rx_req_get_sta_settings)(void *priv,
+						   const struct wpabuf *msg);
+	int (*rx_req_set_sta_settings)(void *priv, const struct wpabuf *msg);
+	int (*rx_req_del_sta_settings)(void *priv, const struct wpabuf *msg);
+	int (*rx_req_put_wlan_response)(
+		void *priv, enum upnp_wps_wlanevent_type ev_type,
+		const u8 *mac_addr, const struct wpabuf *msg,
+		enum wps_msg_type msg_type);
+	int (*rx_req_set_selected_registrar)(void *priv,
+					     const struct wpabuf *msg);
+	int (*rx_req_reboot_ap)(void *priv, const struct wpabuf *msg);
+	int (*rx_req_reset_ap)(void *priv, const struct wpabuf *msg);
+	int (*rx_req_reboot_sta)(void *priv, const struct wpabuf *msg);
+	int (*rx_req_reset_sta)(void *priv, const struct wpabuf *msg);
+};
+
+struct upnp_wps_device_sm *
+upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
+		     void *priv);
+void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm);
+
+int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if);
+void upnp_wps_device_stop(struct upnp_wps_device_sm *sm);
+
+int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
+				    const u8 from_mac_addr[ETH_ALEN],
+				    enum upnp_wps_wlanevent_type ev_type,
+				    const struct wpabuf *msg);
+int upnp_wps_subscribers(struct upnp_wps_device_sm *sm);
+
+#endif /* WPS_UPNP_H */

Added: wpasupplicant/branches/upstream/current/src/wps/wps_upnp_event.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_upnp_event.c?rev=1324&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_upnp_event.c (added)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_upnp_event.c Sun Feb 15 19:38:55 2009
@@ -1,0 +1,534 @@
+/*
+ * UPnP WPS Device - Event processing
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j at w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+#include <assert.h>
+#include <fcntl.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "uuid.h"
+#include "httpread.h"
+#include "wps_defs.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+/*
+ * Event message generation (to subscribers)
+ *
+ * We make a separate copy for each message for each subscriber. This memory
+ * wasted could be limited (adding code complexity) by sharing copies, keeping
+ * a usage count and freeing when zero.
+ *
+ * Sending a message requires using a HTTP over TCP NOTIFY
+ * (like a PUT) which requires a number of states..
+ */
+
+#define MAX_EVENTS_QUEUED 20   /* How far behind queued events */
+#define EVENT_TIMEOUT_SEC 30   /* Drop sending event after timeout */
+
+/* How long to wait before sending event */
+#define EVENT_DELAY_SECONDS 0
+#define EVENT_DELAY_MSEC 0
+
+/*
+ * Event information that we send to each subscriber is remembered in this
+ * struct. The event cannot be sent by simple UDP; it has to be sent by a HTTP
+ * over TCP transaction which requires various states.. It may also need to be
+ * retried at a different address (if more than one is available).
+ *
+ * TODO: As an optimization we could share data between subscribers.
+ */
+struct wps_event_ {
+	struct wps_event_ *next;
+	struct wps_event_ *prev;        /* double linked list */
+	struct subscription *s;         /* parent */
+	unsigned subscriber_sequence;   /* which event for this subscription*/
+	int retry;                      /* which retry */
+	struct subscr_addr *addr;       /* address to connect to */
+	struct wpabuf *data;            /* event data to send */
+	/* The following apply while we are sending an event message. */
+	int sd;            /* -1 or socket descriptor for open connection */
+	int sd_registered;        /* nonzero if we must cancel registration */
+	struct httpread *hread; /* NULL or open connection for event msg */
+};
+
+
+static void event_timeout_handler(void *eloop_data, void *user_ctx);
+
+/* event_clean -- clean sockets etc. of event
+ * Leaves data, retry count etc. alone.
+ */
+static void event_clean(struct wps_event_ *e)
+{
+	if (e->s->current_event == e) {
+		eloop_cancel_timeout(event_timeout_handler, NULL, e);
+		e->s->current_event = NULL;
+	}
+	if (e->sd_registered) {
+		eloop_unregister_sock(e->sd, EVENT_TYPE_WRITE);
+		e->sd_registered = 0;
+	}
+	if (e->sd != -1) {
+		close(e->sd);
+		e->sd = -1;
+	}
+	if (e->hread)
+		httpread_destroy(e->hread);
+	e->hread = NULL;
+}
+
+
+/* event_delete -- delete single unqueued event
+ * (be sure to dequeue first if need be)
+ */
+void event_delete(struct wps_event_ *e)
+{
+	event_clean(e);
+	wpabuf_free(e->data);
+	os_free(e);
+}
+
+
+/* event_dequeue -- get next event from the queue
+ * Returns NULL if empty.
+ */
+static struct wps_event_ *event_dequeue(struct subscription *s)
+{
+	struct wps_event_ **event_head = &s->event_queue;
+	struct wps_event_ *e = *event_head;
+	if (e == NULL)
+		return NULL;
+	e->next->prev = e->prev;
+	e->prev->next = e->next;
+	if (*event_head == e) {
+		if (e == e->next) {
+			/* last in queue */
+			*event_head = NULL;
+		} else {
+			*event_head = e->next;
+		}
+	}
+	s->n_queue--;
+	e->next = e->prev = NULL;
+	/* but parent "s" is still valid */
+	return e;
+}
+
+
+/* event_enqueue_at_end -- add event to end of queue */
+static void event_enqueue_at_end(struct subscription *s, struct wps_event_ *e)
+{
+	struct wps_event_ **event_head = &s->event_queue;
+	if (*event_head == NULL) {
+		*event_head = e->next = e->prev = e;
+	} else {
+		e->next = *event_head;
+		e->prev = e->next->prev;
+		e->prev->next = e;
+		e->next->prev = e;
+	}
+	s->n_queue++;
+}
+
+
+/* event_enqueue_at_begin -- add event to begin of queue
+ * (appropriate for retrying event only)
+ */
+static void event_enqueue_at_begin(struct subscription *s,
+				   struct wps_event_ *e)
+{
+	struct wps_event_ **event_head = &s->event_queue;
+	if (*event_head == NULL) {
+		*event_head = e->next = e->prev = e;
+	} else {
+		e->prev = *event_head;
+		e->next = e->prev->next;
+		e->prev->next = e;
+		e->next->prev = e;
+		*event_head = e;
+	}
+	s->n_queue++;
+}
+
+
+/* event_delete_all -- delete entire event queue and current event */
+void event_delete_all(struct subscription *s)
+{
+	struct wps_event_ *e;
+	while ((e = event_dequeue(s)) != NULL)
+		event_delete(e);
+	if (s->current_event) {
+		event_delete(s->current_event);
+		/* will set: s->current_event = NULL;  */
+	}
+}
+
+
+/**
+ * event_retry - Called when we had a failure delivering event msg
+ * @e: Event
+ * @do_next_address: skip address e.g. on connect fail
+ */
+static void event_retry(struct wps_event_ *e, int do_next_address)
+{
+	struct subscription *s = e->s;
+	struct upnp_wps_device_sm *sm = s->sm;
+
+	event_clean(e);
+	/* will set: s->current_event = NULL; */
+
+	if (do_next_address)
+		e->retry++;
+	if (e->retry >= s->n_addr) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
+			   "for %s", e->addr->domain_and_port);
+		return;
+	}
+	event_enqueue_at_begin(s, e);
+	event_send_all_later(sm);
+}
+
+
+/* called if the overall event-sending process takes too long */
+static void event_timeout_handler(void *eloop_data, void *user_ctx)
+{
+	struct wps_event_ *e = user_ctx;
+	struct subscription *s = e->s;
+
+	assert(e == s->current_event);
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout");
+	event_retry(e, 1);
+}
+
+
+/* event_got_response_handler -- called back when http response is received. */
+static void event_got_response_handler(struct httpread *handle, void *cookie,
+				       enum httpread_event en)
+{
+	struct wps_event_ *e = cookie;
+	struct subscription *s = e->s;
+	struct upnp_wps_device_sm *sm = s->sm;
+	struct httpread *hread = e->hread;
+	int reply_code = 0;
+
+	assert(e == s->current_event);
+	eloop_cancel_timeout(event_timeout_handler, NULL, e);
+
+	if (en == HTTPREAD_EVENT_FILE_READY) {
+		if (httpread_hdr_type_get(hread) == HTTPREAD_HDR_TYPE_REPLY) {
+			reply_code = httpread_reply_code_get(hread);
+			if (reply_code == HTTP_OK) {
+				wpa_printf(MSG_DEBUG,
+					   "WPS UPnP: Got event reply OK from "
+					   "%s", e->addr->domain_and_port);
+				event_delete(e);
+				goto send_more;
+			} else {
+				wpa_printf(MSG_DEBUG, "WPS UPnP: Got event "
+					   "error reply code %d from %s",
+					   reply_code,
+					   e->addr->domain_and_port);
+				goto bad;
+			}
+		} else {
+			wpa_printf(MSG_DEBUG, "WPS UPnP: Got bogus event "
+				   "response %d from %s", en,
+				   e->addr->domain_and_port);
+		}
+	} else {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Event response timeout/fail "
+			   "for %s", e->addr->domain_and_port);
+		goto bad;
+	}
+	event_retry(e, 1);
+	goto send_more;
+
+send_more:
+	/* Schedule sending more if there is more to send */
+	if (s->event_queue)
+		event_send_all_later(sm);
+	return;
+
+bad:
+	/*
+	 * If other side doesn't like what we say, forget about them.
+	 * (There is no way to tell other side that we are dropping
+	 * them...).
+	 * Alternately, we could just do event_delete(e)
+	 */
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to errors");
+	subscription_unlink(s);
+	subscription_destroy(s);
+}
+
+
+/* event_send_tx_ready -- actually write event message
+ *
+ * Prequisite: subscription socket descriptor has become ready to
+ * write (because connection to subscriber has been made).
+ *
+ * It is also possible that we are called because the connect has failed;
+ * it is possible to test for this, or we can just go ahead and then
+ * the write will fail.
+ */
+static void event_send_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wps_event_ *e = sock_ctx;
+	struct subscription *s = e->s;
+	struct wpabuf *buf;
+	char *b;
+
+	assert(e == s->current_event);
+	assert(e->sd == sock);
+
+	buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
+	if (buf == NULL) {
+		event_retry(e, 0);
+		goto bad;
+	}
+	wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
+	wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
+	wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port);
+	wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"
+		       "NT: upnp:event\r\n"
+		       "NTS: upnp:propchange\r\n");
+	wpabuf_put_str(buf, "SID: uuid:");
+	b = wpabuf_put(buf, 0);
+	uuid_bin2str(s->uuid, b, 80);
+	wpabuf_put(buf, os_strlen(b));
+	wpabuf_put_str(buf, "\r\n");
+	wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence);
+	wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n",
+		      (int) wpabuf_len(e->data));
+	wpabuf_put_str(buf, "\r\n"); /* terminating empty line */
+	wpabuf_put_buf(buf, e->data);
+
+	/* Since the message size is pretty small, we should be
+	 * able to get the operating system to buffer what we give it
+	 * and not have to come back again later to write more...
+	 */
+#if 0
+	/* we could: Turn blocking back on? */
+	fcntl(e->sd, F_SETFL, 0);
+#endif
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Sending event to %s",
+		   e->addr->domain_and_port);
+	if (send_wpabuf(e->sd, buf) < 0) {
+		event_retry(e, 1);
+		goto bad;
+	}
+	wpabuf_free(buf);
+	buf = NULL;
+
+	if (e->sd_registered) {
+		e->sd_registered = 0;
+		eloop_unregister_sock(e->sd, EVENT_TYPE_WRITE);
+	}
+	/* Set up to read the reply */
+	e->hread = httpread_create(e->sd, event_got_response_handler,
+				   e /* cookie */,
+				   0 /* no data expected */,
+				   EVENT_TIMEOUT_SEC);
+	if (e->hread == NULL) {
+		wpa_printf(MSG_ERROR, "WPS UPnP: httpread_create failed");
+		event_retry(e, 0);
+		goto bad;
+	}
+	return;
+
+bad:
+	/* Schedule sending more if there is more to send */
+	if (s->event_queue)
+		event_send_all_later(s->sm);
+	wpabuf_free(buf);
+}
+
+
+/* event_send_start -- prepare to send a event message to subscriber
+ *
+ * This gets complicated because:
+ * -- The message is sent via TCP and we have to keep the stream open
+ *      for 30 seconds to get a response... then close it.
+ * -- But we might have other event happen in the meantime...
+ *      we have to queue them, if we lose them then the subscriber will
+ *      be forced to unsubscribe and subscribe again.
+ * -- If multiple URLs are provided then we are supposed to try successive
+ *      ones after 30 second timeout.
+ * -- The URLs might use domain names instead of dotted decimal addresses,
+ *      and resolution of those may cause unwanted sleeping.
+ * -- Doing the initial TCP connect can take a while, so we have to come
+ *      back after connection and then send the data.
+ *
+ * Returns nonzero on error;
+ *
+ * Prerequisite: No current event send (s->current_event == NULL)
+ *      and non-empty queue.
+ */
+static int event_send_start(struct subscription *s)
+{
+	struct wps_event_ *e;
+	int itry;
+
+	/*
+	 * Assume we are called ONLY with no current event and ONLY with
+	 * nonempty event queue and ONLY with at least one address to send to.
+	 */
+	assert(s->addr_list != NULL);
+	assert(s->current_event == NULL);
+	assert(s->event_queue != NULL);
+
+	s->current_event = e = event_dequeue(s);
+
+	/* Use address acc. to no. of retries */
+	e->addr = s->addr_list;
+	for (itry = 0; itry < e->retry; itry++)
+		e->addr = e->addr->next;
+
+	e->sd = socket(AF_INET, SOCK_STREAM, 0);
+	if (e->sd < 0) {
+		event_retry(e, 0);
+		return -1;
+	}
+	/* set non-blocking so we don't sleep waiting for connection */
+	if (fcntl(e->sd, F_SETFL, O_NONBLOCK) != 0) {
+		event_retry(e, 0);
+		return -1;
+	}
+	/*
+	 * Start the connect. It might succeed immediately but more likely will
+	 * return errno EINPROGRESS.
+	 */
+	if (connect(e->sd, (struct sockaddr *) &e->addr->saddr,
+		    sizeof(e->addr->saddr))) {
+		if (errno != EINPROGRESS) {
+			event_retry(e, 1);
+			return -1;
+		}
+	}
+	/* Call back when ready for writing (or on failure...). */
+	if (eloop_register_sock(e->sd, EVENT_TYPE_WRITE, event_send_tx_ready,
+				NULL, e)) {
+		event_retry(e, 0);
+		return -1;
+	}
+	e->sd_registered = 1;
+	/* Don't wait forever! */
+	if (eloop_register_timeout(EVENT_TIMEOUT_SEC, 0, event_timeout_handler,
+				   NULL, e)) {
+		event_retry(e, 0);
+		return -1;
+	}
+	return 0;
+}
+
+
+/* event_send_all_later_handler -- actually send events as needed */
+void event_send_all_later_handler(void *eloop_data, void *user_ctx)
+{
+	struct upnp_wps_device_sm *sm = user_ctx;
+	struct subscription *s;
+	struct subscription *s_old;
+	int nerrors = 0;
+
+	sm->event_send_all_queued = 0;
+	s = sm->subscriptions;
+	if (s == NULL)
+		return;
+	do {
+		if (s->addr_list == NULL) {
+			/* if we've given up on all addresses */
+			wpa_printf(MSG_DEBUG, "WPS UPnP: Removing "
+				   "subscription with no addresses");
+			s_old = s;
+			s = s_old->next;
+			subscription_unlink(s_old);
+			subscription_destroy(s_old);
+		} else {
+			if (s->current_event == NULL /* not busy */ &&
+			    s->event_queue != NULL /* more to do */) {
+				if (event_send_start(s))
+					nerrors++;
+			}
+			s = s->next;
+		}
+	} while (sm->subscriptions != NULL && s != sm->subscriptions);
+
+	if (nerrors) {
+		/* Try again later */
+		event_send_all_later(sm);
+	}
+}
+
+
+/* event_send_all_later -- schedule sending events to all subscribers
+ * that need it.
+ * This avoids two problems:
+ * -- After getting a subscription, we should not send the first event
+ *      until after our reply is fully queued to be sent back,
+ * -- Possible stack depth or infinite recursion issues.
+ */
+void event_send_all_later(struct upnp_wps_device_sm *sm)
+{
+	/*
+	 * The exact time in the future isn't too important. Waiting a bit
+	 * might let us do several together.
+	 */
+	if (sm->event_send_all_queued)
+		return;
+	sm->event_send_all_queued = 1;
+	eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC,
+			       event_send_all_later_handler, NULL, sm);
+}
+
+
+/* event_send_stop_all -- cleanup */
+void event_send_stop_all(struct upnp_wps_device_sm *sm)
+{
+	if (sm->event_send_all_queued)
+		eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
+	sm->event_send_all_queued = 0;
+}
+
+
+/**
+ * event_add - Add a new event to a queue
+ * @s: Subscription
+ * @data: Event data (is copied; caller retains ownership)
+ * Returns: 0 on success, 1 on error
+ */
+int event_add(struct subscription *s, const struct wpabuf *data)
+{
+	struct wps_event_ *e;
+
+	if (s->n_queue >= MAX_EVENTS_QUEUED) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
+			   "subscriber");
+		return 1;
+	}
+
+	e = os_zalloc(sizeof(*e));
+	if (e == NULL)
+		return 1;
+	e->s = s;
+	e->sd = -1;
+	e->data = wpabuf_dup(data);
+	if (e->data == NULL) {
+		os_free(e);
+		return 1;
+	}
+	e->subscriber_sequence = s->next_subscriber_sequence++;
+	if (s->next_subscriber_sequence == 0)
+		s->next_subscriber_sequence++;
+	event_enqueue_at_end(s, e);
+	event_send_all_later(s->sm);
+	return 0;
+}

Added: wpasupplicant/branches/upstream/current/src/wps/wps_upnp_i.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_upnp_i.h?rev=1324&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_upnp_i.h (added)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_upnp_i.h Sun Feb 15 19:38:55 2009
@@ -1,0 +1,193 @@
+/*
+ * UPnP for WPS / internal definitions
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j at w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef WPS_UPNP_I_H
+#define WPS_UPNP_I_H
+
+#define UPNP_MULTICAST_ADDRESS  "239.255.255.250" /* for UPnP multicasting */
+#define UPNP_MULTICAST_PORT 1900 /* UDP port to monitor for UPnP */
+
+/* min subscribe time per UPnP standard */
+#define UPNP_SUBSCRIBE_SEC_MIN 1800
+/* subscribe time we use */
+#define UPNP_SUBSCRIBE_SEC (UPNP_SUBSCRIBE_SEC_MIN + 1)
+
+/* "filenames" used in URLs that we service via our "web server": */
+#define UPNP_WPS_DEVICE_XML_FILE "wps_device.xml"
+#define UPNP_WPS_SCPD_XML_FILE   "wps_scpd.xml"
+#define UPNP_WPS_DEVICE_CONTROL_FILE "wps_control"
+#define UPNP_WPS_DEVICE_EVENT_FILE "wps_event"
+
+
+struct web_connection;
+struct subscription;
+struct upnp_wps_device_sm;
+
+
+enum http_reply_code {
+	HTTP_OK = 200,
+	HTTP_BAD_REQUEST = 400,
+	UPNP_INVALID_ACTION = 401,
+	UPNP_INVALID_ARGS = 402,
+	HTTP_PRECONDITION_FAILED = 412,
+	HTTP_INTERNAL_SERVER_ERROR = 500,
+	HTTP_UNIMPLEMENTED = 501,
+	UPNP_ACTION_FAILED = 501,
+	UPNP_ARG_VALUE_INVALID = 600,
+	UPNP_ARG_VALUE_OUT_OF_RANGE = 601,
+	UPNP_OUT_OF_MEMORY = 603
+};
+
+
+enum advertisement_type_enum {
+	ADVERTISE_UP = 0,
+	ADVERTISE_DOWN = 1,
+	MSEARCH_REPLY = 2
+};
+
+/*
+ * Advertisements are broadcast via UDP NOTIFYs, and are also the essence of
+ * the reply to UDP M-SEARCH requests. This struct handles both cases.
+ *
+ * A state machine is needed because a number of variant forms must be sent in
+ * separate packets and spread out in time to avoid congestion.
+ */
+struct advertisement_state_machine {
+	/* double-linked list */
+	struct advertisement_state_machine *next;
+	struct advertisement_state_machine *prev;
+	struct upnp_wps_device_sm *sm; /* parent */
+	enum advertisement_type_enum type;
+	int state;
+	int nerrors;
+	struct sockaddr_in client; /* for M-SEARCH replies */
+};
+
+
+/*
+ * An address of a subscriber (who may have multiple addresses). We are
+ * supposed to send (via TCP) updates to each subscriber, trying each address
+ * for a subscriber until we find one that seems to work.
+ */
+struct subscr_addr {
+	/* double linked list */
+	struct subscr_addr *next;
+	struct subscr_addr *prev;
+	struct subscription *s; /* parent */
+	char *domain_and_port; /* domain and port part of url */
+	char *path; /* "filepath" part of url (from "mem") */
+	struct sockaddr_in saddr; /* address for doing connect */
+};
+
+
+/*
+ * Subscribers to our events are recorded in this struct. This includes a max
+ * of one outgoing connection (sending an "event message") per subscriber. We
+ * also have to age out subscribers unless they renew.
+ */
+struct subscription {
+	/* double linked list */
+	struct subscription *next;
+	struct subscription *prev;
+	struct upnp_wps_device_sm *sm; /* parent */
+	time_t timeout_time; /* when to age out the subscription */
+	unsigned next_subscriber_sequence; /* number our messages */
+	/*
+	 * This uuid identifies the subscription and is randomly generated by
+	 * us and given to the subscriber when the subscription is accepted;
+	 * and is then included with each event sent to the subscriber.
+	 */
+	u8 uuid[UUID_LEN];
+	/* Linked list of address alternatives (rotate through on failure) */
+	struct subscr_addr *addr_list;
+	int n_addr; /* Number of addresses in list */
+	struct wps_event_ *event_queue; /* Queued event messages. */
+	int n_queue; /* How many events are queued */
+	struct wps_event_ *current_event; /* non-NULL if being sent (not in q)
+					   */
+};
+
+
+/*
+ * Our instance data corresponding to one WiFi network interface
+ * (multiple might share the same wired network interface!).
+ *
+ * This is known as an opaque struct declaration to users of the WPS UPnP code.
+ */
+struct upnp_wps_device_sm {
+	struct upnp_wps_device_ctx *ctx; /* callback table */
+	struct wps_context *wps;
+	void *priv;
+	char *root_dir;
+	char *desc_url;
+	int started; /* nonzero if we are active */
+	char *net_if; /* network interface we use */
+	char *mac_addr_text; /* mac addr of network i.f. we use */
+	u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */
+	char *ip_addr_text; /* IP address of network i.f. we use */
+	unsigned ip_addr; /* IP address of network i.f. we use (host order) */
+	int multicast_sd; /* send multicast messages over this socket */
+	int ssdp_sd; /* receive discovery UPD packets on socket */
+	int ssdp_sd_registered; /* nonzero if we must unregister */
+	unsigned advertise_count; /* how many advertisements done */
+	struct advertisement_state_machine advertisement;
+	struct advertisement_state_machine *msearch_replies;
+	int n_msearch_replies; /* no. of pending M-SEARCH replies */
+	int web_port; /* our port that others get xml files from */
+	int web_sd; /* socket to listen for web requests */
+	int web_sd_registered; /* nonzero if we must cancel registration */
+	struct web_connection *web_connections; /* linked list */
+	int n_web_connections; /* no. of pending web connections */
+	/* Note: subscriptions are kept in expiry order */
+	struct subscription *subscriptions; /* linked list */
+	int n_subscriptions; /* no of current subscriptions */
+	int event_send_all_queued; /* if we are scheduled to send events soon
+				    */
+
+	char *wlanevent; /* the last WLANEvent data */
+
+	/* FIX: maintain separate structures for each UPnP peer */
+	struct upnp_wps_peer peer;
+};
+
+/* wps_upnp.c */
+void format_date(struct wpabuf *buf);
+struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
+					 char *callback_urls);
+struct subscription * subscription_renew(struct upnp_wps_device_sm *sm,
+					 const u8 uuid[UUID_LEN]);
+void subscription_unlink(struct subscription *s);
+void subscription_destroy(struct subscription *s);
+struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
+					const u8 uuid[UUID_LEN]);
+int send_wpabuf(int fd, struct wpabuf *buf);
+
+/* wps_upnp_ssdp.c */
+void msearchreply_state_machine_stop(struct advertisement_state_machine *a);
+int advertisement_state_machine_start(struct upnp_wps_device_sm *sm);
+void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm);
+void ssdp_listener_stop(struct upnp_wps_device_sm *sm);
+int ssdp_listener_start(struct upnp_wps_device_sm *sm);
+int add_ssdp_network(char *net_if);
+int ssdp_open_multicast(struct upnp_wps_device_sm *sm);
+
+/* wps_upnp_web.c */
+void web_connection_stop(struct web_connection *c);
+int web_listener_start(struct upnp_wps_device_sm *sm);
+void web_listener_stop(struct upnp_wps_device_sm *sm);
+
+/* wps_upnp_event.c */
+int event_add(struct subscription *s, const struct wpabuf *data);
+void event_delete(struct wps_event_ *e);
+void event_delete_all(struct subscription *s);
+void event_send_all_later(struct upnp_wps_device_sm *sm);
+void event_send_stop_all(struct upnp_wps_device_sm *sm);
+
+#endif /* WPS_UPNP_I_H */

Added: wpasupplicant/branches/upstream/current/src/wps/wps_upnp_ssdp.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_upnp_ssdp.c?rev=1324&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_upnp_ssdp.c (added)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_upnp_ssdp.c Sun Feb 15 19:38:55 2009
@@ -1,0 +1,886 @@
+/*
+ * UPnP SSDP for WPS
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j at w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <net/route.h>
+
+#include "common.h"
+#include "uuid.h"
+#include "eloop.h"
+#include "wps.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+#define UPNP_CACHE_SEC (UPNP_CACHE_SEC_MIN + 1) /* cache time we use */
+#define UPNP_CACHE_SEC_MIN 1800 /* min cachable time per UPnP standard */
+#define UPNP_ADVERTISE_REPEAT 2 /* no more than 3 */
+#define MULTICAST_MAX_READ 1600 /* max bytes we'll read for UPD request */
+#define MAX_MSEARCH 20          /* max simultaneous M-SEARCH replies ongoing */
+#define SSDP_TARGET  "239.0.0.0"
+#define SSDP_NETMASK "255.0.0.0"
+
+
+/* Check tokens for equality, where tokens consist of letters, digits,
+ * underscore and hyphen, and are matched case insensitive.
+ */
+static int token_eq(const char *s1, const char *s2)
+{
+	int c1;
+	int c2;
+	int end1 = 0;
+	int end2 = 0;
+	for (;;) {
+		c1 = *s1++;
+		c2 = *s2++;
+		if (isalpha(c1) && isupper(c1))
+			c1 = tolower(c1);
+		if (isalpha(c2) && isupper(c2))
+			c2 = tolower(c2);
+		end1 = !(isalnum(c1) || c1 == '_' || c1 == '-');
+		end2 = !(isalnum(c2) || c2 == '_' || c2 == '-');
+		if (end1 || end2 || c1 != c2)
+			break;
+	}
+	return end1 && end2; /* reached end of both words? */
+}
+
+
+/* Return length of token (see above for definition of token) */
+static int token_length(const char *s)
+{
+	const char *begin = s;
+	for (;; s++) {
+		int c = *s;
+		int end = !(isalnum(c) || c == '_' || c == '-');
+		if (end)
+			break;
+	}
+	return s - begin;
+}
+
+
+/* return length of interword separation.
+ * This accepts only spaces/tabs and thus will not traverse a line
+ * or buffer ending.
+ */
+static int word_separation_length(const char *s)
+{
+	const char *begin = s;
+	for (;; s++) {
+		int c = *s;
+		if (c == ' ' || c == '\t')
+			continue;
+		break;
+	}
+	return s - begin;
+}
+
+
+/* No. of chars through (including) end of line */
+static int line_length(const char *l)
+{
+	const char *lp = l;
+	while (*lp && *lp != '\n')
+		lp++;
+	if (*lp == '\n')
+		lp++;
+	return lp - l;
+}
+
+
+/* No. of chars excluding trailing whitespace */
+static int line_length_stripped(const char *l)
+{
+	const char *lp = l + line_length(l);
+	while (lp > l && !isgraph(lp[-1]))
+		lp--;
+	return lp - l;
+}
+
+
+static int str_starts(const char *str, const char *start)
+{
+	return os_strncmp(str, start, os_strlen(start)) == 0;
+}
+
+
+/***************************************************************************
+ * Advertisements.
+ * These are multicast to the world to tell them we are here.
+ * The individual packets are spread out in time to limit loss,
+ * and then after a much longer period of time the whole sequence
+ * is repeated again (for NOTIFYs only).
+ **************************************************************************/
+
+/**
+ * next_advertisement - Build next message and advance the state machine
+ * @a: Advertisement state
+ * @islast: Buffer for indicating whether this is the last message (= 1)
+ * Returns: The new message (caller is responsible for freeing this)
+ *
+ * Note: next_advertisement is shared code with msearchreply_* functions
+ */
+static struct wpabuf *
+next_advertisement(struct advertisement_state_machine *a, int *islast)
+{
+	struct wpabuf *msg;
+	char *NTString = "";
+	char uuid_string[80];
+
+	*islast = 0;
+	uuid_bin2str(a->sm->wps->uuid, uuid_string, sizeof(uuid_string));
+	msg = wpabuf_alloc(800); /* more than big enough */
+	if (msg == NULL)
+		goto fail;
+	switch (a->type) {
+	case ADVERTISE_UP:
+	case ADVERTISE_DOWN:
+		NTString = "NT";
+		wpabuf_put_str(msg, "NOTIFY * HTTP/1.1\r\n");
+		wpabuf_printf(msg, "HOST: %s:%d\r\n",
+			      UPNP_MULTICAST_ADDRESS, UPNP_MULTICAST_PORT);
+		wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n",
+			      UPNP_CACHE_SEC);
+		wpabuf_printf(msg, "NTS: %s\r\n",
+			      (a->type == ADVERTISE_UP ?
+			       "ssdp:alive" : "ssdp:byebye"));
+		break;
+	case MSEARCH_REPLY:
+		NTString = "ST";
+		wpabuf_put_str(msg, "HTTP/1.1 200 OK\r\n");
+		wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n",
+			      UPNP_CACHE_SEC);
+
+		wpabuf_put_str(msg, "DATE: ");
+		format_date(msg);
+		wpabuf_put_str(msg, "\r\n");
+
+		wpabuf_put_str(msg, "EXT:\r\n");
+		break;
+	}
+
+	if (a->type != ADVERTISE_DOWN) {
+		/* Where others may get our XML files from */
+		wpabuf_printf(msg, "LOCATION: http://%s:%d/%s\r\n",
+			      a->sm->ip_addr_text, a->sm->web_port,
+			      UPNP_WPS_DEVICE_XML_FILE);
+	}
+
+	/* The SERVER line has three comma-separated fields:
+	 *      operating system / version
+	 *      upnp version
+	 *      software package / version
+	 * However, only the UPnP version is really required, the
+	 * others can be place holders... for security reasons
+	 * it is better to NOT provide extra information.
+	 */
+	wpabuf_put_str(msg, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
+
+	switch (a->state / UPNP_ADVERTISE_REPEAT) {
+	case 0:
+		wpabuf_printf(msg, "%s: upnp:rootdevice\r\n", NTString);
+		wpabuf_printf(msg, "USN: uuid:%s::upnp:rootdevice\r\n",
+			      uuid_string);
+		break;
+	case 1:
+		wpabuf_printf(msg, "%s: uuid:%s\r\n", NTString, uuid_string);
+		wpabuf_printf(msg, "USN: uuid:%s\r\n", uuid_string);
+		break;
+	case 2:
+		wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:device:"
+			      "WFADevice:1\r\n", NTString);
+		wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-"
+			      "org:device:WFADevice:1\r\n", uuid_string);
+		break;
+	case 3:
+		wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:service:"
+			      "WFAWLANConfig:1\r\n", NTString);
+		wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-"
+			      "org:service:WFAWLANConfig:1\r\n", uuid_string);
+		break;
+	}
+	wpabuf_put_str(msg, "\r\n");
+
+	if (a->state + 1 >= 4 * UPNP_ADVERTISE_REPEAT)
+		*islast = 1;
+
+	return msg;
+
+fail:
+	wpabuf_free(msg);
+	return NULL;
+}
+
+
+static void advertisement_state_machine_handler(void *eloop_data,
+						void *user_ctx);
+
+
+/**
+ * advertisement_state_machine_stop - Stop SSDP advertisements
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ */
+void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm)
+{
+	eloop_cancel_timeout(advertisement_state_machine_handler, NULL, sm);
+}
+
+
+static void advertisement_state_machine_handler(void *eloop_data,
+						void *user_ctx)
+{
+	struct upnp_wps_device_sm *sm = user_ctx;
+	struct advertisement_state_machine *a = &sm->advertisement;
+	struct wpabuf *msg;
+	int next_timeout_msec = 100;
+	int next_timeout_sec = 0;
+	struct sockaddr_in dest;
+	int islast = 0;
+
+	/*
+	 * Each is sent twice (in case lost) w/ 100 msec delay between;
+	 * spec says no more than 3 times.
+	 * One pair for rootdevice, one pair for uuid, and a pair each for
+	 * each of the two urns.
+	 * The entire sequence must be repeated before cache control timeout
+	 * (which  is min  1800 seconds),
+	 * recommend random portion of half of the advertised cache control age
+	 * to ensure against loss... perhaps 1800/4 + rand*1800/4 ?
+	 * Delay random interval < 100 msec prior to initial sending.
+	 * TTL of 4
+	 */
+
+	wpa_printf(MSG_MSGDUMP, "WPS UPnP: Advertisement state=%d", a->state);
+	msg = next_advertisement(a, &islast);
+	if (msg == NULL)
+		return;
+
+	os_memset(&dest, 0, sizeof(dest));
+	dest.sin_family = AF_INET;
+	dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+	dest.sin_port = htons(UPNP_MULTICAST_PORT);
+
+	if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
+		   (struct sockaddr *) &dest, sizeof(dest)) == -1) {
+		wpa_printf(MSG_ERROR, "WPS UPnP: Advertisement sendto failed:"
+			   "%d (%s)", errno, strerror(errno));
+		next_timeout_msec = 0;
+		next_timeout_sec = 10; /* ... later */
+	} else if (islast) {
+		a->state = 0; /* wrap around */
+		if (a->type == ADVERTISE_DOWN) {
+			wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_DOWN->UP");
+			a->type = ADVERTISE_UP;
+			/* do it all over again right away */
+		} else {
+			u16 r;
+			/*
+			 * Start over again after a long timeout
+			 * (see notes above)
+			 */
+			next_timeout_msec = 0;
+			os_get_random((void *) &r, sizeof(r));
+			next_timeout_sec = UPNP_CACHE_SEC / 4 +
+				(((UPNP_CACHE_SEC / 4) * r) >> 16);
+			sm->advertise_count++;
+			wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_UP (#%u); "
+				   "next in %d sec",
+				   sm->advertise_count, next_timeout_sec);
+		}
+	} else {
+		a->state++;
+	}
+
+	wpabuf_free(msg);
+
+	eloop_register_timeout(next_timeout_sec, next_timeout_msec,
+			       advertisement_state_machine_handler, NULL, sm);
+}
+
+
+/**
+ * advertisement_state_machine_start - Start SSDP advertisements
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int advertisement_state_machine_start(struct upnp_wps_device_sm *sm)
+{
+	struct advertisement_state_machine *a = &sm->advertisement;
+	int next_timeout_msec;
+
+	advertisement_state_machine_stop(sm);
+
+	/*
+	 * Start out advertising down, this automatically switches
+	 * to advertising up which signals our restart.
+	 */
+	a->type = ADVERTISE_DOWN;
+	a->state = 0;
+	a->sm = sm;
+	/* (other fields not used here) */
+
+	/* First timeout should be random interval < 100 msec */
+	next_timeout_msec = (100 * (os_random() & 0xFF)) >> 8;
+	return eloop_register_timeout(0, next_timeout_msec,
+				      advertisement_state_machine_handler,
+				      NULL, sm);
+}
+
+
+/***************************************************************************
+ * M-SEARCH replies
+ * These are very similar to the multicast advertisements, with some
+ * small changes in data content; and they are sent (UDP) to a specific
+ * unicast address instead of multicast.
+ * They are sent in response to a UDP M-SEARCH packet.
+ **************************************************************************/
+
+static void msearchreply_state_machine_handler(void *eloop_data,
+					       void *user_ctx);
+
+
+/**
+ * msearchreply_state_machine_stop - Stop M-SEARCH reply state machine
+ * @a: Selected advertisement/reply state
+ */
+void msearchreply_state_machine_stop(struct advertisement_state_machine *a)
+{
+	struct upnp_wps_device_sm *sm = a->sm;
+	wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH stop");
+	if (a->next == a) {
+		sm->msearch_replies = NULL;
+	} else {
+		if (sm->msearch_replies == a)
+			sm->msearch_replies = a->next;
+		a->next->prev = a->prev;
+		a->prev->next = a->next;
+	}
+	os_free(a);
+	sm->n_msearch_replies--;
+}
+
+
+static void msearchreply_state_machine_handler(void *eloop_data,
+					       void *user_ctx)
+{
+	struct advertisement_state_machine *a = user_ctx;
+	struct upnp_wps_device_sm *sm = a->sm;
+	struct wpabuf *msg;
+	int next_timeout_msec = 100;
+	int next_timeout_sec = 0;
+	int islast = 0;
+
+	/*
+	 * Each response is sent twice (in case lost) w/ 100 msec delay
+	 * between; spec says no more than 3 times.
+	 * One pair for rootdevice, one pair for uuid, and a pair each for
+	 * each of the two urns.
+	 */
+
+	/* TODO: should only send the requested response types */
+
+	wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply state=%d (%s:%d)",
+		   a->state, inet_ntoa(a->client.sin_addr),
+		   ntohs(a->client.sin_port));
+	msg = next_advertisement(a, &islast);
+	if (msg == NULL)
+		return;
+
+	/*
+	 * Send it on the multicast socket to avoid having to set up another
+	 * socket.
+	 */
+	if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
+		   (struct sockaddr *) &a->client, sizeof(a->client)) < 0) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply sendto "
+			   "errno %d (%s) for %s:%d",
+			   errno, strerror(errno),
+			   inet_ntoa(a->client.sin_addr),
+			   ntohs(a->client.sin_port));
+		/* Ignore error and hope for the best */
+	}
+	wpabuf_free(msg);
+	if (islast) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply done");
+		msearchreply_state_machine_stop(a);
+		return;
+	}
+	a->state++;
+
+	wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply in %d.%03d sec",
+		   next_timeout_sec, next_timeout_msec);
+	eloop_register_timeout(next_timeout_sec, next_timeout_msec,
+			       msearchreply_state_machine_handler, sm, a);
+}
+
+
+/**
+ * msearchreply_state_machine_start - Reply to M-SEARCH discovery request
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @client: Client address
+ * @mx: Maximum delay in seconds
+ *
+ * Use TTL of 4 (this was done when socket set up).
+ * A response should be given in randomized portion of min(MX,120) seconds
+ *
+ * UPnP-arch-DeviceArchitecture, 1.2.3:
+ * To be found, a device must send a UDP response to the source IP address and
+ * port that sent the request to the multicast channel. Devices respond if the
+ * ST header of the M-SEARCH request is "ssdp:all", "upnp:rootdevice", "uuid:"
+ * followed by a UUID that exactly matches one advertised by the device.
+ */
+static void msearchreply_state_machine_start(struct upnp_wps_device_sm *sm,
+					     struct sockaddr_in *client,
+					     int mx)
+{
+	struct advertisement_state_machine *a;
+	int next_timeout_sec;
+	int next_timeout_msec;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply start (%d "
+		   "outstanding)", sm->n_msearch_replies);
+	if (sm->n_msearch_replies >= MAX_MSEARCH) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Too many outstanding "
+			   "M-SEARCH replies");
+		return;
+	}
+
+	a = os_zalloc(sizeof(*a));
+	if (a == NULL)
+		return;
+	a->type = MSEARCH_REPLY;
+	a->state = 0;
+	a->sm = sm;
+	os_memcpy(&a->client, client, sizeof(client));
+	/* Wait time depending on MX value */
+	next_timeout_msec = (1000 * mx * (os_random() & 0xFF)) >> 8;
+	next_timeout_sec = next_timeout_msec / 1000;
+	next_timeout_msec = next_timeout_msec % 1000;
+	if (eloop_register_timeout(next_timeout_sec, next_timeout_msec,
+				   msearchreply_state_machine_handler, sm,
+				   a)) {
+		/* No way to recover (from malloc failure) */
+		goto fail;
+	}
+	/* Remember for future cleanup */
+	if (sm->msearch_replies) {
+		a->next = sm->msearch_replies;
+		a->prev = a->next->prev;
+		a->prev->next = a;
+		a->next->prev = a;
+	} else {
+		sm->msearch_replies = a->next = a->prev = a;
+	}
+	sm->n_msearch_replies++;
+	return;
+
+fail:
+	wpa_printf(MSG_INFO, "WPS UPnP: M-SEARCH reply failure!");
+	eloop_cancel_timeout(msearchreply_state_machine_handler, sm, a);
+	os_free(a);
+}
+
+
+/**
+ * ssdp_parse_msearch - Process a received M-SEARCH
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @client: Client address
+ * @data: NULL terminated M-SEARCH message
+ *
+ * Given that we have received a header w/ M-SEARCH, act upon it
+ *
+ * Format of M-SEARCH (case insensitive!):
+ *
+ * First line must be:
+ *      M-SEARCH * HTTP/1.1
+ * Other lines in arbitrary order:
+ *      HOST:239.255.255.250:1900
+ *      ST:<varies -- must match>
+ *      MAN:"ssdp:discover"
+ *      MX:<varies>
+ *
+ * It should be noted that when Microsoft Vista is still learning its IP
+ * address, it sends out host lines like: HOST:[FF02::C]:1900
+ */
+static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
+			       struct sockaddr_in *client, const char *data)
+{
+	const char *start = data;
+	const char *end;
+	int got_host = 0;
+	int got_st = 0, st_match = 0;
+	int got_man = 0;
+	int got_mx = 0;
+	int mx = 0;
+
+	/*
+	 * Skip first line M-SEARCH * HTTP/1.1
+	 * (perhaps we should check remainder of the line for syntax)
+	 */
+	data += line_length(data);
+
+	/* Parse remaining lines */
+	for (; *data != '\0'; data += line_length(data)) {
+		end = data + line_length_stripped(data);
+		if (token_eq(data, "host")) {
+			/* The host line indicates who the packet
+			 * is addressed to... but do we really care?
+			 * Note that Microsoft sometimes does funny
+			 * stuff with the HOST: line.
+			 */
+#if 0   /* could be */
+			data += token_length(data);
+			data += word_separation_length(data);
+			if (*data != ':')
+				goto bad;
+			data++;
+			data += word_separation_length(data);
+			/* UPNP_MULTICAST_ADDRESS */
+			if (!str_starts(data, "239.255.255.250"))
+				goto bad;
+			data += os_strlen("239.255.255.250");
+			if (*data == ':') {
+				if (!str_starts(data, ":1900"))
+					goto bad;
+			}
+#endif  /* could be */
+			got_host = 1;
+			continue;
+		} else if (token_eq(data, "st")) {
+			/* There are a number of forms; we look
+			 * for one that matches our case.
+			 */
+			got_st = 1;
+			data += token_length(data);
+			data += word_separation_length(data);
+			if (*data != ':')
+				continue;
+			data++;
+			data += word_separation_length(data);
+			if (str_starts(data, "ssdp:all")) {
+				st_match = 1;
+				continue;
+			}
+			if (str_starts(data, "upnp:rootdevice")) {
+				st_match = 1;
+				continue;
+			}
+			if (str_starts(data, "uuid:")) {
+				char uuid_string[80];
+				data += os_strlen("uuid:");
+				uuid_bin2str(sm->wps->uuid, uuid_string,
+					     sizeof(uuid_string));
+				if (str_starts(data, uuid_string))
+					st_match = 1;
+				continue;
+			}
+#if 0
+			/* FIX: should we really reply to IGD string? */
+			if (str_starts(data, "urn:schemas-upnp-org:device:"
+				       "InternetGatewayDevice:1")) {
+				st_match = 1;
+				continue;
+			}
+#endif
+			if (str_starts(data, "urn:schemas-wifialliance-org:"
+				       "service:WFAWLANConfig:1")) {
+				st_match = 1;
+				continue;
+			}
+			if (str_starts(data, "urn:schemas-wifialliance-org:"
+				       "device:WFADevice:1")) {
+				st_match = 1;
+				continue;
+			}
+			continue;
+		} else if (token_eq(data, "man")) {
+			data += token_length(data);
+			data += word_separation_length(data);
+			if (*data != ':')
+				continue;
+			data++;
+			data += word_separation_length(data);
+			if (!str_starts(data, "\"ssdp:discover\"")) {
+				wpa_printf(MSG_DEBUG, "WPS UPnP: Unexpected "
+					   "M-SEARCH man-field");
+				goto bad;
+			}
+			got_man = 1;
+			continue;
+		} else if (token_eq(data, "mx")) {
+			data += token_length(data);
+			data += word_separation_length(data);
+			if (*data != ':')
+				continue;
+			data++;
+			data += word_separation_length(data);
+			mx = atol(data);
+			got_mx = 1;
+			continue;
+		}
+		/* ignore anything else */
+	}
+	if (!got_host || !got_st || !got_man || !got_mx || mx < 0) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid M-SEARCH: %d %d %d "
+			   "%d mx=%d", got_host, got_st, got_man, got_mx, mx);
+		goto bad;
+	}
+	if (!st_match) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored M-SEARCH (no ST "
+			   "match)");
+		return;
+	}
+	if (mx > 120)
+		mx = 120; /* UPnP-arch-DeviceArchitecture, 1.2.3 */
+	msearchreply_state_machine_start(sm, client, mx);
+	return;
+
+bad:
+	wpa_printf(MSG_INFO, "WPS UPnP: Failed to parse M-SEARCH");
+	wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH data:\n%s", start);
+}
+
+
+/* Listening for (UDP) discovery (M-SEARCH) packets */
+
+/**
+ * ssdp_listener_stop - Stop SSDP listered
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ *
+ * This function stops the SSDP listerner that was started by calling
+ * ssdp_listener_start().
+ */
+void ssdp_listener_stop(struct upnp_wps_device_sm *sm)
+{
+	if (sm->ssdp_sd_registered) {
+		eloop_unregister_sock(sm->ssdp_sd, EVENT_TYPE_READ);
+		sm->ssdp_sd_registered = 0;
+	}
+
+	if (sm->ssdp_sd != -1) {
+		close(sm->ssdp_sd);
+		sm->ssdp_sd = -1;
+	}
+
+	eloop_cancel_timeout(msearchreply_state_machine_handler, sm,
+			     ELOOP_ALL_CTX);
+}
+
+
+static void ssdp_listener_handler(int sd, void *eloop_ctx, void *sock_ctx)
+{
+	struct upnp_wps_device_sm *sm = sock_ctx;
+	struct sockaddr_in addr; /* client address */
+	socklen_t addr_len;
+	int nread;
+	char buf[MULTICAST_MAX_READ], *pos;
+
+	addr_len = sizeof(addr);
+	nread = recvfrom(sm->ssdp_sd, buf, sizeof(buf) - 1, 0,
+			 (struct sockaddr *) &addr, &addr_len);
+	if (nread <= 0)
+		return;
+	buf[nread] = '\0'; /* need null termination for algorithm */
+
+	if (str_starts(buf, "NOTIFY ")) {
+		/*
+		 * Silently ignore NOTIFYs to avoid filling debug log with
+		 * unwanted messages.
+		 */
+		return;
+	}
+
+	pos = os_strchr(buf, '\n');
+	if (pos)
+		*pos = '\0';
+	wpa_printf(MSG_MSGDUMP, "WPS UPnP: Received SSDP packet from %s:%d: "
+		   "%s", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), buf);
+	if (pos)
+		*pos = '\n';
+
+	/* Parse first line */
+	if (os_strncasecmp(buf, "M-SEARCH", os_strlen("M-SEARCH")) == 0 &&
+	    !isgraph(buf[strlen("M-SEARCH")])) {
+		ssdp_parse_msearch(sm, &addr, buf);
+		return;
+	}
+
+	/* Ignore anything else */
+}
+
+
+/**
+ * ssdp_listener_start - Set up for receiving discovery (UDP) packets
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * The SSDP listerner is stopped by calling ssdp_listener_stop().
+ */
+int ssdp_listener_start(struct upnp_wps_device_sm *sm)
+{
+	int sd = -1;
+	struct sockaddr_in addr;
+	struct ip_mreq mcast_addr;
+	int on = 1;
+	/* per UPnP spec, keep IP packet time to live (TTL) small */
+	unsigned char ttl = 4;
+
+	sm->ssdp_sd = sd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sd < 0)
+		goto fail;
+	if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
+		goto fail;
+	if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
+		goto fail;
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = htonl(INADDR_ANY);
+	addr.sin_port = htons(UPNP_MULTICAST_PORT);
+	if (bind(sd, (struct sockaddr *) &addr, sizeof(addr)))
+		goto fail;
+	os_memset(&mcast_addr, 0, sizeof(mcast_addr));
+	mcast_addr.imr_interface.s_addr = htonl(INADDR_ANY);
+	mcast_addr.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+	if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+		       (char *) &mcast_addr, sizeof(mcast_addr)))
+		goto fail;
+	if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
+		       &ttl, sizeof(ttl)))
+		goto fail;
+	if (eloop_register_sock(sd, EVENT_TYPE_READ, ssdp_listener_handler,
+				NULL, sm))
+		goto fail;
+	sm->ssdp_sd_registered = 1;
+	return 0;
+
+fail:
+	/* Error */
+	wpa_printf(MSG_ERROR, "WPS UPnP: ssdp_listener_start failed");
+	ssdp_listener_stop(sm);
+	return -1;
+}
+
+
+/**
+ * add_ssdp_network - Add routing entry for SSDP
+ * @net_if: Selected network interface name
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function assures that the multicast address will be properly
+ * handled by Linux networking code (by a modification to routing tables).
+ * This must be done per network interface. It really only needs to be done
+ * once after booting up, but it does not hurt to call this more frequently
+ * "to be safe".
+ */
+int add_ssdp_network(char *net_if)
+{
+	int ret = -1;
+	int sock = -1;
+	struct rtentry rt;
+	struct sockaddr_in *sin;
+
+	if (!net_if)
+		goto fail;
+
+	os_memset(&rt, 0, sizeof(rt));
+	sock = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sock < 0)
+		goto fail;
+
+	rt.rt_dev = net_if;
+	sin = (struct sockaddr_in *) &rt.rt_dst;
+	sin->sin_family = AF_INET;
+	sin->sin_port = 0;
+	sin->sin_addr.s_addr = inet_addr(SSDP_TARGET);
+	sin = (struct sockaddr_in *) &rt.rt_genmask;
+	sin->sin_family = AF_INET;
+	sin->sin_port = 0;
+	sin->sin_addr.s_addr = inet_addr(SSDP_NETMASK);
+	rt.rt_flags = RTF_UP;
+	if (ioctl(sock, SIOCADDRT, &rt) < 0) {
+		if (errno == EPERM) {
+			wpa_printf(MSG_DEBUG, "add_ssdp_network: No "
+				   "permissions to add routing table entry");
+			/* Continue to allow testing as non-root */
+		} else if (errno != EEXIST) {
+			wpa_printf(MSG_INFO, "add_ssdp_network() ioctl errno "
+				   "%d (%s)", errno, strerror(errno));
+			goto fail;
+		}
+	}
+
+	ret = 0;
+
+fail:
+	if (sock >= 0)
+		close(sock);
+
+	return ret;
+}
+
+
+/**
+ * ssdp_open_multicast - Open socket for sending multicast SSDP messages
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int ssdp_open_multicast(struct upnp_wps_device_sm *sm)
+{
+	int sd = -1;
+	 /* per UPnP-arch-DeviceArchitecture, 1. Discovery, keep IP packet
+	  * time to live (TTL) small */
+	unsigned char ttl = 4;
+
+	sm->multicast_sd = sd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sd < 0)
+		return -1;
+
+#if 0   /* maybe ok if we sometimes block on writes */
+	if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
+		return -1;
+#endif
+
+	if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
+		       &sm->ip_addr, sizeof(sm->ip_addr)))
+		return -1;
+	if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
+		       &ttl, sizeof(ttl)))
+		return -1;
+
+#if 0   /* not needed, because we don't receive using multicast_sd */
+	{
+		struct ip_mreq mreq;
+		mreq.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+		mreq.imr_interface.s_addr = sm->ip_addr;
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Multicast addr 0x%x if addr "
+			   "0x%x",
+			   mreq.imr_multiaddr.s_addr,
+			   mreq.imr_interface.s_addr);
+		if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
+				sizeof(mreq))) {
+			wpa_printf(MSG_ERROR,
+				   "WPS UPnP: setsockopt "
+				   "IP_ADD_MEMBERSHIP errno %d (%s)",
+				   errno, strerror(errno));
+			return -1;
+		}
+	}
+#endif  /* not needed */
+
+	/*
+	 * TODO: What about IP_MULTICAST_LOOP? It seems to be on by default?
+	 * which aids debugging I suppose but isn't really necessary?
+	 */
+
+	return 0;
+}

Added: wpasupplicant/branches/upstream/current/src/wps/wps_upnp_web.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_upnp_web.c?rev=1324&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_upnp_web.c (added)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_upnp_web.c Sun Feb 15 19:38:55 2009
@@ -1,0 +1,1964 @@
+/*
+ * UPnP WPS Device - Web connections
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j at w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+#include <fcntl.h>
+
+#include "common.h"
+#include "base64.h"
+#include "eloop.h"
+#include "uuid.h"
+#include "httpread.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+/***************************************************************************
+ * Web connections (we serve pages of info about ourselves, handle
+ * requests, etc. etc.).
+ **************************************************************************/
+
+#define WEB_CONNECTION_TIMEOUT_SEC 30   /* Drop web connection after t.o. */
+#define WEB_CONNECTION_MAX_READ 8000    /* Max we'll read for TCP request */
+#define MAX_WEB_CONNECTIONS 10          /* max simultaneous web connects */
+
+
+static const char *urn_wfawlanconfig =
+	"urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
+static const char *http_server_hdr =
+	"Server: unspecified, UPnP/1.0, unspecified\r\n";
+static const char *http_connection_close =
+	"Connection: close\r\n";
+
+/*
+ * Incoming web connections are recorded in this struct.
+ * A web connection is a TCP connection to us, the server;
+ * it is called a "web connection" because we use http and serve
+ * data that looks like web pages.
+ * State information is need to track the connection until we figure
+ * out what they want and what we want to do about it.
+ */
+struct web_connection {
+	/* double linked list */
+	struct web_connection *next;
+	struct web_connection *prev;
+	struct upnp_wps_device_sm *sm; /* parent */
+	int sd; /* socket to read from */
+	struct sockaddr_in cli_addr;
+	int sd_registered; /* nonzero if we must cancel registration */
+	struct httpread *hread; /* state machine for reading socket */
+	int n_rcvd_data; /* how much data read so far */
+	int done; /* internal flag, set when we've finished */
+};
+
+
+/*
+ * XML parsing and formatting
+ *
+ * XML is a markup language based on unicode; usually (and in our case,
+ * always!) based on utf-8. utf-8 uses a variable number of bytes per
+ * character. utf-8 has the advantage that all non-ASCII unicode characters are
+ * represented by sequences of non-ascii (high bit set) bytes, whereas ASCII
+ * characters are single ascii bytes, thus we can use typical text processing.
+ *
+ * (One other interesting thing about utf-8 is that it is possible to look at
+ * any random byte and determine if it is the first byte of a character as
+ * versus a continuation byte).
+ *
+ * The base syntax of XML uses a few ASCII punctionation characters; any
+ * characters that would appear in the payload data are rewritten using
+ * sequences, e.g., &amp; for ampersand(&) and &lt for left angle bracket (<).
+ * Five such escapes total (more can be defined but that does not apply to our
+ * case). Thus we can safely parse for angle brackets etc.
+ *
+ * XML describes tree structures of tagged data, with each element beginning
+ * with an opening tag <label> and ending with a closing tag </label> with
+ * matching label. (There is also a self-closing tag <label/> which is supposed
+ * to be equivalent to <label></label>, i.e., no payload, but we are unlikely
+ * to see it for our purpose).
+ *
+ * Actually the opening tags are a little more complicated because they can
+ * contain "attributes" after the label (delimited by ascii space or tab chars)
+ * of the form attribute_label="value" or attribute_label='value'; as it turns
+ * out we do not have to read any of these attributes, just ignore them.
+ *
+ * Labels are any sequence of chars other than space, tab, right angle bracket
+ * (and ?), but may have an inner structure of <namespace><colon><plain_label>.
+ * As it turns out, we can ignore the namespaces, in fact we can ignore the
+ * entire tree hierarchy, because the plain labels we are looking for will be
+ * unique (not in general, but for this application). We do however have to be
+ * careful to skip over the namespaces.
+ *
+ * In generating XML we have to be more careful, but that is easy because
+ * everything we do is pretty canned. The only real care to take is to escape
+ * any special chars in our payload.
+ */
+
+/**
+ * xml_next_tag - Advance to next tag
+ * @in: Input
+ * @out: OUT: start of tag just after '<'
+ * @out_tagname: OUT: start of name of tag, skipping namespace
+ * @end: OUT: one after tag
+ * Returns: 0 on success, 1 on failure
+ *
+ * A tag has form:
+ *     <left angle bracket><...><right angle bracket>
+ * Within the angle brackets, there is an optional leading forward slash (which
+ * makes the tag an ending tag), then an optional leading label (followed by
+ * colon) and then the tag name itself.
+ *
+ * Note that angle brackets present in the original data must have been encoded
+ * as &lt; and &gt; so they will not trouble us.
+ */
+static int xml_next_tag(char *in, char **out, char **out_tagname,
+			char **end)
+{
+	while (*in && *in != '<')
+		in++;
+	if (*in != '<')
+		return 1;
+	*out = ++in;
+	if (*in == '/')
+		in++;
+	*out_tagname = in; /* maybe */
+	while (isalnum(*in) || *in == '-')
+		in++;
+	if (*in == ':')
+		*out_tagname = ++in;
+	while (*in && *in != '>')
+		in++;
+	if (*in != '>')
+		return 1;
+	*end = ++in;
+	return 0;
+}
+
+
+/* xml_data_encode -- format data for xml file, escaping special characters.
+ *
+ * Note that we assume we are using utf8 both as input and as output!
+ * In utf8, characters may be classed as follows:
+ *     0xxxxxxx(2) -- 1 byte ascii char
+ *     11xxxxxx(2) -- 1st byte of multi-byte char w/ unicode value >= 0x80
+ *         110xxxxx(2) -- 1st byte of 2 byte sequence (5 payload bits here)
+ *         1110xxxx(2) -- 1st byte of 3 byte sequence (4 payload bits here)
+ *         11110xxx(2) -- 1st byte of 4 byte sequence (3 payload bits here)
+ *      10xxxxxx(2) -- extension byte (6 payload bits per byte)
+ *      Some values implied by the above are however illegal because they
+ *      do not represent unicode chars or are not the shortest encoding.
+ * Actually, we can almost entirely ignore the above and just do
+ * text processing same as for ascii text.
+ *
+ * XML is written with arbitrary unicode characters, except that five
+ * characters have special meaning and so must be escaped where they
+ * appear in payload data... which we do here.
+ */
+static void xml_data_encode(struct wpabuf *buf, const char *data, int len)
+{
+	int i;
+	for (i = 0; i < len; i++) {
+		u8 c = ((u8 *) data)[i];
+		if (c == '<') {
+			wpabuf_put_str(buf, "&lt;");
+			continue;
+		}
+		if (c == '>') {
+			wpabuf_put_str(buf, "&gt;");
+			continue;
+		}
+		if (c == '&') {
+			wpabuf_put_str(buf, "&amp;");
+			continue;
+		}
+		if (c == '\'') {
+			wpabuf_put_str(buf, "&apos;");
+			continue;
+		}
+		if (c == '"') {
+			wpabuf_put_str(buf, "&quot;");
+			continue;
+		}
+		/*
+		 * We could try to represent control characters using the
+		 * sequence: &#x; where x is replaced by a hex numeral, but not
+		 * clear why we would do this.
+		 */
+		wpabuf_put_u8(buf, c);
+	}
+}
+
+
+/* xml_add_tagged_data -- format tagged data as a new xml line.
+ *
+ * tag must not have any special chars.
+ * data may have special chars, which are escaped.
+ */
+static void xml_add_tagged_data(struct wpabuf *buf, const char *tag,
+				const char *data)
+{
+	wpabuf_printf(buf, "<%s>", tag);
+	xml_data_encode(buf, data, os_strlen(data));
+	wpabuf_printf(buf, "</%s>\n", tag);
+}
+
+
+/* A POST body looks something like (per upnp spec):
+ * <?xml version="1.0"?>
+ * <s:Envelope
+ *     xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
+ *     s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ *   <s:Body>
+ *     <u:actionName xmlns:u="urn:schemas-upnp-org:service:serviceType:v">
+ *       <argumentName>in arg value</argumentName>
+ *       other in args and their values go here, if any
+ *     </u:actionName>
+ *   </s:Body>
+ * </s:Envelope>
+ *
+ * where :
+ *      s: might be some other namespace name followed by colon
+ *      u: might be some other namespace name followed by colon
+ *      actionName will be replaced according to action requested
+ *      schema following actionName will be WFA scheme instead
+ *      argumentName will be actual argument name
+ *      (in arg value) will be actual argument value
+ */
+static int
+upnp_get_first_document_item(char *doc, const char *item, char **value)
+{
+	const char *match = item;
+	int match_len = os_strlen(item);
+	char *tag;
+	char *tagname;
+	char *end;
+
+	*value = NULL;          /* default, bad */
+
+	/*
+	 * This is crude: ignore any possible tag name conflicts and go right
+	 * to the first tag of this name. This should be ok for the limited
+	 * domain of UPnP messages.
+	 */
+	for (;;) {
+		if (xml_next_tag(doc, &tag, &tagname, &end))
+			return 1;
+		doc = end;
+		if (!os_strncasecmp(tagname, match, match_len) &&
+		    *tag != '/' &&
+		    (tagname[match_len] == '>' ||
+		     !isgraph(tagname[match_len]))) {
+			break;
+		}
+	}
+	end = doc;
+	while (*end && *end != '<')
+		end++;
+	*value = os_zalloc(1 + (end - doc));
+	if (*value == NULL)
+		return 1;
+	os_memcpy(*value, doc, end - doc);
+	return 0;
+}
+
+
+/*
+ * "Files" that we serve via HTTP. The format of these files is given by
+ * WFA WPS specifications. Extra white space has been removed to save space.
+ */
+
+static const char wps_scpd_xml[] =
+"<?xml version=\"1.0\"?>\n"
+"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">\n"
+"<specVersion><major>1</major><minor>0</minor></specVersion>\n"
+"<actionList>\n"
+"<action>\n"
+"<name>GetDeviceInfo</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewDeviceInfo</name>\n"
+"<direction>out</direction>\n"
+"<relatedStateVariable>DeviceInfo</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>PutMessage</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewInMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>InMessage</relatedStateVariable>\n"
+"</argument>\n"
+"<argument>\n"
+"<name>NewOutMessage</name>\n"
+"<direction>out</direction>\n"
+"<relatedStateVariable>OutMessage</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>GetAPSettings</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>Message</relatedStateVariable>\n"
+"</argument>\n"
+"<argument>\n"
+"<name>NewAPSettings</name>\n"
+"<direction>out</direction>\n"
+"<relatedStateVariable>APSettings</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>SetAPSettings</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>APSettings</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>APSettings</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>DelAPSettings</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewAPSettings</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>APSettings</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>GetSTASettings</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>Message</relatedStateVariable>\n"
+"</argument>\n"
+"<argument>\n"
+"<name>NewSTASettings</name>\n"
+"<direction>out</direction>\n"
+"<relatedStateVariable>STASettings</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>SetSTASettings</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewSTASettings</name>\n"
+"<direction>out</direction>\n"
+"<relatedStateVariable>STASettings</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>DelSTASettings</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewSTASettings</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>STASettings</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>PutWLANResponse</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>Message</relatedStateVariable>\n"
+"</argument>\n"
+"<argument>\n"
+"<name>NewWLANEventType</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>WLANEventType</relatedStateVariable>\n"
+"</argument>\n"
+"<argument>\n"
+"<name>NewWLANEventMAC</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>WLANEventMAC</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>SetSelectedRegistrar</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>Message</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>RebootAP</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewAPSettings</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>APSettings</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>ResetAP</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>Message</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>RebootSTA</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewSTASettings</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>APSettings</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>ResetSTA</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>Message</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"</actionList>\n"
+"<serviceStateTable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>Message</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>InMessage</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>OutMessage</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>DeviceInfo</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>APSettings</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"yes\">\n"
+"<name>APStatus</name>\n"
+"<dataType>ui1</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>STASettings</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"yes\">\n"
+"<name>STAStatus</name>\n"
+"<dataType>ui1</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"yes\">\n"
+"<name>WLANEvent</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>WLANEventType</name>\n"
+"<dataType>ui1</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>WLANEventMAC</name>\n"
+"<dataType>string</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>WLANResponse</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"</serviceStateTable>\n"
+"</scpd>\n"
+;
+
+
+static const char *wps_device_xml_prefix =
+	"<?xml version=\"1.0\"?>\n"
+	"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
+	"<specVersion>\n"
+	"<major>1</major>\n"
+	"<minor>0</minor>\n"
+	"</specVersion>\n"
+	"<device>\n"
+	"<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1"
+	"</deviceType>\n";
+
+static const char *wps_device_xml_postfix =
+	"<serviceList>\n"
+	"<service>\n"
+	"<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1"
+	"</serviceType>\n"
+	"<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>"
+	"\n"
+	"<SCPDURL>" UPNP_WPS_SCPD_XML_FILE "</SCPDURL>\n"
+	"<controlURL>" UPNP_WPS_DEVICE_CONTROL_FILE "</controlURL>\n"
+	"<eventSubURL>" UPNP_WPS_DEVICE_EVENT_FILE "</eventSubURL>\n"
+	"</service>\n"
+	"</serviceList>\n"
+	"</device>\n"
+	"</root>\n";
+
+
+/* format_wps_device_xml -- produce content of "file" wps_device.xml
+ * (UPNP_WPS_DEVICE_XML_FILE)
+ */
+static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
+				  struct wpabuf *buf)
+{
+	const char *s;
+	char uuid_string[80];
+
+	wpabuf_put_str(buf, wps_device_xml_prefix);
+
+	/*
+	 * Add required fields with default values if not configured. Add
+	 * optional and recommended fields only if configured.
+	 */
+	s = sm->wps->friendly_name;
+	s = ((s && *s) ? s : "WPS Access Point");
+	xml_add_tagged_data(buf, "friendlyName", s);
+
+	s = sm->wps->dev.manufacturer;
+	s = ((s && *s) ? s : "");
+	xml_add_tagged_data(buf, "manufacturer", s);
+
+	if (sm->wps->manufacturer_url)
+		xml_add_tagged_data(buf, "manufacturerURL",
+				    sm->wps->manufacturer_url);
+
+	if (sm->wps->model_description)
+		xml_add_tagged_data(buf, "modelDescription",
+				    sm->wps->model_description);
+
+	s = sm->wps->dev.model_name;
+	s = ((s && *s) ? s : "");
+	xml_add_tagged_data(buf, "modelName", s);
+
+	if (sm->wps->dev.model_number)
+		xml_add_tagged_data(buf, "modelNumber",
+				    sm->wps->dev.model_number);
+
+	if (sm->wps->model_url)
+		xml_add_tagged_data(buf, "modelURL", sm->wps->model_url);
+
+	if (sm->wps->dev.serial_number)
+		xml_add_tagged_data(buf, "serialNumber",
+				    sm->wps->dev.serial_number);
+
+	uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string));
+	s = uuid_string;
+	/* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
+	 * easily...
+	 */
+	wpabuf_put_str(buf, "<UDN>uuid:");
+	xml_data_encode(buf, s, os_strlen(s));
+	wpabuf_put_str(buf, "</UDN>\n");
+
+	if (sm->wps->upc)
+		xml_add_tagged_data(buf, "UPC", sm->wps->upc);
+
+	wpabuf_put_str(buf, wps_device_xml_postfix);
+}
+
+
+void web_connection_stop(struct web_connection *c)
+{
+	struct upnp_wps_device_sm *sm = c->sm;
+
+	httpread_destroy(c->hread);
+	c->hread = NULL;
+	close(c->sd);
+	c->sd = -1;
+	if (c->next == c) {
+		sm->web_connections = NULL;
+	} else {
+		if (sm->web_connections == c)
+			sm->web_connections = c->next;
+		c->next->prev = c->prev;
+		c->prev->next = c->next;
+	}
+	os_free(c);
+	sm->n_web_connections--;
+}
+
+
+static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
+{
+	wpabuf_put_str(buf, "HTTP/1.1 ");
+	switch (code) {
+	case HTTP_OK:
+		wpabuf_put_str(buf, "200 OK\r\n");
+		break;
+	case HTTP_BAD_REQUEST:
+		wpabuf_put_str(buf, "400 Bad request\r\n");
+		break;
+	case HTTP_PRECONDITION_FAILED:
+		wpabuf_put_str(buf, "412 Precondition failed\r\n");
+		break;
+	case HTTP_UNIMPLEMENTED:
+		wpabuf_put_str(buf, "501 Unimplemented\r\n");
+		break;
+	case HTTP_INTERNAL_SERVER_ERROR:
+	default:
+		wpabuf_put_str(buf, "500 Internal server error\r\n");
+		break;
+	}
+}
+
+
+static void http_put_date(struct wpabuf *buf)
+{
+	wpabuf_put_str(buf, "Date: ");
+	format_date(buf);
+	wpabuf_put_str(buf, "\r\n");
+}
+
+
+static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
+{
+	http_put_reply_code(buf, code);
+	wpabuf_put_str(buf, http_server_hdr);
+	wpabuf_put_str(buf, http_connection_close);
+	wpabuf_put_str(buf, "Content-Length: 0\r\n"
+		       "\r\n");
+}
+
+
+/* Given that we have received a header w/ GET, act upon it
+ *
+ * Format of GET (case-insensitive):
+ *
+ * First line must be:
+ *      GET /<file> HTTP/1.1
+ * Since we don't do anything fancy we just ignore other lines.
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Connection: close
+ * Content-Type: text/xml
+ * Date: <rfc1123-date>
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_get(struct web_connection *c, char *filename)
+{
+	struct upnp_wps_device_sm *sm = c->sm;
+	struct wpabuf *buf; /* output buffer, allocated */
+	char *put_length_here;
+	char *body_start;
+	enum {
+		GET_DEVICE_XML_FILE,
+		GET_SCPD_XML_FILE
+	} req;
+	size_t extra_len = 0;
+	int body_length;
+	char len_buf[10];
+
+	/*
+	 * It is not required that filenames be case insensitive but it is
+	 * allowed and cannot hurt here.
+	 */
+	if (filename == NULL)
+		filename = "(null)"; /* just in case */
+	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
+		req = GET_DEVICE_XML_FILE;
+		extra_len = 3000;
+		if (sm->wps->friendly_name)
+			extra_len += os_strlen(sm->wps->friendly_name);
+		if (sm->wps->manufacturer_url)
+			extra_len += os_strlen(sm->wps->manufacturer_url);
+		if (sm->wps->model_description)
+			extra_len += os_strlen(sm->wps->model_description);
+		if (sm->wps->model_url)
+			extra_len += os_strlen(sm->wps->model_url);
+		if (sm->wps->upc)
+			extra_len += os_strlen(sm->wps->upc);
+	} else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
+		req = GET_SCPD_XML_FILE;
+		extra_len = os_strlen(wps_scpd_xml);
+	} else {
+		/* File not found */
+		wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s",
+			   filename);
+		buf = wpabuf_alloc(200);
+		if (buf == NULL)
+			return;
+		wpabuf_put_str(buf,
+			       "HTTP/1.1 404 Not Found\r\n"
+			       "Connection: close\r\n");
+
+		http_put_date(buf);
+
+		/* terminating empty line */
+		wpabuf_put_str(buf, "\r\n");
+
+		goto send_buf;
+	}
+
+	buf = wpabuf_alloc(1000 + extra_len);
+	if (buf == NULL)
+		return;
+
+	wpabuf_put_str(buf,
+		       "HTTP/1.1 200 OK\r\n"
+		       "Content-Type: text/xml; charset=\"utf-8\"\r\n");
+	wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
+	wpabuf_put_str(buf, "Connection: close\r\n");
+	wpabuf_put_str(buf, "Content-Length: ");
+	/*
+	 * We will paste the length in later, leaving some extra whitespace.
+	 * HTTP code is supposed to be tolerant of extra whitespace.
+	 */
+	put_length_here = wpabuf_put(buf, 0);
+	wpabuf_put_str(buf, "        \r\n");
+
+	http_put_date(buf);
+
+	/* terminating empty line */
+	wpabuf_put_str(buf, "\r\n");
+
+	body_start = wpabuf_put(buf, 0);
+
+	switch (req) {
+	case GET_DEVICE_XML_FILE:
+		format_wps_device_xml(sm, buf);
+		break;
+	case GET_SCPD_XML_FILE:
+		wpabuf_put_str(buf, wps_scpd_xml);
+		break;
+	}
+
+	/* Now patch in the content length at the end */
+	body_length = (char *) wpabuf_put(buf, 0) - body_start;
+	os_snprintf(len_buf, 10, "%d", body_length);
+	os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
+
+send_buf:
+	send_wpabuf(c->sd, buf);
+	wpabuf_free(buf);
+}
+
+
+static struct wpabuf * web_get_item(char *data, const char *name,
+				    enum http_reply_code *ret)
+{
+	char *msg;
+	struct wpabuf *buf;
+	unsigned char *decoded;
+	size_t len;
+
+	if (upnp_get_first_document_item(data, name, &msg)) {
+		*ret = UPNP_ARG_VALUE_INVALID;
+		return NULL;
+	}
+
+	decoded = base64_decode((unsigned char *) msg, os_strlen(msg), &len);
+	os_free(msg);
+	if (decoded == NULL) {
+		*ret = UPNP_OUT_OF_MEMORY;
+		return NULL;
+	}
+
+	buf = wpabuf_alloc_ext_data(decoded, len);
+	if (buf == NULL) {
+		os_free(decoded);
+		*ret = UPNP_OUT_OF_MEMORY;
+		return NULL;
+	}
+	return buf;
+}
+
+
+static enum http_reply_code
+web_process_get_device_info(struct upnp_wps_device_sm *sm,
+			    struct wpabuf **reply, const char **replyname)
+{
+	static const char *name = "NewDeviceInfo";
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
+	if (sm->ctx->rx_req_get_device_info == NULL)
+		return HTTP_INTERNAL_SERVER_ERROR;
+	*reply = sm->ctx->rx_req_get_device_info(sm->priv, &sm->peer);
+	if (*reply == NULL) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
+		return HTTP_INTERNAL_SERVER_ERROR;
+	}
+	*replyname = name;
+	return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
+			struct wpabuf **reply, const char **replyname)
+{
+	struct wpabuf *msg;
+	static const char *name = "NewOutMessage";
+	enum http_reply_code ret;
+
+	/*
+	 * PutMessage is used by external UPnP-based Registrar to perform WPS
+	 * operation with the access point itself; as compared with
+	 * PutWLANResponse which is for proxying.
+	 */
+	wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
+	if (sm->ctx->rx_req_put_message == NULL)
+		return HTTP_INTERNAL_SERVER_ERROR;
+	msg = web_get_item(data, "NewInMessage", &ret);
+	if (msg == NULL)
+		return ret;
+	*reply = sm->ctx->rx_req_put_message(sm->priv, &sm->peer, msg);
+	wpabuf_free(msg);
+	if (*reply == NULL)
+		return HTTP_INTERNAL_SERVER_ERROR;
+	*replyname = name;
+	return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_get_ap_settings(struct upnp_wps_device_sm *sm, char *data,
+			    struct wpabuf **reply, const char **replyname)
+{
+	struct wpabuf *msg;
+	static const char *name = "NewAPSettings";
+	enum http_reply_code ret;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: GetAPSettings");
+	if (sm->ctx->rx_req_get_ap_settings == NULL)
+		return HTTP_INTERNAL_SERVER_ERROR;
+	msg = web_get_item(data, "NewMessage", &ret);
+	if (msg == NULL)
+		return ret;
+	*reply = sm->ctx->rx_req_get_ap_settings(sm->priv, msg);
+	wpabuf_free(msg);
+	if (*reply == NULL)
+		return HTTP_INTERNAL_SERVER_ERROR;
+	*replyname = name;
+	return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_set_ap_settings(struct upnp_wps_device_sm *sm, char *data,
+			    struct wpabuf **reply, const char **replyname)
+{
+	struct wpabuf *msg;
+	enum http_reply_code ret;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: SetAPSettings");
+	msg = web_get_item(data, "NewAPSettings", &ret);
+	if (msg == NULL)
+		return ret;
+	if (!sm->ctx->rx_req_set_ap_settings ||
+	    sm->ctx->rx_req_set_ap_settings(sm->priv, msg)) {
+		wpabuf_free(msg);
+		return HTTP_INTERNAL_SERVER_ERROR;
+	}
+	wpabuf_free(msg);
+	*replyname = NULL;
+	*reply = NULL;
+	return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_del_ap_settings(struct upnp_wps_device_sm *sm, char *data,
+			    struct wpabuf **reply, const char **replyname)
+{
+	struct wpabuf *msg;
+	enum http_reply_code ret;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: DelAPSettings");
+	msg = web_get_item(data, "NewAPSettings", &ret);
+	if (msg == NULL)
+		return ret;
+	if (!sm->ctx->rx_req_del_ap_settings ||
+	    sm->ctx->rx_req_del_ap_settings(sm->priv, msg)) {
+		wpabuf_free(msg);
+		return HTTP_INTERNAL_SERVER_ERROR;
+	}
+	wpabuf_free(msg);
+	*replyname = NULL;
+	*reply = NULL;
+	return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_get_sta_settings(struct upnp_wps_device_sm *sm, char *data,
+			     struct wpabuf **reply, const char **replyname)
+{
+	struct wpabuf *msg;
+	static const char *name = "NewSTASettings";
+	enum http_reply_code ret;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: GetSTASettings");
+	if (sm->ctx->rx_req_get_sta_settings == NULL)
+		return HTTP_INTERNAL_SERVER_ERROR;
+	msg = web_get_item(data, "NewMessage", &ret);
+	if (msg == NULL)
+		return ret;
+	*reply = sm->ctx->rx_req_get_sta_settings(sm->priv, msg);
+	wpabuf_free(msg);
+	if (*reply == NULL)
+		return HTTP_INTERNAL_SERVER_ERROR;
+	*replyname = name;
+	return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_set_sta_settings(struct upnp_wps_device_sm *sm, char *data,
+			     struct wpabuf **reply, const char **replyname)
+{
+	struct wpabuf *msg;
+	enum http_reply_code ret;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: SetSTASettings");
+	msg = web_get_item(data, "NewSTASettings", &ret);
+	if (msg == NULL)
+		return ret;
+	if (!sm->ctx->rx_req_set_sta_settings ||
+	    sm->ctx->rx_req_set_sta_settings(sm->priv, msg)) {
+		wpabuf_free(msg);
+		return HTTP_INTERNAL_SERVER_ERROR;
+	}
+	wpabuf_free(msg);
+	*replyname = NULL;
+	*reply = NULL;
+	return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_del_sta_settings(struct upnp_wps_device_sm *sm, char *data,
+			     struct wpabuf **reply, const char **replyname)
+{
+	struct wpabuf *msg;
+	enum http_reply_code ret;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: DelSTASettings");
+	msg = web_get_item(data, "NewSTASettings", &ret);
+	if (msg == NULL)
+		return ret;
+	if (!sm->ctx->rx_req_del_sta_settings ||
+	    sm->ctx->rx_req_del_sta_settings(sm->priv, msg)) {
+		wpabuf_free(msg);
+		return HTTP_INTERNAL_SERVER_ERROR;
+	}
+	wpabuf_free(msg);
+	*replyname = NULL;
+	*reply = NULL;
+	return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
+			      struct wpabuf **reply, const char **replyname)
+{
+	struct wpabuf *msg;
+	enum http_reply_code ret;
+	u8 macaddr[ETH_ALEN];
+	int ev_type;
+	int type;
+	char *val;
+
+	/*
+	 * External UPnP-based Registrar is passing us a message to be proxied
+	 * over to a Wi-Fi -based client of ours.
+	 */
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
+	msg = web_get_item(data, "NewMessage", &ret);
+	if (msg == NULL)
+		return ret;
+	if (upnp_get_first_document_item(data, "NewWLANEventType", &val)) {
+		wpabuf_free(msg);
+		return UPNP_ARG_VALUE_INVALID;
+	}
+	ev_type = atol(val);
+	os_free(val);
+	val = NULL;
+	if (upnp_get_first_document_item(data, "NewWLANEventMAC", &val) ||
+	    hwaddr_aton(val, macaddr)) {
+		wpabuf_free(msg);
+		os_free(val);
+		return UPNP_ARG_VALUE_INVALID;
+	}
+	os_free(val);
+	if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
+		struct wps_parse_attr attr;
+		if (wps_parse_msg(msg, &attr) < 0 ||
+		    attr.msg_type == NULL)
+			type = -1;
+		else
+			type = *attr.msg_type;
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
+	} else
+		type = -1;
+	if (!sm->ctx->rx_req_put_wlan_response ||
+	    sm->ctx->rx_req_put_wlan_response(sm->priv, ev_type, macaddr, msg,
+					      type)) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
+			   "rx_req_put_wlan_response");
+		wpabuf_free(msg);
+		return HTTP_INTERNAL_SERVER_ERROR;
+	}
+	wpabuf_free(msg);
+	*replyname = NULL;
+	*reply = NULL;
+	return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_set_selected_registrar(struct upnp_wps_device_sm *sm, char *data,
+				   struct wpabuf **reply,
+				   const char **replyname)
+{
+	struct wpabuf *msg;
+	enum http_reply_code ret;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
+	msg = web_get_item(data, "NewMessage", &ret);
+	if (msg == NULL)
+		return ret;
+	if (!sm->ctx->rx_req_set_selected_registrar ||
+	    sm->ctx->rx_req_set_selected_registrar(sm->priv, msg)) {
+		wpabuf_free(msg);
+		return HTTP_INTERNAL_SERVER_ERROR;
+	}
+	wpabuf_free(msg);
+	*replyname = NULL;
+	*reply = NULL;
+	return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_reboot_ap(struct upnp_wps_device_sm *sm, char *data,
+		      struct wpabuf **reply, const char **replyname)
+{
+	struct wpabuf *msg;
+	enum http_reply_code ret;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: RebootAP");
+	msg = web_get_item(data, "NewAPSettings", &ret);
+	if (msg == NULL)
+		return ret;
+	if (!sm->ctx->rx_req_reboot_ap ||
+	    sm->ctx->rx_req_reboot_ap(sm->priv, msg)) {
+		wpabuf_free(msg);
+		return HTTP_INTERNAL_SERVER_ERROR;
+	}
+	wpabuf_free(msg);
+	*replyname = NULL;
+	*reply = NULL;
+	return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_reset_ap(struct upnp_wps_device_sm *sm, char *data,
+		     struct wpabuf **reply, const char **replyname)
+{
+	struct wpabuf *msg;
+	enum http_reply_code ret;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: ResetAP");
+	msg = web_get_item(data, "NewMessage", &ret);
+	if (msg == NULL)
+		return ret;
+	if (!sm->ctx->rx_req_reset_ap ||
+	    sm->ctx->rx_req_reset_ap(sm->priv, msg)) {
+		wpabuf_free(msg);
+		return HTTP_INTERNAL_SERVER_ERROR;
+	}
+	wpabuf_free(msg);
+	*replyname = NULL;
+	*reply = NULL;
+	return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_reboot_sta(struct upnp_wps_device_sm *sm, char *data,
+		       struct wpabuf **reply, const char **replyname)
+{
+	struct wpabuf *msg;
+	enum http_reply_code ret;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: RebootSTA");
+	msg = web_get_item(data, "NewSTASettings", &ret);
+	if (msg == NULL)
+		return ret;
+	if (!sm->ctx->rx_req_reboot_sta ||
+	    sm->ctx->rx_req_reboot_sta(sm->priv, msg)) {
+		wpabuf_free(msg);
+		return HTTP_INTERNAL_SERVER_ERROR;
+	}
+	wpabuf_free(msg);
+	*replyname = NULL;
+	*reply = NULL;
+	return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_reset_sta(struct upnp_wps_device_sm *sm, char *data,
+		      struct wpabuf **reply, const char **replyname)
+{
+	struct wpabuf *msg;
+	enum http_reply_code ret;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: ResetSTA");
+	msg = web_get_item(data, "NewMessage", &ret);
+	if (msg == NULL)
+		return ret;
+	if (!sm->ctx->rx_req_reset_sta ||
+	    sm->ctx->rx_req_reset_sta(sm->priv, msg)) {
+		wpabuf_free(msg);
+		return HTTP_INTERNAL_SERVER_ERROR;
+	}
+	wpabuf_free(msg);
+	*replyname = NULL;
+	*reply = NULL;
+	return HTTP_OK;
+}
+
+
+static const char *soap_prefix =
+	"<?xml version=\"1.0\"?>\n"
+	"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+	"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
+	"<s:Body>\n";
+static const char *soap_postfix =
+	"</s:Body>\n</s:Envelope>\n";
+
+static const char *soap_error_prefix =
+	"<s:Fault>\n"
+	"<faultcode>s:Client</faultcode>\n"
+	"<faultstring>UPnPError</faultstring>\n"
+	"<detail>\n"
+	"<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n";
+static const char *soap_error_postfix =
+	"<errorDescription>Error</errorDescription>\n"
+	"</UPnPError>\n"
+	"</detail>\n"
+	"</s:Fault>\n";
+
+static void web_connection_send_reply(struct web_connection *c,
+				      enum http_reply_code ret,
+				      const char *action, int action_len,
+				      const struct wpabuf *reply,
+				      const char *replyname)
+{
+	struct wpabuf *buf;
+	char *replydata;
+	char *put_length_here = NULL;
+	char *body_start = NULL;
+
+	if (reply) {
+		size_t len;
+		replydata = (char *) base64_encode(wpabuf_head(reply),
+						   wpabuf_len(reply), &len);
+	} else
+		replydata = NULL;
+
+	/* Parameters of the response:
+	 *      action(action_len) -- action we are responding to
+	 *      replyname -- a name we need for the reply
+	 *      replydata -- NULL or null-terminated string
+	 */
+	buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
+			   (action_len > 0 ? action_len * 2 : 0));
+	if (buf == NULL) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to "
+			   "POST");
+		wpabuf_free(buf);
+		os_free(replydata);
+		return;
+	}
+
+	/*
+	 * Assuming we will be successful, put in the output header first.
+	 * Note: we do not keep connections alive (and httpread does
+	 * not support it)... therefore we must have Connection: close.
+	 */
+	if (ret == HTTP_OK) {
+		wpabuf_put_str(buf,
+			       "HTTP/1.1 200 OK\r\n"
+			       "Content-Type: text/xml; "
+			       "charset=\"utf-8\"\r\n");
+	} else {
+		wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
+	}
+	wpabuf_put_str(buf, http_connection_close);
+
+	wpabuf_put_str(buf, "Content-Length: ");
+	/*
+	 * We will paste the length in later, leaving some extra whitespace.
+	 * HTTP code is supposed to be tolerant of extra whitespace.
+	 */
+	put_length_here = wpabuf_put(buf, 0);
+	wpabuf_put_str(buf, "        \r\n");
+
+	http_put_date(buf);
+
+	/* terminating empty line */
+	wpabuf_put_str(buf, "\r\n");
+
+	body_start = wpabuf_put(buf, 0);
+
+	if (ret == HTTP_OK) {
+		wpabuf_put_str(buf, soap_prefix);
+		wpabuf_put_str(buf, "<u:");
+		wpabuf_put_data(buf, action, action_len);
+		wpabuf_put_str(buf, "Response xmlns:u=\"");
+		wpabuf_put_str(buf, urn_wfawlanconfig);
+		wpabuf_put_str(buf, "\">\n");
+		if (replydata && replyname) {
+			/* TODO: might possibly need to escape part of reply
+			 * data? ...
+			 * probably not, unlikely to have ampersand(&) or left
+			 * angle bracket (<) in it...
+			 */
+			wpabuf_printf(buf, "<%s>", replyname);
+			wpabuf_put_str(buf, replydata);
+			wpabuf_printf(buf, "</%s>\n", replyname);
+		}
+		wpabuf_put_str(buf, "</u:");
+		wpabuf_put_data(buf, action, action_len);
+		wpabuf_put_str(buf, "Response>\n");
+		wpabuf_put_str(buf, soap_postfix);
+	} else {
+		/* Error case */
+		wpabuf_put_str(buf, soap_prefix);
+		wpabuf_put_str(buf, soap_error_prefix);
+		wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
+		wpabuf_put_str(buf, soap_error_postfix);
+		wpabuf_put_str(buf, soap_postfix);
+	}
+	os_free(replydata);
+
+	/* Now patch in the content length at the end */
+	if (body_start && put_length_here) {
+		int body_length = (char *) wpabuf_put(buf, 0) - body_start;
+		char len_buf[10];
+		os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
+		os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
+	}
+
+	send_wpabuf(c->sd, buf);
+	wpabuf_free(buf);
+}
+
+
+static const char * web_get_action(struct web_connection *c,
+				   const char *filename, size_t *action_len)
+{
+	const char *match;
+	int match_len;
+	char *b;
+	char *action;
+
+	*action_len = 0;
+	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
+			   filename);
+		return NULL;
+	}
+	/* The SOAPAction line of the header tells us what we want to do */
+	b = httpread_hdr_line_get(c->hread, "SOAPAction:");
+	if (b == NULL)
+		return NULL;
+	if (*b == '"')
+		b++;
+	else
+		return NULL;
+	match = urn_wfawlanconfig;
+	match_len = os_strlen(urn_wfawlanconfig) - 1;
+	if (os_strncasecmp(b, match, match_len))
+		return NULL;
+	b += match_len;
+	/* skip over version */
+	while (isgraph(*b) && *b != '#')
+		b++;
+	if (*b != '#')
+		return NULL;
+	b++;
+	/* Following the sharp(#) should be the action and a double quote */
+	action = b;
+	while (isgraph(*b) && *b != '"')
+		b++;
+	if (*b != '"')
+		return NULL;
+	*action_len = b - action;
+	return action;
+}
+
+
+/* Given that we have received a header w/ POST, act upon it
+ *
+ * Format of POST (case-insensitive):
+ *
+ * First line must be:
+ *      POST /<file> HTTP/1.1
+ * Since we don't do anything fancy we just ignore other lines.
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Connection: close
+ * Content-Type: text/xml
+ * Date: <rfc1123-date>
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_post(struct web_connection *c,
+				      const char *filename)
+{
+	enum http_reply_code ret;
+	struct upnp_wps_device_sm *sm = c->sm;
+	char *data = httpread_data_get(c->hread); /* body of http msg */
+	const char *action;
+	size_t action_len;
+	const char *replyname = NULL; /* argument name for the reply */
+	struct wpabuf *reply = NULL; /* data for the reply */
+
+	ret = UPNP_INVALID_ACTION;
+	action = web_get_action(c, filename, &action_len);
+	if (action == NULL)
+		goto bad;
+
+	/*
+	 * There are quite a few possible actions. Although we appear to
+	 * support them all here, not all of them are necessarily supported by
+	 * callbacks at higher levels.
+	 */
+	if (!os_strncasecmp("GetDeviceInfo", action, action_len))
+		ret = web_process_get_device_info(sm, &reply, &replyname);
+	else if (!os_strncasecmp("PutMessage", action, action_len))
+		ret = web_process_put_message(sm, data, &reply, &replyname);
+	else if (!os_strncasecmp("GetAPSettings", action, action_len))
+		ret = web_process_get_ap_settings(sm, data, &reply,
+						  &replyname);
+	else if (!os_strncasecmp("SetAPSettings", action, action_len))
+		ret = web_process_set_ap_settings(sm, data, &reply,
+						  &replyname);
+	else if (!os_strncasecmp("DelAPSettings", action, action_len))
+		ret = web_process_del_ap_settings(sm, data, &reply,
+						  &replyname);
+	else if (!os_strncasecmp("GetSTASettings", action, action_len))
+		ret = web_process_get_sta_settings(sm, data, &reply,
+						   &replyname);
+	else if (!os_strncasecmp("SetSTASettings", action, action_len))
+		ret = web_process_set_sta_settings(sm, data, &reply,
+						   &replyname);
+	else if (!os_strncasecmp("DelSTASettings", action, action_len))
+		ret = web_process_del_sta_settings(sm, data, &reply,
+						  &replyname);
+	else if (!os_strncasecmp("PutWLANResponse", action, action_len))
+		ret = web_process_put_wlan_response(sm, data, &reply,
+						    &replyname);
+	else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
+		ret = web_process_set_selected_registrar(sm, data, &reply,
+							 &replyname);
+	else if (!os_strncasecmp("RebootAP", action, action_len))
+		ret = web_process_reboot_ap(sm, data, &reply, &replyname);
+	else if (!os_strncasecmp("ResetAP", action, action_len))
+		ret = web_process_reset_ap(sm, data, &reply, &replyname);
+	else if (!os_strncasecmp("RebootSTA", action, action_len))
+		ret = web_process_reboot_sta(sm, data, &reply, &replyname);
+	else if (!os_strncasecmp("ResetSTA", action, action_len))
+		ret = web_process_reset_sta(sm, data, &reply, &replyname);
+	else
+		wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
+
+bad:
+	if (ret != HTTP_OK)
+		wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
+	web_connection_send_reply(c, ret, action, action_len, reply,
+				  replyname);
+	wpabuf_free(reply);
+}
+
+
+/* Given that we have received a header w/ SUBSCRIBE, act upon it
+ *
+ * Format of SUBSCRIBE (case-insensitive):
+ *
+ * First line must be:
+ *      SUBSCRIBE /wps_event HTTP/1.1
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Server: xx, UPnP/1.0, xx
+ * SID: uuid:xxxxxxxxx
+ * Timeout: Second-<n>
+ * Content-Length: 0
+ * Date: xxxx
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_subscribe(struct web_connection *c,
+					   const char *filename)
+{
+	struct upnp_wps_device_sm *sm = c->sm;
+	struct wpabuf *buf;
+	char *b;
+	char *hdr = httpread_hdr_get(c->hread);
+	char *h;
+	char *match;
+	int match_len;
+	char *end;
+	int len;
+	int got_nt = 0;
+	u8 uuid[UUID_LEN];
+	int got_uuid = 0;
+	char *callback_urls = NULL;
+	struct subscription *s = NULL;
+	enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
+
+	buf = wpabuf_alloc(1000);
+	if (buf == NULL)
+		return;
+
+	/* Parse/validate headers */
+	h = hdr;
+	/* First line: SUBSCRIBE /wps_event HTTP/1.1
+	 * has already been parsed.
+	 */
+	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
+		ret = HTTP_PRECONDITION_FAILED;
+		goto error;
+	}
+	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
+	end = os_strchr(h, '\n');
+
+	for (; end != NULL; h = end + 1) {
+		/* Option line by option line */
+		h = end + 1;
+		end = os_strchr(h, '\n');
+		if (end == NULL)
+			break; /* no unterminated lines allowed */
+
+		/* NT assures that it is our type of subscription;
+		 * not used for a renewl.
+		 **/
+		match = "NT:";
+		match_len = os_strlen(match);
+		if (os_strncasecmp(h, match, match_len) == 0) {
+			h += match_len;
+			while (*h == ' ' || *h == '\t')
+				h++;
+			match = "upnp:event";
+			match_len = os_strlen(match);
+			if (os_strncasecmp(h, match, match_len) != 0) {
+				ret = HTTP_BAD_REQUEST;
+				goto error;
+			}
+			got_nt = 1;
+			continue;
+		}
+		/* HOST should refer to us */
+#if 0
+		match = "HOST:";
+		match_len = os_strlen(match);
+		if (os_strncasecmp(h, match, match_len) == 0) {
+			h += match_len;
+			while (*h == ' ' || *h == '\t')
+				h++;
+			.....
+		}
+#endif
+		/* CALLBACK gives one or more URLs for NOTIFYs
+		 * to be sent as a result of the subscription.
+		 * Each URL is enclosed in angle brackets.
+		 */
+		match = "CALLBACK:";
+		match_len = os_strlen(match);
+		if (os_strncasecmp(h, match, match_len) == 0) {
+			h += match_len;
+			while (*h == ' ' || *h == '\t')
+				h++;
+			len = end - h;
+			os_free(callback_urls);
+			callback_urls = os_malloc(len + 1);
+			if (callback_urls == NULL) {
+				ret = HTTP_INTERNAL_SERVER_ERROR;
+				goto error;
+			}
+			os_memcpy(callback_urls, h, len);
+			callback_urls[len] = 0;
+			continue;
+		}
+		/* SID is only for renewal */
+		match = "SID:";
+		match_len = os_strlen(match);
+		if (os_strncasecmp(h, match, match_len) == 0) {
+			h += match_len;
+			while (*h == ' ' || *h == '\t')
+				h++;
+			match = "uuid:";
+			match_len = os_strlen(match);
+			if (os_strncasecmp(h, match, match_len) != 0) {
+				ret = HTTP_BAD_REQUEST;
+				goto error;
+			}
+			h += match_len;
+			while (*h == ' ' || *h == '\t')
+				h++;
+			if (uuid_str2bin(h, uuid)) {
+				ret = HTTP_BAD_REQUEST;
+				goto error;
+			}
+			got_uuid = 1;
+			continue;
+		}
+		/* TIMEOUT is requested timeout, but apparently we can
+		 * just ignore this.
+		 */
+	}
+
+	if (got_uuid) {
+		/* renewal */
+		if (callback_urls) {
+			ret = HTTP_BAD_REQUEST;
+			goto error;
+		}
+		s = subscription_renew(sm, uuid);
+		if (s == NULL) {
+			ret = HTTP_PRECONDITION_FAILED;
+			goto error;
+		}
+	} else if (callback_urls) {
+		if (!got_nt) {
+			ret = HTTP_PRECONDITION_FAILED;
+			goto error;
+		}
+		s = subscription_start(sm, callback_urls);
+		if (s == NULL) {
+			ret = HTTP_INTERNAL_SERVER_ERROR;
+			goto error;
+		}
+		callback_urls = NULL;   /* is now owned by subscription */
+	} else {
+		ret = HTTP_PRECONDITION_FAILED;
+		goto error;
+	}
+
+	/* success */
+	http_put_reply_code(buf, HTTP_OK);
+	wpabuf_put_str(buf, http_server_hdr);
+	wpabuf_put_str(buf, http_connection_close);
+	wpabuf_put_str(buf, "Content-Length: 0\r\n");
+	wpabuf_put_str(buf, "SID: uuid:");
+	/* subscription id */
+	b = wpabuf_put(buf, 0);
+	uuid_bin2str(s->uuid, b, 80);
+	wpabuf_put(buf, os_strlen(b));
+	wpabuf_put_str(buf, "\r\n");
+	wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
+	http_put_date(buf);
+	/* And empty line to terminate header: */
+	wpabuf_put_str(buf, "\r\n");
+
+	send_wpabuf(c->sd, buf);
+	wpabuf_free(buf);
+	os_free(callback_urls);
+	return;
+
+error:
+	/* Per UPnP spec:
+	* Errors
+	* Incompatible headers
+	*   400 Bad Request. If SID header and one of NT or CALLBACK headers
+	*     are present, the publisher must respond with HTTP error
+	*     400 Bad Request.
+	* Missing or invalid CALLBACK
+	*   412 Precondition Failed. If CALLBACK header is missing or does not
+	*     contain a valid HTTP URL, the publisher must respond with HTTP
+	*     error 412 Precondition Failed.
+	* Invalid NT
+	*   412 Precondition Failed. If NT header does not equal upnp:event,
+	*     the publisher must respond with HTTP error 412 Precondition
+	*     Failed.
+	* [For resubscription, use 412 if unknown uuid].
+	* Unable to accept subscription
+	*   5xx. If a publisher is not able to accept a subscription (such as
+	*     due to insufficient resources), it must respond with a
+	*     HTTP 500-series error code.
+	*   599 Too many subscriptions (not a standard HTTP error)
+	*/
+	http_put_empty(buf, ret);
+	send_wpabuf(c->sd, buf);
+	wpabuf_free(buf);
+}
+
+
+/* Given that we have received a header w/ UNSUBSCRIBE, act upon it
+ *
+ * Format of UNSUBSCRIBE (case-insensitive):
+ *
+ * First line must be:
+ *      UNSUBSCRIBE /wps_event HTTP/1.1
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Content-Length: 0
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_unsubscribe(struct web_connection *c,
+					     const char *filename)
+{
+	struct upnp_wps_device_sm *sm = c->sm;
+	struct wpabuf *buf;
+	char *hdr = httpread_hdr_get(c->hread);
+	char *h;
+	char *match;
+	int match_len;
+	char *end;
+	u8 uuid[UUID_LEN];
+	int got_uuid = 0;
+	struct subscription *s = NULL;
+	enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
+
+	/* Parse/validate headers */
+	h = hdr;
+	/* First line: UNSUBSCRIBE /wps_event HTTP/1.1
+	 * has already been parsed.
+	 */
+	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
+		ret = HTTP_PRECONDITION_FAILED;
+		goto send_msg;
+	}
+	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
+	end = os_strchr(h, '\n');
+
+	for (; end != NULL; h = end + 1) {
+		/* Option line by option line */
+		h = end + 1;
+		end = os_strchr(h, '\n');
+		if (end == NULL)
+			break; /* no unterminated lines allowed */
+
+		/* HOST should refer to us */
+#if 0
+		match = "HOST:";
+		match_len = os_strlen(match);
+		if (os_strncasecmp(h, match, match_len) == 0) {
+			h += match_len;
+			while (*h == ' ' || *h == '\t')
+				h++;
+			.....
+		}
+#endif
+		/* SID is only for renewal */
+		match = "SID:";
+		match_len = os_strlen(match);
+		if (os_strncasecmp(h, match, match_len) == 0) {
+			h += match_len;
+			while (*h == ' ' || *h == '\t')
+				h++;
+			match = "uuid:";
+			match_len = os_strlen(match);
+			if (os_strncasecmp(h, match, match_len) != 0) {
+				ret = HTTP_BAD_REQUEST;
+				goto send_msg;
+			}
+			h += match_len;
+			while (*h == ' ' || *h == '\t')
+				h++;
+			if (uuid_str2bin(h, uuid)) {
+				ret = HTTP_BAD_REQUEST;
+				goto send_msg;
+			}
+			got_uuid = 1;
+			continue;
+		}
+	}
+
+	if (got_uuid) {
+		s = subscription_find(sm, uuid);
+		if (s) {
+			wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s",
+				   s,
+				   (s && s->addr_list &&
+				    s->addr_list->domain_and_port) ?
+				   s->addr_list->domain_and_port : "-null-");
+			subscription_unlink(s);
+			subscription_destroy(s);
+		}
+	} else {
+		wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
+			   "found)");
+		ret = HTTP_PRECONDITION_FAILED;
+		goto send_msg;
+	}
+
+	ret = HTTP_OK;
+
+send_msg:
+	buf = wpabuf_alloc(200);
+	if (buf == NULL)
+		return;
+	http_put_empty(buf, ret);
+	send_wpabuf(c->sd, buf);
+	wpabuf_free(buf);
+}
+
+
+/* Send error in response to unknown requests */
+static void web_connection_unimplemented(struct web_connection *c)
+{
+	struct wpabuf *buf;
+	buf = wpabuf_alloc(200);
+	if (buf == NULL)
+		return;
+	http_put_empty(buf, HTTP_UNIMPLEMENTED);
+	send_wpabuf(c->sd, buf);
+	wpabuf_free(buf);
+}
+
+
+
+/* Called when we have gotten an apparently valid http request.
+ */
+static void web_connection_check_data(struct web_connection *c)
+{
+	struct httpread *hread = c->hread;
+	enum httpread_hdr_type htype = httpread_hdr_type_get(hread);
+	/* char *data = httpread_data_get(hread); */
+	char *filename = httpread_uri_get(hread);
+
+	c->done = 1;
+	if (!filename) {
+		wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
+		return;
+	}
+	/* Trim leading slashes from filename */
+	while (*filename == '/')
+		filename++;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
+		   htype, inet_ntoa(c->cli_addr.sin_addr),
+		   htons(c->cli_addr.sin_port));
+
+	switch (htype) {
+	case HTTPREAD_HDR_TYPE_GET:
+		web_connection_parse_get(c, filename);
+		break;
+	case HTTPREAD_HDR_TYPE_POST:
+		web_connection_parse_post(c, filename);
+		break;
+	case HTTPREAD_HDR_TYPE_SUBSCRIBE:
+		web_connection_parse_subscribe(c, filename);
+		break;
+	case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
+		web_connection_parse_unsubscribe(c, filename);
+		break;
+		/* We are not required to support M-POST; just plain
+		 * POST is supposed to work, so we only support that.
+		 * If for some reason we need to support M-POST, it is
+		 * mostly the same as POST, with small differences.
+		 */
+	default:
+		/* Send 501 for anything else */
+		web_connection_unimplemented(c);
+		break;
+	}
+}
+
+
+
+/* called back when we have gotten request */
+static void web_connection_got_file_handler(struct httpread *handle,
+					    void *cookie,
+					    enum httpread_event en)
+{
+	struct web_connection *c = cookie;
+
+	if (en == HTTPREAD_EVENT_FILE_READY)
+		web_connection_check_data(c);
+	web_connection_stop(c);
+}
+
+
+/* web_connection_start - Start web connection
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @sd: Socket descriptor
+ * @addr: Client address
+ *
+ * The socket descriptor sd is handed over for ownership by the WPS UPnP
+ * state machine.
+ */
+static void web_connection_start(struct upnp_wps_device_sm *sm,
+				 int sd, struct sockaddr_in *addr)
+{
+	struct web_connection *c = NULL;
+
+	/* if too many connections, bail */
+	if (sm->n_web_connections >= MAX_WEB_CONNECTIONS) {
+		close(sd);
+		return;
+	}
+
+	c = os_zalloc(sizeof(*c));
+	if (c == NULL)
+		return;
+	os_memcpy(&c->cli_addr, addr, sizeof(c->cli_addr));
+	c->sm = sm;
+	c->sd = sd;
+#if 0
+	/*
+	 * Setting non-blocking should not be necessary for read, and can mess
+	 * up sending where blocking might be better.
+	 */
+	if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
+		break;
+#endif
+	c->hread = httpread_create(c->sd, web_connection_got_file_handler,
+				   c /* cookie */,
+				   WEB_CONNECTION_MAX_READ,
+				   WEB_CONNECTION_TIMEOUT_SEC);
+	if (c->hread == NULL)
+		goto fail;
+	if (sm->web_connections) {
+		c->next = sm->web_connections;
+		c->prev = c->next->prev;
+		c->prev->next = c;
+		c->next->prev = c;
+	} else {
+		sm->web_connections = c->next = c->prev = c;
+	}
+	sm->n_web_connections++;
+	return;
+
+fail:
+	if (c)
+		web_connection_stop(c);
+}
+
+
+/*
+ * Listening for web connections
+ * We have a single TCP listening port, and hand off connections as we get
+ * them.
+ */
+
+void web_listener_stop(struct upnp_wps_device_sm *sm)
+{
+	if (sm->web_sd_registered) {
+		sm->web_sd_registered = 0;
+		eloop_unregister_sock(sm->web_sd, EVENT_TYPE_READ);
+	}
+	if (sm->web_sd >= 0)
+		close(sm->web_sd);
+	sm->web_sd = -1;
+}
+
+
+static void web_listener_handler(int sd, void *eloop_ctx, void *sock_ctx)
+{
+	struct sockaddr_in addr;
+	socklen_t addr_len = sizeof(addr);
+	struct upnp_wps_device_sm *sm = sock_ctx;
+	int new_sd;
+
+	/* Create state for new connection */
+	/* Remember so we can cancel if need be */
+	new_sd = accept(sm->web_sd, (struct sockaddr *) &addr, &addr_len);
+	if (new_sd < 0) {
+		wpa_printf(MSG_ERROR, "WPS UPnP: web listener accept "
+			   "errno=%d (%s) web_sd=%d",
+			   errno, strerror(errno), sm->web_sd);
+		return;
+	}
+	web_connection_start(sm, new_sd, &addr);
+}
+
+
+int web_listener_start(struct upnp_wps_device_sm *sm)
+{
+	struct sockaddr_in addr;
+	int port;
+
+	sm->web_sd = socket(AF_INET, SOCK_STREAM, 0);
+	if (sm->web_sd < 0)
+		goto fail;
+	if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0)
+		goto fail;
+	port = 49152;  /* first non-reserved port */
+	for (;;) {
+		os_memset(&addr, 0, sizeof(addr));
+		addr.sin_family = AF_INET;
+		addr.sin_addr.s_addr = sm->ip_addr;
+		addr.sin_port = htons(port);
+		if (bind(sm->web_sd, (struct sockaddr *) &addr,
+			 sizeof(addr)) == 0)
+			break;
+		if (errno == EADDRINUSE) {
+			/* search for unused port */
+			if (++port == 65535)
+				goto fail;
+			continue;
+		}
+		goto fail;
+	}
+	if (listen(sm->web_sd, 10 /* max backlog */) != 0)
+		goto fail;
+	if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0)
+		goto fail;
+	if (eloop_register_sock(sm->web_sd, EVENT_TYPE_READ,
+				web_listener_handler, NULL, sm))
+		goto fail;
+	sm->web_sd_registered = 1;
+	sm->web_port = port;
+
+	return 0;
+
+fail:
+	/* Error */
+	web_listener_stop(sm);
+	return -1;
+}

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/ChangeLog
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/ChangeLog?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/ChangeLog (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/ChangeLog Sun Feb 15 19:38:55 2009
@@ -1,4 +1,10 @@
 ChangeLog for wpa_supplicant
+
+2009-02-15 - v0.6.8
+	* increased wpa_cli ping interval to 5 seconds and made this
+	  configurable with a new command line options (-G<seconds>)
+	* fixed scan buffer processing with WEXT to handle up to 65535
+	  byte result buffer (previously, limited to 32768 bytes)
 
 2009-01-06 - v0.6.7
 	* added support for Wi-Fi Protected Setup (WPS)

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/Makefile
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/Makefile?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/Makefile (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/Makefile Sun Feb 15 19:38:55 2009
@@ -5,6 +5,9 @@
 ifndef CFLAGS
 CFLAGS = -MMD -O2 -Wall -g
 endif
+
+export LIBDIR ?= /usr/local/lib/
+export BINDIR ?= /usr/local/sbin/
 
 CFLAGS += -I../src
 CFLAGS += -I../src/crypto
@@ -35,8 +38,9 @@
 	echo CONFIG_WIRELESS_EXTENSION=y >> .config
 
 install: all
-	mkdir -p $(DESTDIR)/usr/local/sbin/
-	for i in $(ALL); do cp $$i $(DESTDIR)/usr/local/sbin/$$i; done
+	mkdir -p $(DESTDIR)$(BINDIR)
+	for i in $(ALL); do cp $$i $(DESTDIR)$(BINDIR)/$$i; done
+	$(MAKE) -C ../src install
 
 OBJS = config.o
 OBJS += ../src/utils/common.o
@@ -505,10 +509,6 @@
 
 ifdef CONFIG_WPS
 # EAP-WSC
-ifeq ($(CONFIG_EAP_WSC), dyn)
-CFLAGS += -DCONFIG_WPS -DEAP_WSC_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_wsc.so
-else
 CFLAGS += -DCONFIG_WPS -DEAP_WSC
 OBJS += wps_supplicant.o
 OBJS += ../src/utils/uuid.o
@@ -522,11 +522,20 @@
 OBJS += ../src/wps/wps_enrollee.o
 OBJS += ../src/wps/wps_registrar.o
 OBJS_h += ../src/eap_server/eap_wsc.o
-endif
 CONFIG_IEEE8021X_EAPOL=y
 NEED_DH_GROUPS=y
 NEED_SHA256=y
 NEED_BASE64=y
+
+ifdef CONFIG_WPS_UPNP
+CFLAGS += -DCONFIG_WPS_UPNP
+OBJS += ../src/wps/wps_upnp.o
+OBJS += ../src/wps/wps_upnp_ssdp.o
+OBJS += ../src/wps/wps_upnp_web.o
+OBJS += ../src/wps/wps_upnp_event.o
+OBJS += ../src/wps/httpread.o
+endif
+
 endif
 
 ifdef CONFIG_EAP_IKEV2
@@ -564,6 +573,9 @@
 OBJS += ../src/eap_peer/eap_tnc.o
 OBJS += ../src/eap_peer/tncc.o
 NEED_BASE64=y
+ifndef CONFIG_NATIVE_WINDOWS
+LIBS += -ldl
+endif
 endif
 
 ifdef CONFIG_IEEE8021X_EAPOL
@@ -1017,6 +1029,7 @@
 OBJS_priv += ../src/utils/$(CONFIG_ELOOP).o
 OBJS_priv += ../src/utils/common.o
 OBJS_priv += ../src/utils/wpa_debug.o
+OBJS_priv += ../src/utils/wpabuf.o
 OBJS_priv += wpa_priv.o
 ifdef CONFIG_DRIVER_TEST
 OBJS_priv += ../src/crypto/sha1.o
@@ -1126,7 +1139,7 @@
 
 %.so: %.c
 	$(CC) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \
-		-D$(*:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init
+		-D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init
 
 
 wpa_supplicant.exe: wpa_supplicant
@@ -1170,7 +1183,7 @@
 	./test-sha1
 	rm test-sha1
 
-TEST_SHA256_OBJS = ../src/crypto/sha256.o ../src/crypto/md5.o tests/test_sha256.o ../src/crypto/crypto_openssl.o
+TEST_SHA256_OBJS = ../src/crypto/sha256.o ../src/crypto/md5.o tests/test_sha256.o ../src/utils/os_unix.o ../src/crypto/crypto_openssl.o
 test-sha256: $(TEST_SHA256_OBJS)
 	$(LDO) $(LDFLAGS) -o $@ $(TEST_SHA256_OBJS) $(LIBS)
 	./test-sha256

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/README
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/README?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/README (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/README Sun Feb 15 19:38:55 2009
@@ -166,7 +166,7 @@
 	Linux ndiswrapper (http://ndiswrapper.sourceforge.net/) with
 	Windows NDIS driver.
 
-	Broadcom wl.o driver
+	Broadcom wl.o driver (old version only)
 	This is a generic Linux driver for Broadcom IEEE 802.11a/g cards.
 	However, it is proprietary driver that is not publicly available
 	except for couple of exceptions, mainly Broadcom-based APs/wireless
@@ -176,7 +176,10 @@
 	the needed header file, wlioctl.h, for compiling wpa_supplicant.
 	This driver support in wpa_supplicant is expected to work also with
 	other devices based on Broadcom driver (assuming the driver includes
-	client mode support).
+	client mode support). Please note that the newer Broadcom driver
+	("hybrid Linux driver") supports Linux wireless extensions and does
+	not need (or even work) with the specific driver wrapper. Use -Dwext
+	with that driver.
 
 	Intel ipw2100 driver
 	(http://sourceforge.net/projects/ipw2100/)

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/README-WPS
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/README-WPS?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/README-WPS (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/README-WPS Sun Feb 15 19:38:55 2009
@@ -163,3 +163,22 @@
 WPS tab that guides user through WPS registration with automatic AP
 selection. In addition, it shows how WPS can be started manually by
 selecting an AP from scan results.
+
+
+Credential processing
+---------------------
+
+By default, wpa_supplicant processes received credentials and updates
+its configuration internally. However, it is possible to
+control these operations from external programs, if desired.
+
+This internal processing can be disabled with wps_cred_processing=1
+option. When this is used, an external program is responsible for
+processing the credential attributes and updating wpa_supplicant
+configuration based on them.
+
+Following control interface messages are sent out for external programs:
+
+WPS-CRED-RECEIVED  <hexdump of Credential attribute(s)>
+For example:
+<2>WPS-CRED-RECEIVED 100e006f10260001011045000c6a6b6d2d7770732d74657374100300020020100f000200081027004030653462303435366332363666653064333961643135353461316634626637313234333761636664623766333939653534663166316230323061643434386235102000060266a0ee1727

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/config.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/config.h?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/config.h (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/config.h Sun Feb 15 19:38:55 2009
@@ -312,6 +312,17 @@
 	 * in
 	 */
 	char country[2];
+
+	/**
+	 * wps_cred_processing - Credential processing
+	 *
+	 *   0 = process received credentials internally
+	 *   1 = do not process received credentials; just pass them over
+	 *	ctrl_iface to external program(s)
+	 *   2 = process received credentials internally and pass them over
+	 *	ctrl_iface to external program(s)
+	 */
+	int wps_cred_processing;
 };
 
 

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/config_file.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/config_file.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/config_file.c (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/config_file.c Sun Feb 15 19:38:55 2009
@@ -456,6 +456,7 @@
 	{ STR_RANGE(serial_number, 0, 32) },
 	{ STR(device_type) },
 	{ FUNC(os_version) },
+	{ INT_RANGE(wps_cred_processing, 0, 2) },
 #endif /* CONFIG_WPS */
 	{ FUNC(country) }
 };
@@ -881,6 +882,9 @@
 	if (WPA_GET_BE32(config->os_version))
 		fprintf(f, "os_version=%08x\n",
 			WPA_GET_BE32(config->os_version));
+	if (config->wps_cred_processing)
+		fprintf(f, "wps_cred_processing=%d\n",
+			config->wps_cred_processing);
 #endif /* CONFIG_WPS */
 	if (config->country[0] && config->country[1]) {
 		fprintf(f, "country=%c%c\n",

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/config_winreg.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/config_winreg.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/config_winreg.c (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/config_winreg.c Sun Feb 15 19:38:55 2009
@@ -251,6 +251,8 @@
 		hk, TEXT("device_type"));
 	if (wpa_config_read_global_os_version(config, hk))
 		errors++;
+	wpa_config_read_reg_dword(hk, TEXT("wps_cred_processing"),
+				  &config->wps_cred_processing);
 #endif /* CONFIG_WPS */
 
 	return errors ? -1 : 0;
@@ -573,6 +575,8 @@
 			    WPA_GET_BE32(config->os_version));
 		wpa_config_write_reg_string(hk, "os_version", vbuf);
 	}
+	wpa_config_write_reg_dword(hk, TEXT("wps_cred_processing"),
+				   config->wps_cred_processing, 0);
 #endif /* CONFIG_WPS */
 
 	return 0;

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/ctrl_iface_dbus.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/ctrl_iface_dbus.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/ctrl_iface_dbus.c (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/ctrl_iface_dbus.c Sun Feb 15 19:38:55 2009
@@ -18,6 +18,7 @@
 #include "eloop.h"
 #include "config.h"
 #include "wpa_supplicant_i.h"
+#include "wps/wps.h"
 #include "ctrl_iface_dbus.h"
 #include "ctrl_iface_dbus_handlers.h"
 
@@ -547,7 +548,8 @@
 
 	/* If the message was handled, send back the reply */
 	if (reply) {
-		dbus_connection_send(connection, reply, NULL);
+		if (!dbus_message_get_no_reply(message))
+			dbus_connection_send(connection, reply, NULL);
 		dbus_message_unref(reply);
 	}
 
@@ -606,7 +608,8 @@
 
 	/* If the message was handled, send back the reply */
 	if (reply) {
-		dbus_connection_send(connection, reply, NULL);
+		if (!dbus_message_get_no_reply(message))
+			dbus_connection_send(connection, reply, NULL);
 		dbus_message_unref(reply);
 	}
 
@@ -736,6 +739,63 @@
 out:
 	dbus_message_unref(_signal);
 }
+
+
+#ifdef CONFIG_WPS
+void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
+					 const struct wps_credential *cred)
+{
+	struct ctrl_iface_dbus_priv *iface;
+	DBusMessage *_signal = NULL;
+	const char *path;
+
+	/* Do nothing if the control interface is not turned on */
+	if (wpa_s->global == NULL)
+		return;
+	iface = wpa_s->global->dbus_ctrl_iface;
+	if (iface == NULL)
+		return;
+
+	path = wpa_supplicant_get_dbus_path(wpa_s);
+	if (path == NULL) {
+		perror("wpa_supplicant_dbus_notify_wps_cred[dbus]: "
+		       "interface didn't have a dbus path");
+		wpa_printf(MSG_ERROR,
+		           "wpa_supplicant_dbus_notify_wps_cred[dbus]: "
+		           "interface didn't have a dbus path; can't send "
+		           "signal.");
+		return;
+	}
+	_signal = dbus_message_new_signal(path, WPAS_DBUS_IFACE_INTERFACE,
+					  "WpsCred");
+	if (_signal == NULL) {
+		perror("wpa_supplicant_dbus_notify_wps_cred[dbus]: "
+		       "couldn't create dbus signal; likely out of memory");
+		wpa_printf(MSG_ERROR,
+		           "wpa_supplicant_dbus_notify_wps_cred[dbus]: "
+		           "couldn't create dbus signal; likely out of "
+		           "memory.");
+		return;
+	}
+
+	if (!dbus_message_append_args(_signal,
+	                              DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+				      &cred->cred_attr, cred->cred_attr_len,
+	                              DBUS_TYPE_INVALID)) {
+		perror("wpa_supplicant_dbus_notify_wps_cred[dbus]: "
+		       "not enough memory to construct signal.");
+		wpa_printf(MSG_ERROR,
+		           "wpa_supplicant_dbus_notify_wps_cred[dbus]: "
+		           "not enough memory to construct signal.");
+		goto out;
+	}
+
+	dbus_connection_send(iface->con, _signal, NULL);
+
+out:
+	dbus_message_unref(_signal);
+}
+#endif /* CONFIG_WPS */
 
 
 /**

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/ctrl_iface_dbus.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/ctrl_iface_dbus.h?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/ctrl_iface_dbus.h (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/ctrl_iface_dbus.h Sun Feb 15 19:38:55 2009
@@ -14,6 +14,8 @@
 
 #ifndef CTRL_IFACE_DBUS_H
 #define CTRL_IFACE_DBUS_H
+
+struct wps_credential;
 
 #ifdef CONFIG_CTRL_IFACE_DBUS
 
@@ -84,6 +86,8 @@
 void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s,
 					     wpa_states new_state,
 					     wpa_states old_state);
+void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
+					 const struct wps_credential *cred);
 
 char * wpas_dbus_decompose_object_path(const char *path, char **network,
                                        char **bssid);
@@ -129,6 +133,12 @@
 {
 }
 
+static inline void
+wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
+				    const struct wps_credential *cred)
+{
+}
+
 static inline int
 wpas_dbus_register_iface(struct wpa_supplicant *wpa_s)
 {

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/defconfig
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/defconfig?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/defconfig (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/defconfig Sun Feb 15 19:38:55 2009
@@ -51,8 +51,8 @@
 
 # Driver interface for madwifi driver
 #CONFIG_DRIVER_MADWIFI=y
-# Change include directories to match with the local setup
-#CFLAGS += -I../madwifi/wpa
+# Set include directory to the madwifi source tree
+#CFLAGS += -I../../madwifi
 
 # Driver interface for Prism54 driver
 # (Note: Prism54 is not yet supported, i.e., this will not work as-is and is
@@ -65,7 +65,10 @@
 # Driver interface for Atmel driver
 CONFIG_DRIVER_ATMEL=y
 
-# Driver interface for Broadcom driver
+# Driver interface for old Broadcom driver
+# Please note that the newer Broadcom driver ("hybrid Linux driver") supports
+# Linux wireless extensions and does not need (or even work) with the old
+# driver wrapper. Use CONFIG_DRIVER_WEXT=y with that driver.
 #CONFIG_DRIVER_BROADCOM=y
 # Example path for wlioctl.h; change to match your configuration
 #CFLAGS += -I/opt/WRT54GS/release/src/include
@@ -78,6 +81,9 @@
 
 # Driver interface for generic Linux wireless extensions
 CONFIG_DRIVER_WEXT=y
+
+# Driver interface for Linux drivers using the nl80211 kernel interface
+#CONFIG_DRIVER_NL80211=y
 
 # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
 #CONFIG_DRIVER_BSD=y
@@ -357,15 +363,10 @@
 
 # Include client MLME (management frame processing).
 # This can be used to move MLME processing of Linux mac80211 stack into user
-# space.
+# space. Please note that this is currently only available with
+# driver_nl80211.c and only with a modified version of Linux kernel and
+# wpa_supplicant.
 #CONFIG_CLIENT_MLME=y
-# Currently, driver_nl80211.c build requires some additional parameters to be
-# able to include some of the kernel header files. Following lines can be used
-# to set these (WIRELESS_DEV must point to the root directory of the
-# wireless-testing.git tree). In addition, mac80211 may need external patches
-# to enable userspace MLME support.
-#WIRELESS_DEV=/usr/src/wireless-testing
-#CFLAGS += -I$(WIRELESS_DEV)/net/mac80211
 
 # IEEE Std 802.11r-2008 (Fast BSS Transition)
 #CONFIG_IEEE80211R=y

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_background.8
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_background.8?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_background.8 (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_background.8 Sun Feb 15 19:38:55 2009
@@ -3,7 +3,7 @@
 .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> 
 .\" Please send any bug reports, improvements, comments, patches, 
 .\" etc. to Steve Cheng <steve at ggi-project.org>.
-.TH "WPA_BACKGROUND" "8" "06 January 2009" "" ""
+.TH "WPA_BACKGROUND" "8" "15 February 2009" "" ""
 
 .SH NAME
 wpa_background \- Background information on Wi-Fi Protected Access and IEEE 802.11i

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_cli.8
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_cli.8?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_cli.8 (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_cli.8 Sun Feb 15 19:38:55 2009
@@ -3,7 +3,7 @@
 .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> 
 .\" Please send any bug reports, improvements, comments, patches, 
 .\" etc. to Steve Cheng <steve at ggi-project.org>.
-.TH "WPA_CLI" "8" "06 January 2009" "" ""
+.TH "WPA_CLI" "8" "15 February 2009" "" ""
 
 .SH NAME
 wpa_cli \- WPA command line client

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_gui.8
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_gui.8?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_gui.8 (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_gui.8 Sun Feb 15 19:38:55 2009
@@ -3,7 +3,7 @@
 .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> 
 .\" Please send any bug reports, improvements, comments, patches, 
 .\" etc. to Steve Cheng <steve at ggi-project.org>.
-.TH "WPA_GUI" "8" "06 January 2009" "" ""
+.TH "WPA_GUI" "8" "15 February 2009" "" ""
 
 .SH NAME
 wpa_gui \- WPA Graphical User Interface

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_passphrase.8
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_passphrase.8?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_passphrase.8 (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_passphrase.8 Sun Feb 15 19:38:55 2009
@@ -3,7 +3,7 @@
 .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> 
 .\" Please send any bug reports, improvements, comments, patches, 
 .\" etc. to Steve Cheng <steve at ggi-project.org>.
-.TH "WPA_PASSPHRASE" "8" "06 January 2009" "" ""
+.TH "WPA_PASSPHRASE" "8" "15 February 2009" "" ""
 
 .SH NAME
 wpa_passphrase \- Generate a WPA PSK from an ASCII passphrase for a SSID

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_priv.8
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_priv.8?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_priv.8 (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_priv.8 Sun Feb 15 19:38:55 2009
@@ -3,7 +3,7 @@
 .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> 
 .\" Please send any bug reports, improvements, comments, patches, 
 .\" etc. to Steve Cheng <steve at ggi-project.org>.
-.TH "WPA_PRIV" "8" "06 January 2009" "" ""
+.TH "WPA_PRIV" "8" "15 February 2009" "" ""
 
 .SH NAME
 wpa_priv \- wpa_supplicant privilege separation helper

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_supplicant.8
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_supplicant.8?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_supplicant.8 (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_supplicant.8 Sun Feb 15 19:38:55 2009
@@ -3,7 +3,7 @@
 .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> 
 .\" Please send any bug reports, improvements, comments, patches, 
 .\" etc. to Steve Cheng <steve at ggi-project.org>.
-.TH "WPA_SUPPLICANT" "8" "06 January 2009" "" ""
+.TH "WPA_SUPPLICANT" "8" "15 February 2009" "" ""
 
 .SH NAME
 wpa_supplicant \- Wi-Fi Protected Access client and IEEE 802.1X supplicant

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 Sun Feb 15 19:38:55 2009
@@ -3,7 +3,7 @@
 .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> 
 .\" Please send any bug reports, improvements, comments, patches, 
 .\" etc. to Steve Cheng <steve at ggi-project.org>.
-.TH "WPA_SUPPLICANT.CONF" "5" "06 January 2009" "" ""
+.TH "WPA_SUPPLICANT.CONF" "5" "15 February 2009" "" ""
 
 .SH NAME
 wpa_supplicant.conf \- configuration file for wpa_supplicant

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/eapol_test.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/eapol_test.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/eapol_test.c (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/eapol_test.c Sun Feb 15 19:38:55 2009
@@ -156,6 +156,18 @@
 }
 
 
+static struct extra_radius_attr *
+find_extra_attr(struct extra_radius_attr *attrs, u8 type)
+{
+	struct extra_radius_attr *p;
+	for (p = attrs; p; p = p->next) {
+		if (p->type == type)
+			return p;
+	}
+	return NULL;
+}
+
+
 static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e,
 					  const u8 *eap, size_t len)
 {
@@ -200,7 +212,8 @@
 		goto fail;
 	}
 
-	if (!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+	if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_NAS_IP_ADDRESS) &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
 				 (u8 *) &e->own_ip_addr, 4)) {
 		printf("Could not add NAS-IP-Address\n");
 		goto fail;
@@ -208,7 +221,9 @@
 
 	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
 		    MAC2STR(e->wpa_s->own_addr));
-	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+	if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CALLING_STATION_ID)
+	    &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
 				 (u8 *) buf, os_strlen(buf))) {
 		printf("Could not add Calling-Station-Id\n");
 		goto fail;
@@ -217,19 +232,22 @@
 	/* TODO: should probably check MTU from driver config; 2304 is max for
 	 * IEEE 802.11, but use 1400 to avoid problems with too large packets
 	 */
-	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
+	if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_FRAMED_MTU) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
 		printf("Could not add Framed-MTU\n");
 		goto fail;
 	}
 
-	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
+	if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_NAS_PORT_TYPE) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
 				       RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
 		printf("Could not add NAS-Port-Type\n");
 		goto fail;
 	}
 
 	os_snprintf(buf, sizeof(buf), "%s", e->connect_info);
-	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+	if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CONNECT_INFO) &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
 				 (u8 *) buf, os_strlen(buf))) {
 		printf("Could not add Connect-Info\n");
 		goto fail;

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/events.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/events.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/events.c (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/events.c Sun Feb 15 19:38:55 2009
@@ -270,7 +270,8 @@
 }
 
 
-static int wpa_supplicant_ssid_bss_match(struct wpa_ssid *ssid,
+static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
+					 struct wpa_ssid *ssid,
 					 struct wpa_scan_res *bss)
 {
 	struct wpa_ie_data ie;
@@ -278,7 +279,7 @@
 	const u8 *rsn_ie, *wpa_ie;
 	int ret;
 
-	ret = wpas_wps_ssid_bss_match(ssid, bss);
+	ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss);
 	if (ret >= 0)
 		return ret;
 
@@ -424,7 +425,7 @@
 
 #ifdef CONFIG_WPS
 			if (ssid->ssid_len == 0 &&
-			    wpas_wps_ssid_wildcard_ok(ssid, bss))
+			    wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
 				check_ssid = 0;
 #endif /* CONFIG_WPS */
 
@@ -444,7 +445,7 @@
 				continue;
 			}
 
-			if (!wpa_supplicant_ssid_bss_match(ssid, bss))
+			if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
 				continue;
 
 			wpa_printf(MSG_DEBUG, "   selected WPA AP "
@@ -514,7 +515,8 @@
 				 * with our mode. */
 				check_ssid = 1;
 				if (ssid->ssid_len == 0 &&
-				    wpas_wps_ssid_wildcard_ok(ssid, bss))
+				    wpas_wps_ssid_wildcard_ok(wpa_s, ssid,
+							      bss))
 					check_ssid = 0;
 			}
 #endif /* CONFIG_WPS */
@@ -645,6 +647,7 @@
 			wpa_printf(MSG_DEBUG, "No APs found - clear blacklist "
 				   "and try again");
 			wpa_blacklist_clear(wpa_s);
+			wpa_s->blacklist_cleared++;
 		} else if (selected == NULL) {
 			break;
 		}

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/mlme.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/mlme.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/mlme.c (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/mlme.c Sun Feb 15 19:38:55 2009
@@ -1087,9 +1087,10 @@
 			   status_code);
 #ifdef CONFIG_IEEE80211W
 		if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
-		    elems.assoc_comeback && elems.assoc_comeback_len == 4) {
+		    elems.timeout_int && elems.timeout_int_len == 5 &&
+		    elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) {
 			u32 tu, ms;
-			tu = WPA_GET_LE32(elems.assoc_comeback);
+			tu = WPA_GET_LE32(elems.timeout_int + 1);
 			ms = tu * 1024 / 1000;
 			wpa_printf(MSG_DEBUG, "MLME: AP rejected association "
 				   "temporarily; comeback duration %u TU "

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/nmake.mak
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/nmake.mak?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/nmake.mak (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/nmake.mak Sun Feb 15 19:38:55 2009
@@ -81,12 +81,13 @@
 	$(OBJDIR)\eap_common.obj \
 	$(OBJDIR)\chap.obj \
 	$(OBJDIR)\eap_methods.obj \
-	$(OBJDIR)\eap_tlv.obj \
 	$(OBJDIR)\eap_md5.obj \
 	$(OBJDIR)\eap_tls.obj \
 	$(OBJDIR)\eap_tls_common.obj \
 	$(OBJDIR)\eap_mschapv2.obj \
+	$(OBJDIR)\mschapv2.obj \
 	$(OBJDIR)\eap_peap.obj \
+	$(OBJDIR)\eap_peap_common.obj \
 	$(OBJDIR)\eap_ttls.obj \
 	$(OBJDIR)\eap_gtc.obj \
 	$(OBJDIR)\eap_otp.obj \

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/scan.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/scan.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/scan.c (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/scan.c Sun Feb 15 19:38:55 2009
@@ -204,7 +204,8 @@
 	if (ret) {
 		wpa_printf(MSG_WARNING, "Failed to initiate AP scan.");
 		wpa_supplicant_req_scan(wpa_s, 10, 0);
-	}
+	} else
+		wpa_s->scan_runs++;
 }
 
 

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/tests/test_sha256.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/tests/test_sha256.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/tests/test_sha256.c (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/tests/test_sha256.c Sun Feb 15 19:38:55 2009
@@ -323,7 +323,8 @@
 	}
 
 	printf("Test IEEE 802.11r KDF\n");
-	sha256_prf("abc", 3, "KDF test", "data", 4, hash, sizeof(hash));
+	sha256_prf((u8 *) "abc", 3, "KDF test", (u8 *) "data", 4,
+		   hash, sizeof(hash));
 	/* TODO: add proper test case for this */
 
 	return errors;

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_cli.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_cli.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_cli.c (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_cli.c Sun Feb 15 19:38:55 2009
@@ -95,6 +95,7 @@
 static char *ctrl_ifname = NULL;
 static const char *pid_file = NULL;
 static const char *action_file = NULL;
+static int ping_interval = 5;
 
 
 static void print_help();
@@ -104,7 +105,8 @@
 {
 	printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] "
 	       "[-a<action file>] \\\n"
-	       "        [-P<pid file>] [-g<global ctrl>]  [command..]\n"
+	       "        [-P<pid file>] [-g<global ctrl>] [-G<ping interval>]  "
+	       "[command..]\n"
 	       "  -h = help (show this usage text)\n"
 	       "  -v = shown version information\n"
 	       "  -a = run in daemon mode executing the action file based on "
@@ -1563,7 +1565,7 @@
 	do {
 		wpa_cli_recv_pending(ctrl_conn, 0, 0);
 #ifndef CONFIG_NATIVE_WINDOWS
-		alarm(1);
+		alarm(ping_interval);
 #endif /* CONFIG_NATIVE_WINDOWS */
 #ifdef CONFIG_READLINE
 		cmd = readline("> ");
@@ -1667,7 +1669,7 @@
 	while (!wpa_cli_quit) {
 		FD_ZERO(&rfds);
 		FD_SET(fd, &rfds);
-		tv.tv_sec = 2;
+		tv.tv_sec = ping_interval;
 		tv.tv_usec = 0;
 		res = select(fd + 1, &rfds, NULL, NULL, &tv);
 		if (res < 0 && errno != EINTR) {
@@ -1721,7 +1723,7 @@
 		wpa_cli_reconnect();
 	if (ctrl_conn)
 		wpa_cli_recv_pending(ctrl_conn, 1, 0);
-	alarm(1);
+	alarm(ping_interval);
 }
 #endif /* CONFIG_NATIVE_WINDOWS */
 
@@ -1794,7 +1796,7 @@
 		return -1;
 
 	for (;;) {
-		c = getopt(argc, argv, "a:Bg:hi:p:P:v");
+		c = getopt(argc, argv, "a:Bg:G:hi:p:P:v");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -1806,6 +1808,9 @@
 			break;
 		case 'g':
 			global = optarg;
+			break;
+		case 'G':
+			ping_interval = atoi(optarg);
 			break;
 		case 'h':
 			usage();

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/main.cpp
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/main.cpp?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/main.cpp (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/main.cpp Sun Feb 15 19:38:55 2009
@@ -18,10 +18,32 @@
 #include <QApplication>
 #include "wpagui.h"
 
+
+class WpaGuiApp : public QApplication
+{
+public:
+	WpaGuiApp(int &argc, char **argv);
+
+	virtual void saveState(QSessionManager &manager);
+
+	WpaGui *w;
+};
+
+WpaGuiApp::WpaGuiApp(int &argc, char **argv) : QApplication(argc, argv)
+{
+}
+
+void WpaGuiApp::saveState(QSessionManager &manager)
+{
+	QApplication::saveState(manager);
+	w->saveState();
+}
+
+
 int main(int argc, char *argv[])
 {
-	QApplication app(argc, argv);
-	WpaGui w;
+	WpaGuiApp app(argc, argv);
+	WpaGui w(&app);
 	int ret;
 
 #ifdef CONFIG_NATIVE_WINDOWS
@@ -32,6 +54,8 @@
 	}
 #endif /* CONFIG_NATIVE_WINDOWS */
 
+	app.w = &w;
+
 	ret = app.exec();
 
 #ifdef CONFIG_NATIVE_WINDOWS

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/wpagui.cpp?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/wpagui.cpp (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/wpagui.cpp Sun Feb 15 19:38:55 2009
@@ -25,6 +25,7 @@
 #include <QMessageBox>
 #include <QCloseEvent>
 #include <QImageReader>
+#include <QSettings>
 
 #include "wpagui.h"
 #include "dirent.h"
@@ -41,8 +42,8 @@
 }
 #endif
 
-WpaGui::WpaGui(QWidget *parent, const char *, Qt::WFlags)
-	: QMainWindow(parent)
+WpaGui::WpaGui(QApplication *_app, QWidget *parent, const char *, Qt::WFlags)
+	: QMainWindow(parent), app(_app)
 {
 	setupUi(this);
 
@@ -71,6 +72,13 @@
 #endif /* CONFIG_NATIVE_WINDOWS */
 
 	(void) statusBar();
+
+	/*
+	 * Disable WPS tab by default; it will be enabled if wpa_supplicant is
+	 * built with WPS support.
+	 */
+	wpsTab->setEnabled(false);
+	wpaguiTab->setTabEnabled(wpaguiTab->indexOf(wpsTab), false);
 
 	connect(fileEventHistoryAction, SIGNAL(triggered()), this,
 		SLOT(eventHistory()));
@@ -137,6 +145,15 @@
 
 	parse_argv();
 
+	if (app->isSessionRestored()) {
+		QSettings settings("wpa_supplicant", "wpa_gui");
+		settings.beginGroup("state");
+		if (app->sessionId().compare(settings.value("session_id").
+					     toString()) == 0)
+			startInTray = settings.value("in_tray").toBool();
+		settings.endGroup();
+	}
+
 	if (QSystemTrayIcon::isSystemTrayAvailable())
 		createTrayIcon(startInTray);
 	else
@@ -406,7 +423,10 @@
 
 		QString res(buf);
 		QStringList types = res.split(QChar(' '));
-		actionWPS->setEnabled(types.contains("WSC"));
+		bool wps = types.contains("WSC");
+		actionWPS->setEnabled(wps);
+		wpsTab->setEnabled(wps);
+		wpaguiTab->setTabEnabled(wpaguiTab->indexOf(wpsTab), wps);
 	}
 
 	return 0;
@@ -1293,6 +1313,7 @@
 
 	if (!trayOnly)
 		show();
+	inTray = trayOnly;
 }
 
 
@@ -1316,10 +1337,13 @@
 	 * custom closeEvent handler take care of children */
 	case QSystemTrayIcon::Trigger:
 		ackTrayIcon = true;
-		if (isVisible())
+		if (isVisible()) {
 			close();
-		else
+			inTray = true;
+		} else {
 			show();
+			inTray = false;
+		}
 		break;
 	case QSystemTrayIcon::MiddleClick:
 		showTrayStatus();
@@ -1663,3 +1687,13 @@
 	add_iface->show();
 	add_iface->exec();
 }
+
+
+void WpaGui::saveState()
+{
+	QSettings settings("wpa_supplicant", "wpa_gui");
+	settings.beginGroup("state");
+	settings.setValue("session_id", app->sessionId());
+	settings.setValue("in_tray", inTray);
+	settings.endGroup();
+}

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/wpagui.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/wpagui.h?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/wpagui.h (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/wpagui.h Sun Feb 15 19:38:55 2009
@@ -28,7 +28,7 @@
 	Q_OBJECT
 
 public:
-	WpaGui(QWidget *parent = 0, const char *name = 0,
+	WpaGui(QApplication *app, QWidget *parent = 0, const char *name = 0,
 	       Qt::WFlags fl = 0);
 	~WpaGui();
 
@@ -40,6 +40,7 @@
 	virtual void disableNetwork(const QString &sel);
 	virtual int getNetworkDisabled(const QString &sel);
 	void setBssFromScan(const QString &bssid);
+	void saveState();
 
 public slots:
 	virtual void parse_argv();
@@ -136,6 +137,9 @@
 	AddInterface *add_iface;
 
 	bool connectedToService;
+
+	QApplication *app;
+	bool inTray;
 };
 
 #endif /* WPAGUI_H */

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/wpagui.ui
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/wpagui.ui?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/wpagui.ui (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_gui-qt4/wpagui.ui Sun Feb 15 19:38:55 2009
@@ -293,7 +293,7 @@
        <attribute name="title" >
         <string>WPS</string>
        </attribute>
-       <layout class="QGridLayout" name="gridLayout" >
+       <layout class="QGridLayout" name="wpsGridLayout" >
         <item row="0" column="0" >
          <widget class="QLabel" name="label_2" >
           <property name="text" >

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_priv.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_priv.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_priv.c (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_priv.c Sun Feb 15 19:38:55 2009
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / privileged helper program
- * Copyright (c) 2007, Jouni Malinen <j at w1.fi>
+ * Copyright (c) 2007-2009, Jouni Malinen <j at w1.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
@@ -575,10 +575,21 @@
 }
 
 
+static void wpa_priv_cmd_set_country(struct wpa_priv_interface *iface,
+				     char *buf)
+{
+	if (iface->drv_priv == NULL || iface->driver->set_country == NULL ||
+	    *buf == '\0')
+		return;
+
+	iface->driver->set_country(iface->drv_priv, buf);
+}
+
+
 static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx)
 {
 	struct wpa_priv_interface *iface = eloop_ctx;
-	char buf[2000];
+	char buf[2000], *pos;
 	void *cmd_buf;
 	size_t cmd_len;
 	int res, cmd;
@@ -648,6 +659,13 @@
 		break;
 	case PRIVSEP_CMD_SET_MODE:
 		wpa_priv_cmd_set_mode(iface, cmd_buf, cmd_len);
+		break;
+	case PRIVSEP_CMD_SET_COUNTRY:
+		pos = cmd_buf;
+		if (pos + cmd_len >= buf + sizeof(buf))
+			break;
+		pos[cmd_len] = '\0';
+		wpa_priv_cmd_set_country(iface, pos);
 		break;
 	}
 }
@@ -1030,6 +1048,53 @@
 		perror("sendmsg(wpas_socket)");
 }
 
+
+#ifdef CONFIG_CLIENT_MLME
+void wpa_supplicant_sta_free_hw_features(struct wpa_hw_modes *hw_features,
+					 size_t num_hw_features)
+{
+	size_t i;
+
+	if (hw_features == NULL)
+		return;
+
+	for (i = 0; i < num_hw_features; i++) {
+		os_free(hw_features[i].channels);
+		os_free(hw_features[i].rates);
+	}
+
+	os_free(hw_features);
+}
+
+
+void wpa_supplicant_sta_rx(void *ctx, const u8 *buf, size_t len,
+			   struct ieee80211_rx_status *rx_status)
+{
+	struct wpa_priv_interface *iface = ctx;
+	struct msghdr msg;
+	struct iovec io[3];
+	int event = PRIVSEP_EVENT_STA_RX;
+
+	wpa_printf(MSG_DEBUG, "STA RX from driver");
+	io[0].iov_base = &event;
+	io[0].iov_len = sizeof(event);
+	io[1].iov_base = (u8 *) rx_status;
+	io[1].iov_len = sizeof(*rx_status);
+	io[2].iov_base = (u8 *) buf;
+	io[2].iov_len = len;
+
+	os_memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = io;
+	msg.msg_iovlen = 3;
+	msg.msg_name = &iface->drv_addr;
+	msg.msg_namelen = sizeof(iface->drv_addr);
+
+	if (sendmsg(iface->fd, &msg, 0) < 0)
+		perror("sendmsg(wpas_socket)");
+}
+#endif /* CONFIG_CLIENT_MLME */
+
+
 static void wpa_priv_terminate(int sig, void *eloop_ctx, void *signal_ctx)
 {
 	wpa_printf(MSG_DEBUG, "wpa_priv termination requested");
@@ -1060,7 +1125,8 @@
 static void usage(void)
 {
 	printf("wpa_priv v" VERSION_STR "\n"
-	       "Copyright (c) 2007, Jouni Malinen <j at w1.fi> and contributors\n"
+	       "Copyright (c) 2007-2009, Jouni Malinen <j at w1.fi> and "
+	       "contributors\n"
 	       "\n"
 	       "usage:\n"
 	       "  wpa_priv [-Bdd] [-P<pid file>] <driver:ifname> "

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_supplicant.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_supplicant.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_supplicant.c (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_supplicant.c Sun Feb 15 19:38:55 2009
@@ -1219,7 +1219,7 @@
  * @wpa_s: Pointer to wpa_supplicant data
  * @reason_code: IEEE 802.11 reason code for the deauthenticate frame
  *
- * This function is used to request %wpa_supplicant to disassociate with the
+ * This function is used to request %wpa_supplicant to deauthenticate from the
  * current AP.
  */
 void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
@@ -2150,7 +2150,7 @@
 
 	eap_peer_unregister_methods();
 
-	for (i = 0; wpa_supplicant_drivers[i]; i++) {
+	for (i = 0; wpa_supplicant_drivers[i] && global->drv_priv; i++) {
 		if (!global->drv_priv[i])
 			continue;
 		wpa_supplicant_drivers[i]->global_deinit(global->drv_priv[i]);

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_supplicant.conf
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_supplicant.conf?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_supplicant.conf (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_supplicant.conf Sun Feb 15 19:38:55 2009
@@ -190,6 +190,13 @@
 # 4-octet operating system version number (hex string)
 #os_version=01020300
 
+# Credential processing
+#   0 = process received credentials internally (default)
+#   1 = do not process received credentials; just pass them over ctrl_iface to
+#	external program(s)
+#   2 = process received credentials internally and pass them over ctrl_iface
+#	to external program(s)
+#wps_cred_processing=0
 
 # network block
 #
@@ -255,8 +262,7 @@
 #
 # key_mgmt: list of accepted authenticated key management protocols
 # WPA-PSK = WPA pre-shared key (this requires 'psk' field)
-# WPA-EAP = WPA using EAP authentication (this can use an external
-#	program, e.g., Xsupplicant, for IEEE 802.1X EAP Authentication
+# WPA-EAP = WPA using EAP authentication
 # IEEE8021X = IEEE 802.1X using EAP authentication and (optionally) dynamically
 #	generated WEP keys
 # NONE = WPA is not used; plaintext or static WEP could be used

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_supplicant_i.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_supplicant_i.h?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_supplicant_i.h (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/wpa_supplicant_i.h Sun Feb 15 19:38:55 2009
@@ -345,6 +345,7 @@
 			     * results without a new scan request; this is used
 			     * to speed up the first association if the driver
 			     * has already available scan results. */
+	int scan_runs; /* number of scan runs since WPS was started */
 
 	struct wpa_client_mlme mlme;
 	int use_client_mlme;
@@ -355,6 +356,8 @@
 	int mic_errors_seen; /* Michael MIC errors with the current PTK */
 
 	struct wps_context *wps;
+	int wps_success; /* WPS success event received */
+	int blacklist_cleared;
 };
 
 

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/wps_supplicant.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/wps_supplicant.c?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/wps_supplicant.c (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/wps_supplicant.c Sun Feb 15 19:38:55 2009
@@ -23,9 +23,12 @@
 #include "eloop.h"
 #include "uuid.h"
 #include "wpa_ctrl.h"
+#include "ctrl_iface_dbus.h"
 #include "eap_common/eap_wsc_common.h"
+#include "blacklist.h"
 #include "wps_supplicant.h"
 
+#define WPS_PIN_SCAN_IGNORE_SEL_REG 3
 
 static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx);
 static void wpas_clear_wps(struct wpa_supplicant *wpa_s);
@@ -33,6 +36,27 @@
 
 int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
 {
+	if (!wpa_s->wps_success &&
+	    wpa_s->current_ssid &&
+	    eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) {
+		const u8 *bssid = wpa_s->bssid;
+		if (is_zero_ether_addr(bssid))
+			bssid = wpa_s->pending_bssid;
+
+		wpa_printf(MSG_DEBUG, "WPS: PIN registration with " MACSTR
+			   " did not succeed - continue trying to find "
+			   "suitable AP", MAC2STR(bssid));
+		wpa_blacklist_add(wpa_s, bssid);
+
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+		wpa_s->reassociate = 1;
+		wpa_supplicant_req_scan(wpa_s,
+					wpa_s->blacklist_cleared ? 5 : 0, 0);
+		wpa_s->blacklist_cleared = 0;
+		return 1;
+	}
+
 	eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
 
 	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid &&
@@ -46,6 +70,15 @@
 		return 1;
 	}
 
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid) {
+		wpa_printf(MSG_DEBUG, "WPS: Registration completed - waiting "
+			   "for external credential processing");
+		wpas_clear_wps(wpa_s);
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+		return 1;
+	}
+
 	return 0;
 }
 
@@ -56,7 +89,36 @@
 	struct wpa_supplicant *wpa_s = ctx;
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
 
-	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CRED_RECEIVED);
+	if ((wpa_s->conf->wps_cred_processing == 1 ||
+	     wpa_s->conf->wps_cred_processing == 2) && cred->cred_attr) {
+		size_t blen = cred->cred_attr_len * 2 + 1;
+		char *buf = os_malloc(blen);
+		if (buf) {
+			wpa_snprintf_hex(buf, blen,
+					 cred->cred_attr, cred->cred_attr_len);
+			wpa_msg(wpa_s, MSG_INFO, "%s%s",
+				WPS_EVENT_CRED_RECEIVED, buf);
+			os_free(buf);
+		}
+		wpa_supplicant_dbus_notify_wps_cred(wpa_s, cred);
+	} else
+		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CRED_RECEIVED);
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
+			cred->cred_attr, cred->cred_attr_len);
+
+	if (wpa_s->conf->wps_cred_processing == 1)
+		return 0;
+
+	if (cred->auth_type != WPS_AUTH_OPEN &&
+	    cred->auth_type != WPS_AUTH_SHARED &&
+	    cred->auth_type != WPS_AUTH_WPAPSK &&
+	    cred->auth_type != WPS_AUTH_WPA2PSK) {
+		wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
+			   "unsupported authentication type %d",
+			   cred->auth_type);
+		return 0;
+	}
 
 	if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
 		wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based "
@@ -87,11 +149,8 @@
 
 	switch (cred->encr_type) {
 	case WPS_ENCR_NONE:
-		ssid->pairwise_cipher = ssid->group_cipher = WPA_CIPHER_NONE;
 		break;
 	case WPS_ENCR_WEP:
-		ssid->pairwise_cipher = ssid->group_cipher =
-			WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104;
 		if (cred->key_len > 0 && cred->key_len <= MAX_WEP_KEY_LEN &&
 		    cred->key_idx < NUM_WEP_KEYS) {
 			os_memcpy(ssid->wep_key[cred->key_idx], cred->key,
@@ -102,11 +161,9 @@
 		break;
 	case WPS_ENCR_TKIP:
 		ssid->pairwise_cipher = WPA_CIPHER_TKIP;
-		ssid->group_cipher = WPA_CIPHER_TKIP;
 		break;
 	case WPS_ENCR_AES:
 		ssid->pairwise_cipher = WPA_CIPHER_CCMP;
-		ssid->group_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP;
 		break;
 	}
 
@@ -200,6 +257,7 @@
 static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s)
 {
 	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
+	wpa_s->wps_success = 1;
 }
 
 
@@ -216,6 +274,8 @@
 		break;
 	case WPS_EV_SUCCESS:
 		wpa_supplicant_wps_event_success(wpa_s);
+		break;
+	case WPS_EV_PWD_AUTH_FAIL:
 		break;
 	}
 }
@@ -257,7 +317,8 @@
 static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
-	wpa_printf(MSG_DEBUG, "WPS: Requested operation timed out");
+	wpa_printf(MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed "
+		   "out");
 	wpas_clear_wps(wpa_s);
 }
 
@@ -329,6 +390,9 @@
 	}
 	wpa_s->disconnected = 0;
 	wpa_s->reassociate = 1;
+	wpa_s->scan_runs = 0;
+	wpa_s->wps_success = 0;
+	wpa_s->blacklist_cleared = 0;
 	wpa_supplicant_req_scan(wpa_s, 0, 0);
 }
 
@@ -516,7 +580,8 @@
 }
 
 
-int wpas_wps_ssid_bss_match(struct wpa_ssid *ssid, struct wpa_scan_res *bss)
+int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
+			    struct wpa_ssid *ssid, struct wpa_scan_res *bss)
 {
 	struct wpabuf *wps_ie;
 
@@ -550,14 +615,24 @@
 			return 0;
 		}
 
+		/*
+		 * Start with WPS APs that advertise active PIN Registrar and
+		 * allow any WPS AP after third scan since some APs do not set
+		 * Selected Registrar attribute properly when using external
+		 * Registrar.
+		 */
 		if (!wps_is_selected_pin_registrar(wps_ie)) {
-			wpa_printf(MSG_DEBUG, "   skip - WPS AP "
-				   "without active PIN Registrar");
-			wpabuf_free(wps_ie);
-			return 0;
-		}
-		wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
-			   "(Active PIN)");
+			if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) {
+				wpa_printf(MSG_DEBUG, "   skip - WPS AP "
+					   "without active PIN Registrar");
+				wpabuf_free(wps_ie);
+				return 0;
+			}
+			wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
+		} else {
+			wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
+				   "(Active PIN)");
+		}
 		wpabuf_free(wps_ie);
 		return 1;
 	}
@@ -572,7 +647,8 @@
 }
 
 
-int wpas_wps_ssid_wildcard_ok(struct wpa_ssid *ssid,
+int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid,
 			      struct wpa_scan_res *bss)
 {
 	struct wpabuf *wps_ie = NULL;
@@ -586,7 +662,9 @@
 		}
 	} else if (eap_is_wps_pin_enrollee(&ssid->eap)) {
 		wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
-		if (wps_ie && wps_is_selected_pin_registrar(wps_ie)) {
+		if (wps_ie &&
+		    (wps_is_selected_pin_registrar(wps_ie) ||
+		     wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) {
 			/* allow wildcard SSID for WPS PIN */
 			ret = 1;
 		}

Modified: wpasupplicant/branches/upstream/current/wpa_supplicant/wps_supplicant.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/wpa_supplicant/wps_supplicant.h?rev=1324&op=diff
==============================================================================
--- wpasupplicant/branches/upstream/current/wpa_supplicant/wps_supplicant.h (original)
+++ wpasupplicant/branches/upstream/current/wpa_supplicant/wps_supplicant.h Sun Feb 15 19:38:55 2009
@@ -29,8 +29,10 @@
 		       const char *pin);
 int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
 		       const char *pin);
-int wpas_wps_ssid_bss_match(struct wpa_ssid *ssid, struct wpa_scan_res *bss);
-int wpas_wps_ssid_wildcard_ok(struct wpa_ssid *ssid, struct wpa_scan_res *bss);
+int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
+			    struct wpa_ssid *ssid, struct wpa_scan_res *bss);
+int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid, struct wpa_scan_res *bss);
 int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
 			      struct wpa_scan_res *selected,
 			      struct wpa_ssid *ssid);
@@ -58,13 +60,15 @@
 	return 0;
 }
 
-static inline int wpas_wps_ssid_bss_match(struct wpa_ssid *ssid,
+static inline int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
+					  struct wpa_ssid *ssid,
 					  struct wpa_scan_res *bss)
 {
 	return -1;
 }
 
-static inline int wpas_wps_ssid_wildcard_ok(struct wpa_ssid *ssid,
+static inline int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
+					    struct wpa_ssid *ssid,
 					    struct wpa_scan_res *bss)
 {
 	return 0;




More information about the Pkg-wpa-devel mailing list