All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 01/13] frame-xchg: Fix potential use after free
@ 2020-07-31  1:31 Andrew Zaborowski
  2020-07-31  1:31 ` [PATCH 02/13] frame-xchg: Improve search for current frame in MLME notify Andrew Zaborowski
                   ` (12 more replies)
  0 siblings, 13 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-07-31  1:31 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 1263 bytes --]

Check if the frame callback has cancelled the frame_xchg before
attempting to free it.
---
I'm adding a frame_xchg_match_ptr here and I think I might have
used a similar function somewhere else.  I was wondering if we
want to add a pointer compare function in util.c or accept NULL
as match function in l_queue_find() to mean match by data pointer.
---
 src/frame-xchg.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/frame-xchg.c b/src/frame-xchg.c
index 3c1fc279..dfe08f6a 100644
--- a/src/frame-xchg.c
+++ b/src/frame-xchg.c
@@ -975,6 +975,11 @@ static bool frame_xchg_tx_retry(struct wiphy_radio_work_item *item)
 	return false;
 }
 
+static bool frame_xchg_match_ptr(const void *a, const void *b)
+{
+	return a == b;
+}
+
 static bool frame_xchg_resp_handle(const struct mmpdu_header *mpdu,
 					const void *body, size_t body_len,
 					int rssi, void *user_data)
@@ -1013,6 +1018,9 @@ static bool frame_xchg_resp_handle(const struct mmpdu_header *mpdu,
 
 		done = watch->cb(mpdu, body, body_len, rssi, fx->user_data);
 
+		if (!l_queue_find(frame_xchgs, frame_xchg_match_ptr, fx))
+			return true;
+
 		if (done) {
 			/* NULL callback here since the caller is done */
 			fx->cb = NULL;
-- 
2.25.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 02/13] frame-xchg: Improve search for current frame in MLME notify
  2020-07-31  1:31 [PATCH 01/13] frame-xchg: Fix potential use after free Andrew Zaborowski
@ 2020-07-31  1:31 ` Andrew Zaborowski
  2020-07-31  1:31 ` [PATCH 03/13] frame-xchg: Re-add frame_xchg_stop Andrew Zaborowski
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-07-31  1:31 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 1346 bytes --]

Since there may now be multiple frames-xchg record for each wdev, when
we receive the TX Status event, make sure we find the record who's radio
work has started, as indicated by fx->retry_cnt > 0.  Otherwise we're
relying on the ordering of the frames in the "frame_xchgs" queue and
constant priority.
---
 src/frame-xchg.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/frame-xchg.c b/src/frame-xchg.c
index dfe08f6a..8abcc8a7 100644
--- a/src/frame-xchg.c
+++ b/src/frame-xchg.c
@@ -1057,12 +1057,12 @@ static void frame_xchg_resp_cb(const struct mmpdu_header *mpdu,
 	frame_xchg_resp_handle(mpdu, body, body_len, rssi, user_data);
 }
 
-static bool frame_xchg_match(const void *a, const void *b)
+static bool frame_xchg_match_running(const void *a, const void *b)
 {
 	const struct frame_xchg_data *fx = a;
 	const uint64_t *wdev_id = b;
 
-	return fx->wdev_id == *wdev_id;
+	return fx->retry_cnt > 0 && fx->wdev_id == *wdev_id;
 }
 
 /*
@@ -1225,7 +1225,8 @@ static void frame_xchg_mlme_notify(struct l_genl_msg *msg, void *user_data)
 
 		l_debug("Received %s", ack ? "an ACK" : "no ACK");
 
-		fx = l_queue_find(frame_xchgs, frame_xchg_match, &wdev_id);
+		fx = l_queue_find(frame_xchgs, frame_xchg_match_running,
+					&wdev_id);
 		if (!fx)
 			return;
 
-- 
2.25.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 03/13] frame-xchg: Re-add frame_xchg_stop
  2020-07-31  1:31 [PATCH 01/13] frame-xchg: Fix potential use after free Andrew Zaborowski
  2020-07-31  1:31 ` [PATCH 02/13] frame-xchg: Improve search for current frame in MLME notify Andrew Zaborowski
@ 2020-07-31  1:31 ` Andrew Zaborowski
  2020-07-31  1:31 ` [PATCH 04/13] station: Comment/whitespace fix Andrew Zaborowski
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-07-31  1:31 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 3632 bytes --]

In 98cf2bf3ece070bfe7ff45670b95d24b34bf3e13 frame_xchg_stop was removed
and its use in p2p.c was changed to frame_xchg_cancel with the slight
complication that the ID returned by frame_xchg_start had do be stored.
Re-add frame_xchg_stop, (renamed as frame_xchg_stop_wdev) to simplify
this bit in p2p.c.
---
 src/frame-xchg.c | 18 ++++++++++++++++++
 src/frame-xchg.h |  1 +
 src/p2p.c        | 17 ++---------------
 3 files changed, 21 insertions(+), 15 deletions(-)

diff --git a/src/frame-xchg.c b/src/frame-xchg.c
index 8abcc8a7..2ea8504a 100644
--- a/src/frame-xchg.c
+++ b/src/frame-xchg.c
@@ -1188,6 +1188,24 @@ uint32_t frame_xchg_startv(uint64_t wdev_id, struct iovec *frame, uint32_t freq,
 					&fx->work, 0, &work_ops);
 }
 
+static bool frame_xchg_cancel_by_wdev(void *data, void *user_data)
+{
+	struct frame_xchg_data *fx = data;
+	const uint64_t *wdev_id = user_data;
+
+	if (fx->wdev_id != *wdev_id)
+		return false;
+
+	wiphy_radio_work_done(wiphy_find_by_wdev(fx->wdev_id), fx->work.id);
+	return true;
+}
+
+void frame_xchg_stop_wdev(uint64_t wdev_id)
+{
+	l_queue_foreach_remove(frame_xchgs, frame_xchg_cancel_by_wdev,
+				&wdev_id);
+}
+
 static bool frame_xchg_match_id(const void *a, const void *b)
 {
 	const struct frame_xchg_data *fx = a;
diff --git a/src/frame-xchg.h b/src/frame-xchg.h
index ba4dc61a..e3748538 100644
--- a/src/frame-xchg.h
+++ b/src/frame-xchg.h
@@ -53,4 +53,5 @@ uint32_t frame_xchg_startv(uint64_t wdev_id, struct iovec *frame, uint32_t freq,
 			unsigned int retries_on_ack, uint32_t group_id,
 			frame_xchg_cb_t cb, void *user_data,
 			frame_xchg_destroy_func_t destroy, va_list resp_args);
+void frame_xchg_stop_wdev(uint64_t wdev_id);
 void frame_xchg_cancel(uint32_t id);
diff --git a/src/p2p.c b/src/p2p.c
index 58027fc6..e327cba9 100644
--- a/src/p2p.c
+++ b/src/p2p.c
@@ -68,7 +68,6 @@ struct p2p_device {
 	uint32_t start_stop_cmd_id;
 	l_dbus_property_complete_cb_t pending_complete;
 	struct l_dbus_message *pending_message;
-	uint32_t xchg_id;
 
 	uint8_t listen_country[3];
 	uint8_t listen_oper_class;
@@ -471,11 +470,7 @@ static void p2p_connection_reset(struct p2p_device *dev)
 	netdev_watch_remove(dev->conn_netdev_watch_id);
 
 	frame_watch_group_remove(dev->wdev_id, FRAME_GROUP_CONNECT);
-
-	if (dev->xchg_id) {
-		frame_xchg_cancel(dev->xchg_id);
-		dev->xchg_id = 0;
-	}
+	frame_xchg_stop_wdev(dev->wdev_id);
 
 	if (!dev->enabled || (dev->enabled && dev->start_stop_cmd_id)) {
 		/*
@@ -557,7 +552,7 @@ static void p2p_peer_frame_xchg(struct p2p_peer *peer, struct iovec *tx_body,
 
 	va_start(args, cb);
 
-	dev->xchg_id = frame_xchg_startv(dev->wdev_id, frame, freq,
+	frame_xchg_startv(dev->wdev_id, frame, freq,
 				retry_interval, resp_timeout, retries_on_ack,
 				group_id, cb, dev, NULL, args);
 	va_end(args);
@@ -1313,17 +1308,11 @@ static void p2p_go_negotiation_resp_done(int error, void *user_data)
 	else
 		l_error("No GO Negotiation Confirmation frame received");
 
-	dev->xchg_id = 0;
-
 	p2p_connect_failed(dev);
 }
 
 static void p2p_go_negotiation_resp_err_done(int error, void *user_data)
 {
-	struct p2p_device *dev = user_data;
-
-	dev->xchg_id = 0;
-
 	if (error)
 		l_error("Sending the GO Negotiation Response failed: %s (%i)",
 			strerror(-error), -error);
@@ -1573,8 +1562,6 @@ static bool p2p_go_negotiation_confirm_cb(const struct mmpdu_header *mpdu,
 
 	l_debug("");
 
-	dev->xchg_id = 0;
-
 	if (body_len < 8) {
 		l_error("GO Negotiation Confirmation frame too short");
 		p2p_connect_failed(dev);
-- 
2.25.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 04/13] station: Comment/whitespace fix
  2020-07-31  1:31 [PATCH 01/13] frame-xchg: Fix potential use after free Andrew Zaborowski
  2020-07-31  1:31 ` [PATCH 02/13] frame-xchg: Improve search for current frame in MLME notify Andrew Zaborowski
  2020-07-31  1:31 ` [PATCH 03/13] frame-xchg: Re-add frame_xchg_stop Andrew Zaborowski
@ 2020-07-31  1:31 ` Andrew Zaborowski
  2020-07-31  1:31 ` [PATCH 05/13] p2p: Retry connect on "Previous authentication not valid" Andrew Zaborowski
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-07-31  1:31 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 946 bytes --]

---
 src/station.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/station.c b/src/station.c
index 0095d6c8..42d60942 100644
--- a/src/station.c
+++ b/src/station.c
@@ -2282,7 +2282,7 @@ static bool station_retry_with_reason(struct station *station,
 	return station_try_next_bss(station);
 }
 
-/* A bit more consise for trying to fit these into 80 characters */
+/* A bit more concise for trying to fit these into 80 characters */
 #define IS_TEMPORARY_STATUS(code) \
 	((code) == MMPDU_STATUS_CODE_DENIED_UNSUFFICIENT_BANDWIDTH || \
 	(code) == MMPDU_STATUS_CODE_DENIED_POOR_CHAN_CONDITIONS || \
@@ -2685,7 +2685,6 @@ static void station_disconnect_cb(struct netdev *netdev, bool success,
 			reply = dbus_error_failed(station->disconnect_pending);
 
 		dbus_pending_reply(&station->disconnect_pending, reply);
-
 	}
 
 	station_enter_state(station, STATION_STATE_DISCONNECTED);
-- 
2.25.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 05/13] p2p: Retry connect on "Previous authentication not valid"
  2020-07-31  1:31 [PATCH 01/13] frame-xchg: Fix potential use after free Andrew Zaborowski
                   ` (2 preceding siblings ...)
  2020-07-31  1:31 ` [PATCH 04/13] station: Comment/whitespace fix Andrew Zaborowski
@ 2020-07-31  1:31 ` Andrew Zaborowski
  2020-07-31  1:31 ` [PATCH 06/13] p2p: Add a null-check before use Andrew Zaborowski
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-07-31  1:31 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 11476 bytes --]

With some WFD devices we occasionally get a Disconnect before or during
the DHCP setup on the first connection attempt to a newly formeg group,
with the reason code MMPDU_REASON_CODE_PREV_AUTH_NOT_VALID.  Retrying a
a few times makes the connections consistently successful.  Some
conditions are simplified/update in this patch because
conn_dhcp_timeout now implies conn_wsc_bss, and both imply
conn_retry_count.
---
 src/p2p.c | 248 ++++++++++++++++++++++++++++++++----------------------
 1 file changed, 147 insertions(+), 101 deletions(-)

diff --git a/src/p2p.c b/src/p2p.c
index e327cba9..aaecb8ee 100644
--- a/src/p2p.c
+++ b/src/p2p.c
@@ -97,6 +97,8 @@ struct p2p_device {
 	struct netconfig *conn_netconfig;
 	struct l_timeout *conn_dhcp_timeout;
 	struct p2p_wfd_properties *conn_own_wfd;
+	uint8_t conn_psk[32];
+	int conn_retry_count;
 
 	struct l_timeout *config_timeout;
 	unsigned long go_config_delay;
@@ -206,8 +208,7 @@ static void p2p_discovery_user_free(void *data)
 static inline bool p2p_peer_operational(struct p2p_peer *peer)
 {
 	return peer && peer->dev->conn_netdev && !peer->dev->conn_wsc_bss &&
-		!peer->dev->conn_dhcp_timeout && !peer->wsc.pending_connect &&
-		!peer->dev->disconnecting;
+		!peer->wsc.pending_connect && !peer->dev->disconnecting;
 }
 
 static bool p2p_peer_match(const void *a, const void *b)
@@ -488,6 +489,9 @@ static void p2p_connection_reset(struct p2p_device *dev)
 		p2p_own_wfd->available = true;
 	}
 
+	explicit_bzero(dev->conn_psk, 32);
+	dev->conn_retry_count = 0;
+
 	if (dev->enabled && !dev->start_stop_cmd_id &&
 			!l_queue_isempty(dev->discovery_users))
 		p2p_device_discovery_start(dev);
@@ -596,31 +600,40 @@ static const struct frame_xchg_prefix p2p_frame_pd_resp = {
 	.len = 7,
 };
 
+static void p2p_peer_connect_done(struct p2p_device *dev)
+{
+	struct p2p_peer *peer = dev->conn_peer;
+
+	/* We can free anything potentially needed for a retry */
+	scan_bss_free(dev->conn_wsc_bss);
+	dev->conn_wsc_bss = NULL;
+	explicit_bzero(dev->conn_psk, 32);
+
+	dbus_pending_reply(&peer->wsc.pending_connect,
+				l_dbus_message_new_method_return(
+						peer->wsc.pending_connect));
+	l_dbus_property_changed(dbus_get_bus(),
+				p2p_peer_get_path(dev->conn_peer),
+				IWD_P2P_PEER_INTERFACE, "Connected");
+	l_dbus_property_changed(dbus_get_bus(),
+				p2p_peer_get_path(dev->conn_peer),
+				IWD_P2P_PEER_INTERFACE,
+				"ConnectedInterface");
+	l_dbus_property_changed(dbus_get_bus(),
+				p2p_peer_get_path(dev->conn_peer),
+				IWD_P2P_PEER_INTERFACE,
+				"ConnectedIP");
+}
+
 static void p2p_netconfig_event_handler(enum netconfig_event event,
 					void *user_data)
 {
 	struct p2p_device *dev = user_data;
-	struct p2p_peer *peer = dev->conn_peer;
 
 	switch (event) {
 	case NETCONFIG_EVENT_CONNECTED:
 		l_timeout_remove(dev->conn_dhcp_timeout);
-
-		dbus_pending_reply(&peer->wsc.pending_connect,
-					l_dbus_message_new_method_return(
-						peer->wsc.pending_connect));
-		l_dbus_property_changed(dbus_get_bus(),
-					p2p_peer_get_path(dev->conn_peer),
-					IWD_P2P_PEER_INTERFACE, "Connected");
-		l_dbus_property_changed(dbus_get_bus(),
-					p2p_peer_get_path(dev->conn_peer),
-					IWD_P2P_PEER_INTERFACE,
-					"ConnectedInterface");
-		l_dbus_property_changed(dbus_get_bus(),
-					p2p_peer_get_path(dev->conn_peer),
-					IWD_P2P_PEER_INTERFACE,
-					"ConnectedIP");
-
+		p2p_peer_connect_done(dev);
 		break;
 	default:
 		l_error("station: Unsupported netconfig event: %d.", event);
@@ -654,10 +667,12 @@ static void p2p_start_dhcp(struct p2p_device *dev)
 					&dhcp_timeout_val))
 		dhcp_timeout_val = 10;	/* 10s default */
 
-	dev->conn_netconfig = netconfig_new(ifindex);
 	if (!dev->conn_netconfig) {
-		p2p_connect_failed(dev);
-		return;
+		dev->conn_netconfig = netconfig_new(ifindex);
+		if (!dev->conn_netconfig) {
+			p2p_connect_failed(dev);
+			return;
+		}
 	}
 
 	netconfig_configure(dev->conn_netconfig, p2p_dhcp_settings,
@@ -711,13 +726,35 @@ static void p2p_netdev_connect_cb(struct netdev *netdev,
 	}
 }
 
+static void p2p_try_connect_group(struct p2p_device *dev);
+
 static void p2p_netdev_event(struct netdev *netdev, enum netdev_event event,
 				void *event_data, void *user_data)
 {
 	struct p2p_device *dev = user_data;
+	const uint16_t *reason_code;
 
 	switch (event) {
 	case NETDEV_EVENT_DISCONNECT_BY_AP:
+		reason_code = event_data;
+
+		if (*reason_code == MMPDU_REASON_CODE_PREV_AUTH_NOT_VALID &&
+				dev->conn_wsc_bss &&
+				dev->conn_retry_count < 5) {
+			/*
+			 * Sometimes a retry helps here, may be that we haven't
+			 * waited long enough for the GO setup.
+			 */
+			l_timeout_remove(dev->conn_dhcp_timeout);
+
+			if (dev->conn_netconfig)
+				netconfig_reset(dev->conn_netconfig);
+
+			p2p_try_connect_group(dev);
+			break;
+		}
+
+		/* Fall through. */
 	case NETDEV_EVENT_DISCONNECT_BY_SME:
 		/*
 		 * We may get a DISCONNECT_BY_SME as a result of a
@@ -770,72 +807,19 @@ static void p2p_handshake_event(struct handshake_state *hs,
 	va_end(args);
 }
 
-static void p2p_peer_provision_done(int err, struct wsc_credentials_info *creds,
-					unsigned int n_creds, void *user_data)
+static void p2p_try_connect_group(struct p2p_device *dev)
 {
-	struct p2p_peer *peer = user_data;
-	struct p2p_device *dev = peer->dev;
 	struct scan_bss *bss = dev->conn_wsc_bss;
 	struct handshake_state *hs = NULL;
 	struct iovec ie_iov[16];
 	int ie_num = 0;
-	int r = -EOPNOTSUPP;
+	int r;
 	struct p2p_association_req info = {};
 	struct ie_rsn_info bss_info = {};
 	struct ie_rsn_info rsn_info = {};
 	uint8_t rsne_buf[256];
 	uint8_t wfd_ie[32];
 
-	l_debug("err=%i n_creds=%u", err, n_creds);
-
-	dev->conn_wsc_bss = NULL;
-	dev->conn_enrollee = NULL;
-
-	l_timeout_remove(dev->config_timeout);
-	l_timeout_remove(dev->go_neg_req_timeout);
-
-	if (err < 0) {
-		if (err == -ECANCELED && peer->wsc.pending_cancel) {
-			dbus_pending_reply(&peer->wsc.pending_cancel,
-				l_dbus_message_new_method_return(
-						peer->wsc.pending_cancel));
-
-			p2p_connection_reset(dev);
-		} else
-			p2p_connect_failed(dev);
-
-		goto done;
-	}
-
-	if (strlen(creds[0].ssid) != bss->ssid_len ||
-			memcmp(creds[0].ssid, bss->ssid, bss->ssid_len)) {
-		l_error("Unsupported: the SSID from the P2P peer's WSC "
-			"credentials doesn't match the SSID from the "
-			"Probe Response IEs");
-		goto not_supported;
-	}
-
-	/*
-	 * Apparently some implementations send the intended client's address
-	 * here (i.e. our), and some send the target BSS's (their own).
-	 */
-	if (memcmp(creds[0].addr, netdev_get_address(dev->conn_netdev), 6) &&
-			memcmp(creds[0].addr, bss->addr, 6)) {
-		char addr1[32], addr2[32];
-
-		l_strlcpy(addr1, util_address_to_string(creds[0].addr),
-				sizeof(addr1));
-		l_strlcpy(addr2, util_address_to_string(
-					netdev_get_address(dev->conn_netdev)),
-				sizeof(addr2));
-		l_error("Error: WSC credentials are not for our client "
-			"interface (%s vs. %s)", addr1, addr2);
-		goto error;
-	}
-
-	if (!bss->rsne || creds[0].security != SECURITY_PSK)
-		goto not_supported;
-
 	info.capability = dev->capability;
 	info.device_info = dev->device_info;
 
@@ -879,39 +863,100 @@ static void p2p_peer_provision_done(int err, struct wsc_credentials_info *creds,
 
 	handshake_state_set_event_func(hs, p2p_handshake_event, dev);
 	handshake_state_set_ssid(hs, bss->ssid, bss->ssid_len);
-
-	if (creds[0].has_passphrase) {
-		uint8_t psk[32];
-
-		if (crypto_psk_from_passphrase(creds[0].passphrase, bss->ssid,
-						bss->ssid_len, psk) < 0)
-			goto error;
-
-		handshake_state_set_pmk(hs, psk, 32);
-	} else
-		handshake_state_set_pmk(hs, creds[0].psk, 32);
+	handshake_state_set_pmk(hs, dev->conn_psk, 32);
 
 	r = netdev_connect(dev->conn_netdev, bss, hs, ie_iov, ie_num,
 				p2p_netdev_event, p2p_netdev_connect_cb, dev);
-	if (r == 0)
-		goto done;
+	if (r < 0) {
+		l_error("netdev_connect error: %s (%i)", strerror(-r), -r);
+		goto error;
+	}
 
-	l_error("netdev_connect error: %s (%i)", strerror(-err), -err);
+	dev->conn_retry_count++;
+
+done:
+	l_free(ie_iov[0].iov_base);
+	return;
 
 error:
 not_supported:
-	if (r < 0) {
-		if (hs)
-			handshake_state_free(hs);
+	if (hs)
+		handshake_state_free(hs);
 
-		p2p_connect_failed(dev);
+	p2p_connect_failed(dev);
+	goto done;
+}
+
+static void p2p_peer_provision_done(int err, struct wsc_credentials_info *creds,
+					unsigned int n_creds, void *user_data)
+{
+	struct p2p_peer *peer = user_data;
+	struct p2p_device *dev = peer->dev;
+	struct scan_bss *bss = dev->conn_wsc_bss;
+
+	l_debug("err=%i n_creds=%u", err, n_creds);
+
+	dev->conn_enrollee = NULL;
+
+	l_timeout_remove(dev->config_timeout);
+	l_timeout_remove(dev->go_neg_req_timeout);
+
+	if (err < 0) {
+		if (err == -ECANCELED && peer->wsc.pending_cancel) {
+			dbus_pending_reply(&peer->wsc.pending_cancel,
+				l_dbus_message_new_method_return(
+						peer->wsc.pending_cancel));
+
+			p2p_connection_reset(dev);
+			return;
+		} else
+			goto error;
 	}
 
-done:
-	if (ie_num)
-		l_free(ie_iov[0].iov_base);
+	if (strlen(creds[0].ssid) != bss->ssid_len ||
+			memcmp(creds[0].ssid, bss->ssid, bss->ssid_len)) {
+		l_error("Unsupported: the SSID from the P2P peer's WSC "
+			"credentials doesn't match the SSID from the "
+			"Probe Response IEs");
+		goto not_supported;
+	}
+
+	/*
+	 * Apparently some implementations send the intended client's address
+	 * here (i.e. our), and some send the target BSS's (their own).
+	 */
+	if (memcmp(creds[0].addr, netdev_get_address(dev->conn_netdev), 6) &&
+			memcmp(creds[0].addr, bss->addr, 6)) {
+		char addr1[32], addr2[32];
+
+		l_strlcpy(addr1, util_address_to_string(creds[0].addr),
+				sizeof(addr1));
+		l_strlcpy(addr2, util_address_to_string(
+					netdev_get_address(dev->conn_netdev)),
+				sizeof(addr2));
+		l_error("Error: WSC credentials are not for our client "
+			"interface (%s vs. %s)", addr1, addr2);
+		goto error;
+	}
+
+	if (!bss->rsne || creds[0].security != SECURITY_PSK)
+		goto not_supported;
+
+	if (creds[0].has_passphrase) {
+		if (crypto_psk_from_passphrase(creds[0].passphrase, bss->ssid,
+						bss->ssid_len,
+						dev->conn_psk) < 0)
+			goto error;
+	} else
+		memcpy(dev->conn_psk, creds[0].psk, 32);
 
-	scan_bss_free(bss);
+	dev->conn_retry_count = 0;
+	p2p_try_connect_group(dev);
+	return;
+
+error:
+not_supported:
+	p2p_connect_failed(dev);
 }
 
 static void p2p_provision_connect(struct p2p_device *dev)
@@ -963,7 +1008,8 @@ static void p2p_device_netdev_notify(struct netdev *netdev,
 	switch (event) {
 	case NETDEV_WATCH_EVENT_UP:
 	case NETDEV_WATCH_EVENT_NEW:
-		if (!dev->conn_wsc_bss || dev->conn_enrollee ||
+		/* Ignore the event if we're already connecting/connected */
+		if (dev->conn_enrollee || dev->conn_retry_count ||
 				!netdev_get_is_up(netdev))
 			break;
 
@@ -2459,7 +2505,7 @@ static void p2p_peer_disconnect(struct p2p_peer *peer)
 		return;
 	}
 
-	if (dev->conn_netdev && !dev->conn_wsc_bss) {
+	if (dev->conn_netdev && dev->conn_retry_count) {
 		/* Note: in theory we need to add the P2P IEs here too */
 		if (netdev_disconnect(dev->conn_netdev, p2p_peer_disconnect_cb,
 					peer) == 0)
-- 
2.25.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 06/13] p2p: Add a null-check before use
  2020-07-31  1:31 [PATCH 01/13] frame-xchg: Fix potential use after free Andrew Zaborowski
                   ` (3 preceding siblings ...)
  2020-07-31  1:31 ` [PATCH 05/13] p2p: Retry connect on "Previous authentication not valid" Andrew Zaborowski
@ 2020-07-31  1:31 ` Andrew Zaborowski
  2020-07-31  1:31 ` [PATCH 07/13] p2p: Set p2p_own_wfd->available by default Andrew Zaborowski
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-07-31  1:31 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 669 bytes --]

p2p_connection_reset may be called as a result of a WFD service
unregistering and p2p_own_wfd is going to be NULL, don't update
p2p_own_wfd->available in this case.
---
 src/p2p.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/p2p.c b/src/p2p.c
index aaecb8ee..06416bd8 100644
--- a/src/p2p.c
+++ b/src/p2p.c
@@ -486,7 +486,9 @@ static void p2p_connection_reset(struct p2p_device *dev)
 	if (dev->conn_own_wfd) {
 		l_free(dev->conn_own_wfd);
 		dev->conn_own_wfd = NULL;
-		p2p_own_wfd->available = true;
+
+		if (p2p_own_wfd)
+			p2p_own_wfd->available = true;
 	}
 
 	explicit_bzero(dev->conn_psk, 32);
-- 
2.25.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 07/13] p2p: Set p2p_own_wfd->available by default
  2020-07-31  1:31 [PATCH 01/13] frame-xchg: Fix potential use after free Andrew Zaborowski
                   ` (4 preceding siblings ...)
  2020-07-31  1:31 ` [PATCH 06/13] p2p: Add a null-check before use Andrew Zaborowski
@ 2020-07-31  1:31 ` Andrew Zaborowski
  2020-07-31  1:31 ` [PATCH 08/13] p2p: Use a longer DHCP timeout Andrew Zaborowski
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-07-31  1:31 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 701 bytes --]

We need to set this flag true when the WFD service is first registered
as there's no WFD session and we're available for a new session.
---
 src/p2p.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/p2p.c b/src/p2p.c
index 06416bd8..da98f2a7 100644
--- a/src/p2p.c
+++ b/src/p2p.c
@@ -4327,6 +4327,9 @@ static struct l_dbus_message *p2p_wfd_register(struct l_dbus *dbus,
 	if (p2p_own_wfd)
 		return dbus_error_already_exists(message);
 
+	/* Available for WFD Session by default */
+	props.available = true;
+
 	p2p_wfd_disconnect_watch = l_dbus_add_disconnect_watch(dbus,
 					l_dbus_message_get_sender(message),
 					p2p_wfd_disconnect_watch_cb, NULL,
-- 
2.25.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 08/13] p2p: Use a longer DHCP timeout
  2020-07-31  1:31 [PATCH 01/13] frame-xchg: Fix potential use after free Andrew Zaborowski
                   ` (5 preceding siblings ...)
  2020-07-31  1:31 ` [PATCH 07/13] p2p: Set p2p_own_wfd->available by default Andrew Zaborowski
@ 2020-07-31  1:31 ` Andrew Zaborowski
  2020-07-31  1:31 ` [PATCH 09/13] wfd-source: Print call trace on error Andrew Zaborowski
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-07-31  1:31 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 768 bytes --]

With some devices the 10 seconds are not enough for the P2P Group Owner
to give us an address but I think we still want to use a timeout as
short as possible so that the user doesn't wait too long if the
connection isn't working.
---
 src/p2p.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p2p.c b/src/p2p.c
index da98f2a7..3b38f738 100644
--- a/src/p2p.c
+++ b/src/p2p.c
@@ -667,7 +667,7 @@ static void p2p_start_dhcp(struct p2p_device *dev)
 
 	if (!l_settings_get_uint(iwd_get_config(), "P2P", "DHCPTimeout",
 					&dhcp_timeout_val))
-		dhcp_timeout_val = 10;	/* 10s default */
+		dhcp_timeout_val = 20;	/* 20s default */
 
 	if (!dev->conn_netconfig) {
 		dev->conn_netconfig = netconfig_new(ifindex);
-- 
2.25.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 09/13] wfd-source: Print call trace on error
  2020-07-31  1:31 [PATCH 01/13] frame-xchg: Fix potential use after free Andrew Zaborowski
                   ` (6 preceding siblings ...)
  2020-07-31  1:31 ` [PATCH 08/13] p2p: Use a longer DHCP timeout Andrew Zaborowski
@ 2020-07-31  1:31 ` Andrew Zaborowski
  2020-07-31  1:31 ` [PATCH 10/13] wfd-source: Support wfd-idr-request Andrew Zaborowski
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-07-31  1:31 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 1179 bytes --]

---
 test/wfd-source | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/test/wfd-source b/test/wfd-source
index 557093a9..c9ad3c34 100755
--- a/test/wfd-source
+++ b/test/wfd-source
@@ -15,6 +15,7 @@ import collections
 import collections.abc
 import random
 import dataclasses
+import traceback
 
 import gi
 gi.require_version('GLib', '2.0')
@@ -1139,8 +1140,13 @@ class WFDSource(Gtk.Window):
 
         def on_rtsp_error(excp):
             self.disconnect_peer(dev_path, path)
+            tb = ''
+            try:
+                tb = '\n\nDebug info: ' + traceback.format_exc()
+            except:
+                pass
             dialog = Gtk.MessageDialog(parent=self, message_type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK, text='Negotiation failed')
-            dialog.format_secondary_text('RTSP error when talking to ' + self.objects[path][PEER_IF]['Name'] + ': ' + repr(excp))
+            dialog.format_secondary_text('RTSP error when talking to ' + self.objects[path][PEER_IF]['Name'] + ': ' + repr(excp) + tb)
             dialog.show()
 
             def on_ok(response, *args):
-- 
2.25.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 10/13] wfd-source: Support wfd-idr-request
  2020-07-31  1:31 [PATCH 01/13] frame-xchg: Fix potential use after free Andrew Zaborowski
                   ` (7 preceding siblings ...)
  2020-07-31  1:31 ` [PATCH 09/13] wfd-source: Print call trace on error Andrew Zaborowski
@ 2020-07-31  1:31 ` Andrew Zaborowski
  2020-07-31  1:31 ` [PATCH 11/13] wfd-source: Display some stream properties Andrew Zaborowski
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-07-31  1:31 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 3670 bytes --]

---
 test/wfd-source | 38 ++++++++++++++++++++++++++++----------
 1 file changed, 28 insertions(+), 10 deletions(-)

diff --git a/test/wfd-source b/test/wfd-source
index c9ad3c34..09b47a27 100755
--- a/test/wfd-source
+++ b/test/wfd-source
@@ -21,7 +21,7 @@ import gi
 gi.require_version('GLib', '2.0')
 gi.require_version('Gst', '1.0')
 gi.require_version('Gtk', '3.0')
-from gi.repository import GLib, Gst, Gtk, Gdk, Pango
+from gi.repository import GLib, Gst, Gtk, Gdk, Pango, GObject
 
 class WFDRTSPServer:
     class RTSPException(Exception):
@@ -325,7 +325,21 @@ class WFDRTSPServer:
         elif method == 'TEARDOWN' and target != self.session_stream_url:
             self.error('Unknown target "' + target + '" in TEARDOWN request')
         elif method == 'SET_PARAMETER':
-            pass
+            params = []
+            names = []
+            for line in content.split(b'\r\n'):
+                param = (line.decode('utf8').strip(), None)
+                if not param[0]:
+                    continue
+                if ':' in param[0]:
+                    k, v = param[0].split(':', 1)
+                    param = (k.strip(), v.strip())
+                if param[0] in names:
+                    self.error('Duplicate key "' + param[0] + '" in SET_PARAMETER response')
+
+                names.append(param[0])
+                params.append(param)
+            self.last_params = params
 
     def request(self, method, target, require=[], params=[]):
         content = ''
@@ -437,17 +451,17 @@ class WFDRTSPServer:
             self.debug('Gstreamer message of type ' + str(t) + ' for ' + message.src.name + ': ' + str(message))
         return True
 
-    def gst_force_keyframe(self):
+    def force_keyframe(self):
         enc = self.rtp_pipeline.get_by_name('videnc')
         sink = enc.get_static_pad('sink')
         timestamp = Gst.CLOCK_TIME_NONE # can/should we use sink.query_position?
 
         s = Gst.Structure('GstForceKeyUnit')
-        s.set_value('timestamp', timestamp, 'uint64')
-        s.set_value('stream-time', timestamp, 'uint64')
-        s.set_value('all-headers', True)
+        s.set_value('timestamp', GObject.Value(GObject.TYPE_UINT64, timestamp))
+        s.set_value('stream-time', GObject.Value(GObject.TYPE_UINT64, timestamp))
+        s.set_value('all-headers', GObject.Value(GObject.TYPE_BOOLEAN, True))
         # TODO: can we also send this event directly to the element instead of the pad?
-        sink.send_event(Gst.event_new_custom(Gst.EVENT_CUSTOM_DOWNSTREAM, s))
+        sink.send_event(Gst.Event.new_custom(Gst.EventType.CUSTOM_DOWNSTREAM, s))
 
     def rtsp_keepalive_timeout_cb(self):
         try:
@@ -585,9 +599,13 @@ class WFDRTSPServer:
                 self.response()
                 return
             if method == 'SET_PARAMETER':
-                # TODO: parse the stuff, on 'wfd-idr-request\r\n' (no semicolon) call the following:
-                self.gst_force_keyframe()
-                self.response()
+                self.validate_msg(method, 'SET_PARAMETER', status, reason, headers, target, content)
+                for k, v in self.last_params:
+                    if k == 'wfd_idr_request' and v is None:
+                        self.force_keyframe()
+                        self.response()
+                    else:
+                        self.error('Unknown request "' + k + '" with value ' + repr(v))
                 return
             if method == 'TEARDOWN':
                 # The spec suggests a more graceful teardown but we just close the connection
-- 
2.25.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 11/13] wfd-source: Display some stream properties
  2020-07-31  1:31 [PATCH 01/13] frame-xchg: Fix potential use after free Andrew Zaborowski
                   ` (8 preceding siblings ...)
  2020-07-31  1:31 ` [PATCH 10/13] wfd-source: Support wfd-idr-request Andrew Zaborowski
@ 2020-07-31  1:31 ` Andrew Zaborowski
  2020-07-31  1:31 ` [PATCH 12/13] wfd-source: Add stream utility buttons Andrew Zaborowski
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-07-31  1:31 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 28116 bytes --]

Define a bunch of stream parameters each with a getter and an optional
setter.  In the right pane of the window show widgets for these
properties, some as just labels and some as editable controls depending
on the type of the property.  Parse the EDID data.
---
 test/wfd-source | 361 +++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 326 insertions(+), 35 deletions(-)

diff --git a/test/wfd-source b/test/wfd-source
index 09b47a27..261ae750 100755
--- a/test/wfd-source
+++ b/test/wfd-source
@@ -16,6 +16,7 @@ import collections.abc
 import random
 import dataclasses
 import traceback
+import codecs
 
 import gi
 gi.require_version('GLib', '2.0')
@@ -27,7 +28,9 @@ class WFDRTSPServer:
     class RTSPException(Exception):
         pass
 
-    def __init__(self, port, state_handler, error_handler):
+    Prop = collections.namedtuple('Prop', ['name', 'desc', 'getter', 'setter', 'type', 'vals'])
+
+    def __init__(self, port, state_handler, error_handler, init_values, prop_handler):
         # Should start the TCP server only on the P2P connection's local IP but we won't
         # know the IP or interface name until after the connection is established.  At that
         # time the sink may try to make the TCP connection at any time so our listen
@@ -44,7 +47,8 @@ class WFDRTSPServer:
 
         self.state_handler = state_handler
         self.error_handler = error_handler
-        self.sm_init()
+        self.prop_handler = prop_handler
+        self.sm_init(init_values)
 
     def handle_data_out(self, conn, *args):
         try:
@@ -200,7 +204,11 @@ class WFDRTSPServer:
     def ready(self):
         return self._state in ['streaming', 'paused']
 
-    def sm_init(self):
+    @property
+    def props(self):
+        return self._props
+
+    def sm_init(self, init_values):
         self._state = 'waiting-rtsp'
         self.local_params = {
             'wfd_video_formats': '00 00 01 08 00000000 00000000 00000040 00 0000 0000 00 none none'
@@ -226,6 +234,33 @@ class WFDRTSPServer:
         self.rtsp_keepalive_timeout = None
         self.expected_remote_ip = None
         self.remote_ip = None
+        self.init_width = init_values['width']
+        self.init_height = init_values['height']
+        self.rtcp_enabled = init_values['rtcp_enabled']
+
+        self._props = []
+
+    @staticmethod
+    def get_init_props():
+        props = []
+        values = {
+            'width': 800,
+            'height': 600,
+            'rtcp_enabled': True
+        }
+
+        def set_val(key, val):
+            values[key] = val
+        props.append(WFDRTSPServer.Prop('Output width', 'Scale the video stream to this X resolution for sending',
+            lambda: values['width'], lambda x: set_val('width', x), int, (640, 1920)))
+        props.append(WFDRTSPServer.Prop('Output height', 'Scale the video stream to this Y resolution for sending',
+            lambda: values['height'], lambda x: set_val('height', x), int, (480, 1080)))
+        props.append(WFDRTSPServer.Prop('Enable RTCP', 'Use RTCP if the Sink requests it during setup',
+            lambda: values['rtcp_enabled'], lambda x: set_val('rtcp_enabled', x), bool, None))
+        # TODO: Enable Audio
+        # TODO: Audio source
+
+        return props, values
 
     def close(self):
         # Avoid passing self to io watches so that the refcount can ever reach 0 and
@@ -431,6 +466,94 @@ class WFDRTSPServer:
                 self.error('Optional RTCP port not valid in SETUP Transport header: ' + str(rtcp_port))
             self.remote_rtcp_port = rtcp_port
 
+        self._props.append(WFDRTSPServer.Prop('RTP transport', '', lambda: 'TCP' if self.use_tcp else 'UDP', None, str, None))
+        self._props.append(WFDRTSPServer.Prop('Remote RTP port', '', lambda: self.remote_rtp_port, None, int, None))
+        self._props.append(WFDRTSPServer.Prop('Remote RTCP port', '', lambda: self.remote_rtcp_port, None, int, None))
+
+    def parse_display_edid(self):
+        try:
+            len_str, hex_str = self.remote_params['wfd_display_edid'].split(' ', 1)
+            if len(len_str.strip()) != 4:
+                raise Exception('edid-block-count length is not 4 hex digits')
+            blocks = int(len_str, 16)
+            edid = codecs.decode(hex_str.strip(), 'hex')
+            if blocks < 1 or blocks > 256 or blocks * 128 != len(edid):
+                raise Exception('edid-block-count value wrong')
+        except:
+            edid = None
+
+        self._props.append(WFDRTSPServer.Prop('EDID info', 'Remote display\'s EDID data', lambda: edid, None, bytes, None))
+
+    def create_running_props(self):
+        src = self.rtp_pipeline.get_by_name('src')
+        fps = self.rtp_pipeline.get_by_name('fps')
+        enc = self.rtp_pipeline.get_by_name('videnc')
+        res = self.rtp_pipeline.get_by_name('res')
+        sink = self.rtp_pipeline.get_by_name('sink')
+        self.pipeline_props = []
+
+        srcpadcaps = src.srcpads[0].get_allowed_caps()
+        width = srcpadcaps[0]['width']
+        height = srcpadcaps[0]['height']
+        props = []
+        props.append(WFDRTSPServer.Prop('Local width', 'Local screen X resolution', lambda: width, None, int, None))
+        props.append(WFDRTSPServer.Prop('Local height', 'Local screen Y resolution', lambda: height, None, int, None))
+
+        def set_use_damage(val):
+            src.props.use_damage = val
+        props.append(WFDRTSPServer.Prop('Use XDamage', 'Try to use XDamage to reduce bandwidth usage',
+            lambda: src.props.use_damage, set_use_damage, bool, None))
+
+        src.props.endx = width
+        src.props.endy = height
+        def set_startx(val):
+            src.set_property('startx', min(val, src.props.endx - 1))
+        def set_starty(val):
+            src.set_property('starty', min(val, src.props.endy - 1))
+        def set_endx(val):
+            src.set_property('endx', max(val, src.props.startx + 1))
+        def set_endy(val):
+            src.set_property('endy', max(val, src.props.starty + 1))
+        props.append(WFDRTSPServer.Prop('Window min X', 'Skip this many pixels on the left side of the local screen',
+            lambda: src.props.startx, set_startx, int, (0, width - 1)))
+        props.append(WFDRTSPServer.Prop('Window min Y', 'Skip this many pixels on the top of the local screen',
+            lambda: src.props.starty, set_starty, int, (0, height - 1)))
+        props.append(WFDRTSPServer.Prop('Window max X', 'Send screen contents only up to this X coordinate',
+            lambda: src.props.endx, set_endx, int, (1, width)))
+        props.append(WFDRTSPServer.Prop('Window max Y', 'Send screen contents only up to this Y coordinate',
+            lambda: src.props.endy, set_endy, int, (1, height)))
+
+        def set_framerate(val):
+            fps.props.caps[0]['framerate'] = Gst.Fraction(val)
+        def set_width(val):
+            res.props.caps[0]['width'] = val
+        def set_height(val):
+            res.props.caps[0]['height'] = val
+        props.append(WFDRTSPServer.Prop('Framerate', 'Try to output this many frames per second',
+            lambda: int(fps.props.caps[0]['framerate'].num), set_framerate, int, (1, 30)))
+        props.append(WFDRTSPServer.Prop('Output width', 'Scale the video stream to this X resolution for sending',
+            lambda: res.props.caps[0]['width'], set_width, int, (640, 1920)))
+        props.append(WFDRTSPServer.Prop('Output height', 'Scale the video stream to this Y resolution for sending',
+            lambda: res.props.caps[0]['height'], set_height, int, (480, 1080)))
+
+        preset_values = ['veryslow', 'slower', 'slow', 'medium', 'fast', 'faster', 'veryfast', 'superfast', 'ultrafast', 'placebo']
+        preset_map = {'veryslow': 9, 'slower': 8, 'slow': 7, 'medium': 6, 'fast': 5, 'faster': 4, 'veryfast': 3, 'superfast': 2, 'ultrafast': 1, 'placebo': 10}
+
+        def set_speed_preset(val):
+            enc.props.speed_preset = preset_map[val]
+        props.append(WFDRTSPServer.Prop('H.264 speed preset', 'Speed/quality setting of the H.264 encoder to optimise bandwidth/latency',
+            lambda: enc.props.speed_preset.value_nick, set_speed_preset, str, preset_values))
+
+        def set_max_lateness(val):
+            if val <= 0:
+                sink.props.max_lateness = -1
+            else:
+                sink.props.max_lateness = val * 1000000 # milliseconds to nanoseconds
+        props.append(WFDRTSPServer.Prop('Max lateness', 'Maximum number of milliseconds that a buffer can be late before it is dropped, or 0 for unlimited',
+            lambda: 0 if sink.props.max_lateness == -1 else sink.props.max_lateness / 1000000, set_max_lateness, int, (-1, 3000)))
+
+        return props
+
     def on_gst_message(self, bus, message):
         t = message.type
         if t == Gst.MessageType.EOS:
@@ -438,6 +561,8 @@ class WFDRTSPServer:
         elif t == Gst.MessageType.STATE_CHANGED:
             old, new, pending = message.parse_state_changed()
             self.debug('Gstreamer state change for ' + message.src.name + ' from ' + str(old) + ' to ' + str(new) + ', pending=' + str(pending))
+            if message.src == self.rtp_pipeline:
+                self.prop_handler()
         elif t == Gst.MessageType.INFO:
             err, debug = message.parse_info()
             self.debug('Gstreamer info for ' + message.src.name + ': ' + str(err) + '\nDebug: ' + str(debug))
@@ -511,7 +636,8 @@ class WFDRTSPServer:
             # Send M2 response
             self.response(public=self.local_methods)
             # Send M3
-            self.request('GET_PARAMETER', 'rtsp://localhost/wfd1.0', params=['wfd_audio_codecs', 'wfd_video_formats', 'wfd_client_rtp_ports', 'wfd_display_edid', 'wfd_uibc_capability'])
+            params = ['wfd_audio_codecs', 'wfd_video_formats', 'wfd_client_rtp_ports', 'wfd_display_edid', 'wfd_uibc_capability']
+            self.request('GET_PARAMETER', 'rtsp://localhost/wfd1.0', params=params)
             self.enter_state('M3')
         elif self._state == 'M3':
             # Validate M3 response
@@ -520,6 +646,8 @@ class WFDRTSPServer:
                 self.error('Required parameters missing from GET_PARAMETER response')
             self.parse_video_formats(self.remote_params['wfd_video_formats'])
             self.parse_client_rtp_ports(self.remote_params['wfd_client_rtp_ports'])
+            self.parse_display_edid()
+            self.prop_handler()
             # Send M4
             params = {
                 'wfd_video_formats': self.local_params['wfd_video_formats'],
@@ -547,7 +675,7 @@ class WFDRTSPServer:
             self.session_stream_url = target
             self.session_id = str(random.randint(a=1, b=999999))
             self.local_rtp_port = random.randint(a=20000, b=30000)
-            if self.remote_rtcp_port is not None:
+            if self.remote_rtcp_port is not None and self.rtcp_enabled:
                 self.local_rtcp_port = self.local_rtp_port + 1
             profile ='RTP/AVP/TCP;unicast' if self.use_tcp else 'RTP/AVP/UDP;unicast'
             client_port = str(self.remote_rtp_port) + (('-' + str(self.remote_rtcp_port)) if self.remote_rtcp_port is not None else '')
@@ -555,22 +683,26 @@ class WFDRTSPServer:
             transport = profile + ';client_port' + client_port + ';server_port=' + server_port
             # Section B.1
             pipeline = ('ximagesrc name=src use-damage=false do-timestamp=true ! capsfilter name=fps caps=video/x-raw,framerate=10/1' +
-                ' ! videoscale method=0 ! capsfilter name=res caps=video/x-raw,width=800,height=600' +
+                ' ! videoscale method=0 ! capsfilter name=res caps=video/x-raw,width=' + str(self.init_width) + ',height=' + str(self.init_height) +
                 ' ! videoconvert ! video/x-raw,format=I420 ! x264enc tune=zerolatency speed-preset=ultrafast name=videnc' +
                 ' ! queue' + # TODO: add leaky=downstream
                 ' ! mpegtsmux name=mux' +
                 ' ! rtpmp2tpay pt=33 mtu=1472 ! .send_rtp_sink rtpsession name=session .send_rtp_src' +
-                ' ! udpsink host=' + self.remote_ip + ' port=' + str(self.remote_rtp_port) + ' bind-port=' + str(self.local_rtp_port)) # TODO: bind-address
-
+                ' ! udpsink name=sink host=' + self.remote_ip + ' port=' + str(self.remote_rtp_port) + ' bind-port=' + str(self.local_rtp_port)) # TODO: bind-address
             if self.local_rtcp_port is not None:
                 pipeline += ' session.send_rtcp_src ! udpsink name=rtcp_sink host=' + self.remote_ip + \
                     ' port=' + str(self.remote_rtcp_port) + ' bind-port=' + str(self.local_rtcp_port) # TODO: bind-address
+            self._props.append(WFDRTSPServer.Prop('RTCP enabled', 'Whether we\'re currently sending RTCP data',
+                lambda: self.local_rtcp_port is not None, None, bool, None))
 
             self.rtp_pipeline = Gst.parse_launch(pipeline)
             bus = self.rtp_pipeline.get_bus()
             bus.enable_sync_message_emission()
             bus.add_signal_watch()
-            bus.connect('sync-message', self.on_gst_message)
+            bus.connect('message', self.on_gst_message)
+
+            self._props += self.create_running_props()
+            self.prop_handler()
 
             # Send M6 response
             self.response(session=self.session_id + ';timeout=' + str(self.session_timeout), transport=transport)
@@ -653,12 +785,16 @@ class WFDSource(Gtk.Window):
         self.device_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
         leftscroll = Gtk.ScrolledWindow(hscrollbar_policy=Gtk.PolicyType.NEVER)
         leftscroll.add(self.device_box)
-        self.infolabel1 = Gtk.Label()
-        self.infolabel1.set_ellipsize(Pango.EllipsizeMode.START)
-        infopane = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
-        infopane.pack_start(self.infolabel1, False, False, padding=10)
-        rightscroll = Gtk.ScrolledWindow(hscrollbar_policy=Gtk.PolicyType.NEVER, vscrollbar_policy=Gtk.PolicyType.NEVER)
-        rightscroll.add(infopane)
+        self.infopane = Gtk.FlowBox(orientation=Gtk.Orientation.VERTICAL)
+        self.infopane.set_selection_mode(Gtk.SelectionMode.NONE)
+        self.infopane.set_max_children_per_line(20)
+        self.infopane.set_min_children_per_line(3)
+        self.infopane.set_column_spacing(20)
+        self.infopane.set_row_spacing(5)
+        self.infopane.set_valign(Gtk.Align.START)
+        self.infopane.set_halign(Gtk.Align.START)
+        rightscroll = Gtk.ScrolledWindow(vscrollbar_policy=Gtk.PolicyType.NEVER)
+        rightscroll.add(self.infopane)
         paned = Gtk.Paned(orientation=Gtk.Orientation.HORIZONTAL)
         paned.pack1(leftscroll, True, True)
         paned.pack2(rightscroll, False, False)
@@ -669,6 +805,8 @@ class WFDSource(Gtk.Window):
         self.show_all()
         self.connect('notify::is-active', self.on_notify_is_active)
 
+        self.rtsp_props = None
+        self.rtsp_init_values = {}
         self.rtsp_port = 7236
         self.devices = None
         self.objects = {}
@@ -1001,11 +1139,13 @@ class WFDSource(Gtk.Window):
             peer_list.insert(peer.widget, index)
             peer.widget.show_all()
         elif (PEER_IF not in props or WFD_IF not in props or WSC_IF not in props or not props[WFD_IF]['Sink']) and peer.widget:
-            del device.sorted_peers[peer.widget.get_index()]
-            peer_list.remove(peer.widget)
+            tmp = peer.widget
+            peer.widget = None
+            del device.sorted_peers[tmp.get_index()]
+            peer_list.remove(tmp)
             if peer == device.selected_peer:
                 device.selected_peer = None
-                self.update_info(dev_path, None)
+                self.update_info_pane(dev_path, None)
             if peer == device.connecting_peer:
                 device.dbus_call.cancel()
                 device.connecting_peer = None
@@ -1020,7 +1160,6 @@ class WFDSource(Gtk.Window):
             peer.peer_proxy = None
             peer.wfd_proxy = None
             peer.wsc_proxy = None
-            peer.widget = None
             if peer.rtsp:
                 peer.rtsp.close()
                 peer.rtsp = None
@@ -1055,7 +1194,7 @@ class WFDSource(Gtk.Window):
             button.hide()
 
         if peer == device.selected_peer:
-            self.update_info(dev_path, path)
+            self.update_info_pane(dev_path, path)
 
     def update_selected_peer(self, dev_path):
         device = self.devices[dev_path]
@@ -1063,12 +1202,74 @@ class WFDSource(Gtk.Window):
             sel_path = self.get_peer_path(device, device.selected_peer)
             self.update_peer_props(dev_path, sel_path)
 
-    def update_info(self, dev_path, path):
-        device = self.devices[dev_path]
+    def add_info(self, name, desc, valuewidget):
+        namelabel = Gtk.Label(label=name + ':', xalign=0)
+        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
+        box.pack_start(namelabel, expand=False, fill=False, padding=3)
+        if valuewidget:
+            box.pack_end(valuewidget, expand=False, fill=False, padding=3)
+        if desc:
+            box.set_tooltip_text(desc)
+        self.infopane.add(box)
+
+    def add_info_str(self, name, value):
+        vlabel = Gtk.Label(xalign=0)
+        vlabel.set_markup('<span weight="bold">' + value + '</span>')
+        self.add_info(name, None, vlabel)
+
+    def add_info_prop(self, prop):
+        val = prop.getter()
+        if prop.setter is None:
+            if val is None:
+                return
+            if prop.type == bool:
+                vals = prop.vals if prop.vals is not None else ['no', 'yes']
+                text = vals[val]
+            elif prop.name == 'EDID info':
+                text = WFDSource.edid_to_text(val)
+                if isinstance(text, collections.abc.Sequence):
+                    self.add_info(prop.name, prop.desc, None)
+                    for name, val in text:
+                        if val:
+                            v = Gtk.Label(xalign=0)
+                            v.set_markup('<span weight="bold">» ' + str(val) + '</span>')
+                            self.add_info(name, prop.desc, v)
+                        else:
+                            self.add_info(name, prop.desc, None)
+                    return
+            else:
+                text = str(val)
+            v = Gtk.Label(xalign=0)
+            v.set_markup('<span weight="bold">' + text + '</span>')
+        elif val is None:
+            return
+        elif prop.type == bool:
+            v = Gtk.Switch()
+            v.set_active(val)
+            v.connect('state-set', lambda switch, state: prop.setter(state))
+        elif prop.type == int:
+            v = Gtk.SpinButton.new_with_range(min=prop.vals[0], max=prop.vals[1], step=prop.vals[2] if len(prop.vals) > 2 else 1)
+            v.set_value(val)
+            v.connect('value-changed', lambda sb: prop.setter(int(sb.get_value())))
+        elif prop.type == str:
+            if prop.vals:
+                v = Gtk.ComboBoxText()
+                for option in prop.vals:
+                    v.append(option, option)
+                v.set_active_id(val)
+                v.connect('changed', lambda entry: prop.setter(entry.get_active_text()))
+            else:
+                v = Gtk.Entry(text=val)
+                v.connect('changed', lambda entry: prop.setter(entry.get_text()))
+        self.add_info(prop.name, prop.desc, v)
+
+    def update_info_pane(self, dev_path, path):
+        self.infopane.foreach(lambda x, y: self.infopane.remove(x), None)
+
         if path is None:
-            self.infolabel1.set_text('')
             return
 
+        device = self.devices[dev_path]
         peer = device.peers[path]
 
         if peer == device.connecting_peer:
@@ -1085,14 +1286,13 @@ class WFDSource(Gtk.Window):
                 state = 'connected'
         else:
             state = 'not connected'
+        self.add_info_str('Connection state', state)
 
         subcat = 'unknown'
         if 'DeviceSubcategory' in self.objects[path][PEER_IF]:
             subcat = self.objects[path][PEER_IF]['DeviceSubcategory']
-
-        text = ('Connection state: ' + state + '\n' +
-                'Device category: ' + self.objects[path][PEER_IF]['DeviceCategory'] + '\n'
-                'Device subcategory: ' + subcat + '\n')
+        self.add_info_str('Peer category', self.objects[path][PEER_IF]['DeviceCategory'])
+        self.add_info_str('Peer subcategory', subcat)
 
         if WFD_IF in self.objects[path]:
             if self.objects[path][WFD_IF]['Source']:
@@ -1102,17 +1302,27 @@ class WFDSource(Gtk.Window):
                     t = 'source'
             else:
                 t = 'sink'
-            text += 'WFD device type: ' + t + '\n'
+            self.add_info_str('Peer WFD type', t)
 
             if self.objects[path][WFD_IF]['Sink']:
-                text += 'Audio: ' + ('yes' if self.objects[path][WFD_IF]['HasAudio'] else 'no') + '\n'
+                self.add_info_str('Peer audio support', 'yes' if self.objects[path][WFD_IF]['HasAudio'] else 'no')
+
+            self.add_info_str('Peer UIBC support', 'yes' if self.objects[path][WFD_IF]['HasUIBC'] else 'no')
+
+            self.add_info_str('Peer content protection', 'yes' if self.objects[path][WFD_IF]['HasContentProtection'] else 'no')
 
-            text += 'UIBC: ' + ('yes' if self.objects[path][WFD_IF]['HasUIBC'] else 'no') + '\n'
+        if self.rtsp_props is None:
+            self.rtsp_props, self.rtsp_init_values = WFDRTSPServer.get_init_props()
+
+        if peer.rtsp is not None:
+            props = peer.rtsp.props
+        else:
+            props = self.rtsp_props
 
-            text += 'Content protection: ' + ('yes' if self.objects[path][WFD_IF]['HasContentProtection'] else 'no') + '\n'
+        for prop in props:
+            self.add_info_prop(prop)
 
-        self.infolabel1.set_text(text)
-        # TODO: more info in labels 2 and so on
+        self.infopane.show_all()
 
     # Direct method calls on dbus.Interface's don't return dbus.lowlevel.PendingCall objects so
     # we have to use bus.call_async to make cancellable async calls
@@ -1172,12 +1382,17 @@ class WFDSource(Gtk.Window):
 
             dialog.connect('response', on_ok)
 
+        def on_rtsp_props_changed():
+            # Should also check if the infopane is currently showing a selected peer on another device...
+            if peer == device.selected_peer:
+                self.update_info_pane(dev_path, path)
+
         # Cannot use peer.wsc_proxy.PushButton()
         device.dbus_call = self.async_call(peer.wsc_proxy, 'PushButton', reply_handler=on_reply, error_handler=on_error, timeout=120)
         device.connecting_peer = peer
         # Create the RTSP server now so it's ready as soon as the P2P connection succeeds even if
         # we haven't received the DBus reply yet
-        peer.rtsp = WFDRTSPServer(self.rtsp_port, on_rtsp_state, on_rtsp_error)
+        peer.rtsp = WFDRTSPServer(self.rtsp_port, on_rtsp_state, on_rtsp_error, self.rtsp_init_values, on_rtsp_props_changed)
         self.update_dev_props(dev_path)
         self.update_peer_props(dev_path, path)
         if peer != device.selected_peer:
@@ -1272,7 +1487,7 @@ class WFDSource(Gtk.Window):
             path = self.get_peer_path(device, device.selected_peer)
             device.selected_peer = None
             self.update_peer_props(dev_path, path)
-            self.update_info(dev_path, None)
+            self.update_info_pane(dev_path, None)
 
         if row is None:
             return True
@@ -1335,6 +1550,82 @@ class WFDSource(Gtk.Window):
         mainloop.quit()
         return False
 
+    @staticmethod
+    def edid_to_text(edid):
+        if edid is None:
+            return 'unavailable'
+        if len(edid) < 128:
+            return 'invalid (too short)'
+        if edid[0:8] != b'\0\xff\xff\xff\xff\xff\xff\0':
+            return 'invalid (bad magic)'
+        if sum(edid[0:128]) & 255 != 0:
+            return 'invalid (bad checksum)'
+
+        header = edid[0:20]
+        manf_id = (header[8] << 8) + header[9]
+        text = [('Header', '')]
+        text.append(('» Version', str(header[18]) + '.' + str(header[19])))
+        text.append(('» Manufacturer ID', chr(64 + ((manf_id >> 10) & 31)) + chr(64 + ((manf_id >> 5) & 31)) + chr(64 + ((manf_id >> 0) & 31))))
+        text.append(('» Product code', hex((header[11] << 8) + header[10])))
+        text.append(('» Serial', hex((header[15] << 24) +(header[14] << 16) +  (header[13] << 8) + header[12])))
+        text.append(('» Manufactured', str(1990 + header[17]) + ' week ' + str(header[16])))
+
+        basic_params = edid[20:25]
+        text.append(('Basic parameters', ''))
+        if basic_params[0] & 0x80:
+            intf_table = {
+                2: 'HDMIa',
+                3: 'HDMIb',
+                4: 'MDDI',
+                5: 'DisplayPort'
+            }
+            dt_table = {
+                0: 'RGB 4:4:4',
+                1: 'RGB 4:4:4 + YCrCb 4:4:4',
+                2: 'RGB 4:4:4 + YCrCb 4:2:2',
+                3: 'RGB 4:4:4 + YCrCb 4:4:4 + YCrCb 4:2:2'
+            }
+            bpp = (basic_params[0] >> 4) & 7
+            intf = (basic_params[0] >> 0) & 7
+
+            text.append(('» Video input type', 'digital'))
+            text.append(('» Bit depth', 'undefined' if bpp in [0, 7] else str(4 + bpp * 2)))
+            text.append(('» Interface', 'undefined' if intf not in intf_table else intf_table[intf]))
+        else:
+            level_table = {
+                0: '+0.7 / -0.3 V',
+                1: '+0.714 / -0.286 V',
+                2: '+1.0 / -0.4 V',
+                3: '+0.7 / 0 V'
+            }
+            dt_table = {
+                0: 'monochrome/grayscale',
+                1: 'RGB color',
+                2: 'non-RGB color',
+                3: 'undefined'
+            }
+            text.append(('» Video input type', 'analog'))
+            text.append(('» Video white/sync level', level_table[(basic_parmas[0] >> 5) & 3]))
+
+        if basic_params[1] and basic_params[2]:
+            text.append(('» Screen width', str(basic_params[1]) + ' cm'))
+            text.append(('» Screen height', str(basic_params[2]) + ' cm'))
+        elif basic_params[2] == 0:
+            text.append(('» Landscape aspect ratio', str((basic_params[1] + 99) * 0.01)))
+        else:
+            text.append(('» Portrait aspect ratio', str(100.0 / (basic_params[2] + 99))))
+
+        text.append(('» Gamma', str((basic_params[3] + 100) * 0.01)))
+        text.append(('» DPMS Standby', 'supported' if (basic_params[4] >> 7) & 1 else 'unsupported'))
+        text.append(('» DPMS Suspend', 'supported' if (basic_params[4] >> 6) & 1 else 'unsupported'))
+        text.append(('» DPMS Active-off', 'supported' if (basic_params[4] >> 5) & 1 else 'unsupported'))
+        text.append(('» Color type', dt_table[(basic_params[4] >> 3) & 3]))
+        text.append(('» sRGB color space', 'yes' if (basic_params[4] >> 2) & 1 else 'no'))
+        text.append(('» Continuous timings', 'yes' if (basic_params[4] >> 0) & 1 else 'no'))
+
+        # TODO: timing information and extensions
+        return text
+
 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
 Gst.init(None)
 WFDSource()
-- 
2.25.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 12/13] wfd-source: Add stream utility buttons
  2020-07-31  1:31 [PATCH 01/13] frame-xchg: Fix potential use after free Andrew Zaborowski
                   ` (9 preceding siblings ...)
  2020-07-31  1:31 ` [PATCH 11/13] wfd-source: Display some stream properties Andrew Zaborowski
@ 2020-07-31  1:31 ` Andrew Zaborowski
  2020-07-31  1:31 ` [PATCH 13/13] wfd-source: Allow alternative URLs in SETUP request Andrew Zaborowski
  2020-07-31 15:40 ` [PATCH 01/13] frame-xchg: Fix potential use after free Denis Kenzior
  12 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-07-31  1:31 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 2176 bytes --]

Add two buttons to the UI when the stream is playing: one for forcing an
H.264 key-frame (IDR) and one for restarting the stream in gstreamer.
---
 test/wfd-source | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/test/wfd-source b/test/wfd-source
index 261ae750..4891c70c 100755
--- a/test/wfd-source
+++ b/test/wfd-source
@@ -588,6 +588,11 @@ class WFDRTSPServer:
         # TODO: can we also send this event directly to the element instead of the pad?
         sink.send_event(Gst.Event.new_custom(Gst.EventType.CUSTOM_DOWNSTREAM, s))
 
+    def reset_stream(self):
+        if self.rtp_pipeline.get_state(timeout=0)[1] == Gst.State.PLAYING:
+            self.rtp_pipeline.set_state(Gst.State.PAUSED)
+            self.rtp_pipeline.set_state(Gst.State.PLAYING)
+
     def rtsp_keepalive_timeout_cb(self):
         try:
             self.rtsp_keepalive_timeout = None
@@ -1311,6 +1316,25 @@ class WFDSource(Gtk.Window):
 
             self.add_info_str('Peer content protection', 'yes' if self.objects[path][WFD_IF]['HasContentProtection'] else 'no')
 
+        if peer.rtsp is not None and peer.rtsp.ready:
+            def force_keyframe(widget):
+                peer.rtsp.force_keyframe()
+                return True
+            def reset_stream(widget):
+                peer.rtsp.reset_stream()
+                return True
+            # The idea for these buttons is to make sure any parameter changes get fully applied
+            button1 = Gtk.Button()
+            button1.set_label('Force keyframe')
+            button1.connect('clicked', force_keyframe)
+            button2 = Gtk.Button()
+            button2.set_label('Reset stream')
+            button2.connect('clicked', reset_stream)
+            box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
+            box.pack_start(button1, expand=False, fill=False, padding=3)
+            box.pack_start(button2, expand=False, fill=False, padding=3)
+            self.infopane.add(box)
+
         if self.rtsp_props is None:
             self.rtsp_props, self.rtsp_init_values = WFDRTSPServer.get_init_props()
 
-- 
2.25.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 13/13] wfd-source: Allow alternative URLs in SETUP request
  2020-07-31  1:31 [PATCH 01/13] frame-xchg: Fix potential use after free Andrew Zaborowski
                   ` (10 preceding siblings ...)
  2020-07-31  1:31 ` [PATCH 12/13] wfd-source: Add stream utility buttons Andrew Zaborowski
@ 2020-07-31  1:31 ` Andrew Zaborowski
  2020-07-31 15:40 ` [PATCH 01/13] frame-xchg: Fix potential use after free Denis Kenzior
  12 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-07-31  1:31 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 2238 bytes --]

Some WFD sinks issue an RTSP SETUP request with the target
'rtsp://<source-ip>/wfd1.0/streamid=0' so add that URL to the targets we
allow for SETUP.
---
 test/wfd-source | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/test/wfd-source b/test/wfd-source
index 4891c70c..cb92c161 100755
--- a/test/wfd-source
+++ b/test/wfd-source
@@ -17,6 +17,10 @@ import random
 import dataclasses
 import traceback
 import codecs
+try:
+    import netifaces
+except:
+    pass
 
 import gi
 gi.require_version('GLib', '2.0')
@@ -216,6 +220,7 @@ class WFDRTSPServer:
         self.remote_params = {}
         self.local_methods = [ 'org.wfa.wfd1.0', 'SET_PARAMETER', 'GET_PARAMETER', 'PLAY', 'SETUP', 'TEARDOWN' ]
         self.presentation_url = [ 'rtsp://127.0.0.1/wfd1.0/streamid=0', 'none' ] # Table 88
+        self.alternative_urls = [ 'rtsp://localhost/wfd1.0/streamid=0' ]
         self.session_stream_url = None
         self.session_id = None
         self.session_timeout = 60
@@ -282,7 +287,11 @@ class WFDRTSPServer:
             self.conn = None
 
     def set_local_interface(self, new_value):
-        pass
+        try:
+            local_ip = netifaces.ifaddresses(new_value)[netifaces.AF_INET][0]['addr']
+            self.alternative_urls.append('rtsp://' + local_ip + '/wfd1.0/streamid=0')
+        except:
+            pass
 
     def set_remote_ip(self, new_value):
         self.expected_remote_ip = new_value
@@ -345,7 +354,7 @@ class WFDRTSPServer:
             self.error('Missing "Require" header in OPTIONS request')
         elif method == 'SETUP' and 'transport' not in headers:
             self.error('Missing "Transport" header in SETUP request')
-        elif method == 'SETUP' and (target not in self.presentation_url or target == 'none'):
+        elif method == 'SETUP' and (target not in self.presentation_url + self.alternative_urls or target == 'none'):
             self.error('Unknown target "' + target + '" in SETUP request')
         elif method == 'PLAY' and ('session' not in headers  or headers['session'] != self.session_id):
             self.error('Missing or invalid "Session" header in PLAY request')
-- 
2.25.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 01/13] frame-xchg: Fix potential use after free
  2020-07-31  1:31 [PATCH 01/13] frame-xchg: Fix potential use after free Andrew Zaborowski
                   ` (11 preceding siblings ...)
  2020-07-31  1:31 ` [PATCH 13/13] wfd-source: Allow alternative URLs in SETUP request Andrew Zaborowski
@ 2020-07-31 15:40 ` Denis Kenzior
  2020-07-31 18:56   ` Andrew Zaborowski
  12 siblings, 1 reply; 15+ messages in thread
From: Denis Kenzior @ 2020-07-31 15:40 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 639 bytes --]

Hi Andrew,

On 7/30/20 8:31 PM, Andrew Zaborowski wrote:
> Check if the frame callback has cancelled the frame_xchg before
> attempting to free it.
> ---
> I'm adding a frame_xchg_match_ptr here and I think I might have
> used a similar function somewhere else.  I was wondering if we
> want to add a pointer compare function in util.c or accept NULL
> as match function in l_queue_find() to mean match by data pointer.
> ---
>   src/frame-xchg.c | 8 ++++++++
>   1 file changed, 8 insertions(+)
> 

Patches 1-10 applied, thanks.

Patch 11-13 refuse to apply for some reason, so please rebase/resend.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 01/13] frame-xchg: Fix potential use after free
  2020-07-31 15:40 ` [PATCH 01/13] frame-xchg: Fix potential use after free Denis Kenzior
@ 2020-07-31 18:56   ` Andrew Zaborowski
  0 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2020-07-31 18:56 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 332 bytes --]

On Fri, 31 Jul 2020 at 17:57, Denis Kenzior <denkenz@gmail.com> wrote:
> Patches 1-10 applied, thanks.

Thank you.

>
> Patch 11-13 refuse to apply for some reason, so please rebase/resend.

Patch 11 had some utf8 characters that may have confused the mail
utils, this time I escaoed these chars using \xXX.

Best regards

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2020-07-31 18:56 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-31  1:31 [PATCH 01/13] frame-xchg: Fix potential use after free Andrew Zaborowski
2020-07-31  1:31 ` [PATCH 02/13] frame-xchg: Improve search for current frame in MLME notify Andrew Zaborowski
2020-07-31  1:31 ` [PATCH 03/13] frame-xchg: Re-add frame_xchg_stop Andrew Zaborowski
2020-07-31  1:31 ` [PATCH 04/13] station: Comment/whitespace fix Andrew Zaborowski
2020-07-31  1:31 ` [PATCH 05/13] p2p: Retry connect on "Previous authentication not valid" Andrew Zaborowski
2020-07-31  1:31 ` [PATCH 06/13] p2p: Add a null-check before use Andrew Zaborowski
2020-07-31  1:31 ` [PATCH 07/13] p2p: Set p2p_own_wfd->available by default Andrew Zaborowski
2020-07-31  1:31 ` [PATCH 08/13] p2p: Use a longer DHCP timeout Andrew Zaborowski
2020-07-31  1:31 ` [PATCH 09/13] wfd-source: Print call trace on error Andrew Zaborowski
2020-07-31  1:31 ` [PATCH 10/13] wfd-source: Support wfd-idr-request Andrew Zaborowski
2020-07-31  1:31 ` [PATCH 11/13] wfd-source: Display some stream properties Andrew Zaborowski
2020-07-31  1:31 ` [PATCH 12/13] wfd-source: Add stream utility buttons Andrew Zaborowski
2020-07-31  1:31 ` [PATCH 13/13] wfd-source: Allow alternative URLs in SETUP request Andrew Zaborowski
2020-07-31 15:40 ` [PATCH 01/13] frame-xchg: Fix potential use after free Denis Kenzior
2020-07-31 18:56   ` Andrew Zaborowski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.