xref: /freebsd/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
1325151a3SRui Paulo /*
2325151a3SRui Paulo  * wpa_supplicant - P2P service discovery
3325151a3SRui Paulo  * Copyright (c) 2009-2010, Atheros Communications
4325151a3SRui Paulo  * Copyright (c) 2010-2014, Jouni Malinen <j@w1.fi>
5325151a3SRui Paulo  *
6325151a3SRui Paulo  * This software may be distributed under the terms of the BSD license.
7325151a3SRui Paulo  * See README for more details.
8325151a3SRui Paulo  */
9325151a3SRui Paulo 
10325151a3SRui Paulo #include "utils/includes.h"
11325151a3SRui Paulo 
12325151a3SRui Paulo #include "utils/common.h"
13325151a3SRui Paulo #include "p2p/p2p.h"
14325151a3SRui Paulo #include "wpa_supplicant_i.h"
15325151a3SRui Paulo #include "notify.h"
16325151a3SRui Paulo #include "p2p_supplicant.h"
17325151a3SRui Paulo 
18325151a3SRui Paulo 
19325151a3SRui Paulo /*
20325151a3SRui Paulo  * DNS Header section is used only to calculate compression pointers, so the
21325151a3SRui Paulo  * contents of this data does not matter, but the length needs to be reserved
22325151a3SRui Paulo  * in the virtual packet.
23325151a3SRui Paulo  */
24325151a3SRui Paulo #define DNS_HEADER_LEN 12
25325151a3SRui Paulo 
26325151a3SRui Paulo /*
27325151a3SRui Paulo  * 27-octet in-memory packet from P2P specification containing two implied
28325151a3SRui Paulo  * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN
29325151a3SRui Paulo  */
30325151a3SRui Paulo #define P2P_SD_IN_MEMORY_LEN 27
31325151a3SRui Paulo 
p2p_sd_dns_uncompress_label(char ** upos,char * uend,u8 * start,u8 ** spos,const u8 * end)32325151a3SRui Paulo static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start,
33325151a3SRui Paulo 				       u8 **spos, const u8 *end)
34325151a3SRui Paulo {
35325151a3SRui Paulo 	while (*spos < end) {
36325151a3SRui Paulo 		u8 val = ((*spos)[0] & 0xc0) >> 6;
37325151a3SRui Paulo 		int len;
38325151a3SRui Paulo 
39325151a3SRui Paulo 		if (val == 1 || val == 2) {
40325151a3SRui Paulo 			/* These are reserved values in RFC 1035 */
41325151a3SRui Paulo 			wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
42325151a3SRui Paulo 				   "sequence starting with 0x%x", val);
43325151a3SRui Paulo 			return -1;
44325151a3SRui Paulo 		}
45325151a3SRui Paulo 
46325151a3SRui Paulo 		if (val == 3) {
47325151a3SRui Paulo 			u16 offset;
48325151a3SRui Paulo 			u8 *spos_tmp;
49325151a3SRui Paulo 
50325151a3SRui Paulo 			/* Offset */
51780fb4a2SCy Schubert 			if (end - *spos < 2) {
52325151a3SRui Paulo 				wpa_printf(MSG_DEBUG, "P2P: No room for full "
53325151a3SRui Paulo 					   "DNS offset field");
54325151a3SRui Paulo 				return -1;
55325151a3SRui Paulo 			}
56325151a3SRui Paulo 
57325151a3SRui Paulo 			offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1];
58325151a3SRui Paulo 			if (offset >= *spos - start) {
59325151a3SRui Paulo 				wpa_printf(MSG_DEBUG, "P2P: Invalid DNS "
60325151a3SRui Paulo 					   "pointer offset %u", offset);
61325151a3SRui Paulo 				return -1;
62325151a3SRui Paulo 			}
63325151a3SRui Paulo 
64325151a3SRui Paulo 			(*spos) += 2;
65325151a3SRui Paulo 			spos_tmp = start + offset;
66325151a3SRui Paulo 			return p2p_sd_dns_uncompress_label(upos, uend, start,
67325151a3SRui Paulo 							   &spos_tmp,
68325151a3SRui Paulo 							   *spos - 2);
69325151a3SRui Paulo 		}
70325151a3SRui Paulo 
71325151a3SRui Paulo 		/* Label */
72325151a3SRui Paulo 		len = (*spos)[0] & 0x3f;
73325151a3SRui Paulo 		if (len == 0)
74325151a3SRui Paulo 			return 0;
75325151a3SRui Paulo 
76325151a3SRui Paulo 		(*spos)++;
77780fb4a2SCy Schubert 		if (len > end - *spos) {
78325151a3SRui Paulo 			wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
79325151a3SRui Paulo 				   "sequence - no room for label with length "
80325151a3SRui Paulo 				   "%u", len);
81325151a3SRui Paulo 			return -1;
82325151a3SRui Paulo 		}
83325151a3SRui Paulo 
84780fb4a2SCy Schubert 		if (len + 2 > uend - *upos)
85325151a3SRui Paulo 			return -2;
86325151a3SRui Paulo 
87325151a3SRui Paulo 		os_memcpy(*upos, *spos, len);
88325151a3SRui Paulo 		*spos += len;
89325151a3SRui Paulo 		*upos += len;
90325151a3SRui Paulo 		(*upos)[0] = '.';
91325151a3SRui Paulo 		(*upos)++;
92325151a3SRui Paulo 		(*upos)[0] = '\0';
93325151a3SRui Paulo 	}
94325151a3SRui Paulo 
95325151a3SRui Paulo 	return 0;
96325151a3SRui Paulo }
97325151a3SRui Paulo 
98325151a3SRui Paulo 
99325151a3SRui Paulo /* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet.
100325151a3SRui Paulo  * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is
101325151a3SRui Paulo  * not large enough */
p2p_sd_dns_uncompress(char * buf,size_t buf_len,const u8 * msg,size_t msg_len,size_t offset)102325151a3SRui Paulo static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg,
103325151a3SRui Paulo 				 size_t msg_len, size_t offset)
104325151a3SRui Paulo {
105325151a3SRui Paulo 	/* 27-octet in-memory packet from P2P specification */
106325151a3SRui Paulo 	const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01"
107325151a3SRui Paulo 		"\x04_udp\xC0\x11\x00\x0C\x00\x01";
108325151a3SRui Paulo 	u8 *tmp, *end, *spos;
109325151a3SRui Paulo 	char *upos, *uend;
110325151a3SRui Paulo 	int ret = 0;
111325151a3SRui Paulo 
112325151a3SRui Paulo 	if (buf_len < 2)
113325151a3SRui Paulo 		return -1;
114325151a3SRui Paulo 	if (offset > msg_len)
115325151a3SRui Paulo 		return -1;
116325151a3SRui Paulo 
117325151a3SRui Paulo 	tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len);
118325151a3SRui Paulo 	if (tmp == NULL)
119325151a3SRui Paulo 		return -1;
120325151a3SRui Paulo 	spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN;
121325151a3SRui Paulo 	end = spos + msg_len;
122325151a3SRui Paulo 	spos += offset;
123325151a3SRui Paulo 
124325151a3SRui Paulo 	os_memset(tmp, 0, DNS_HEADER_LEN);
125325151a3SRui Paulo 	os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN);
126325151a3SRui Paulo 	os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len);
127325151a3SRui Paulo 
128325151a3SRui Paulo 	upos = buf;
129325151a3SRui Paulo 	uend = buf + buf_len;
130325151a3SRui Paulo 
131325151a3SRui Paulo 	ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end);
132325151a3SRui Paulo 	if (ret) {
133325151a3SRui Paulo 		os_free(tmp);
134325151a3SRui Paulo 		return ret;
135325151a3SRui Paulo 	}
136325151a3SRui Paulo 
137325151a3SRui Paulo 	if (upos == buf) {
138325151a3SRui Paulo 		upos[0] = '.';
139325151a3SRui Paulo 		upos[1] = '\0';
140325151a3SRui Paulo 	} else if (upos[-1] == '.')
141325151a3SRui Paulo 		upos[-1] = '\0';
142325151a3SRui Paulo 
143325151a3SRui Paulo 	os_free(tmp);
144325151a3SRui Paulo 	return 0;
145325151a3SRui Paulo }
146325151a3SRui Paulo 
147325151a3SRui Paulo 
148325151a3SRui Paulo static struct p2p_srv_bonjour *
wpas_p2p_service_get_bonjour(struct wpa_supplicant * wpa_s,const struct wpabuf * query)149325151a3SRui Paulo wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s,
150325151a3SRui Paulo 			     const struct wpabuf *query)
151325151a3SRui Paulo {
152325151a3SRui Paulo 	struct p2p_srv_bonjour *bsrv;
153325151a3SRui Paulo 	size_t len;
154325151a3SRui Paulo 
155325151a3SRui Paulo 	len = wpabuf_len(query);
156325151a3SRui Paulo 	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
157325151a3SRui Paulo 			 struct p2p_srv_bonjour, list) {
158325151a3SRui Paulo 		if (len == wpabuf_len(bsrv->query) &&
159325151a3SRui Paulo 		    os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query),
160325151a3SRui Paulo 			      len) == 0)
161325151a3SRui Paulo 			return bsrv;
162325151a3SRui Paulo 	}
163325151a3SRui Paulo 	return NULL;
164325151a3SRui Paulo }
165325151a3SRui Paulo 
166325151a3SRui Paulo 
167325151a3SRui Paulo static struct p2p_srv_upnp *
wpas_p2p_service_get_upnp(struct wpa_supplicant * wpa_s,u8 version,const char * service)168325151a3SRui Paulo wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version,
169325151a3SRui Paulo 			  const char *service)
170325151a3SRui Paulo {
171325151a3SRui Paulo 	struct p2p_srv_upnp *usrv;
172325151a3SRui Paulo 
173325151a3SRui Paulo 	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
174325151a3SRui Paulo 			 struct p2p_srv_upnp, list) {
175325151a3SRui Paulo 		if (version == usrv->version &&
176325151a3SRui Paulo 		    os_strcmp(service, usrv->service) == 0)
177325151a3SRui Paulo 			return usrv;
178325151a3SRui Paulo 	}
179325151a3SRui Paulo 	return NULL;
180325151a3SRui Paulo }
181325151a3SRui Paulo 
182325151a3SRui Paulo 
wpas_sd_add_empty(struct wpabuf * resp,u8 srv_proto,u8 srv_trans_id,u8 status)183325151a3SRui Paulo static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto,
184325151a3SRui Paulo 			      u8 srv_trans_id, u8 status)
185325151a3SRui Paulo {
186325151a3SRui Paulo 	u8 *len_pos;
187325151a3SRui Paulo 
188325151a3SRui Paulo 	if (wpabuf_tailroom(resp) < 5)
189325151a3SRui Paulo 		return;
190325151a3SRui Paulo 
191325151a3SRui Paulo 	/* Length (to be filled) */
192325151a3SRui Paulo 	len_pos = wpabuf_put(resp, 2);
193325151a3SRui Paulo 	wpabuf_put_u8(resp, srv_proto);
194325151a3SRui Paulo 	wpabuf_put_u8(resp, srv_trans_id);
195325151a3SRui Paulo 	/* Status Code */
196325151a3SRui Paulo 	wpabuf_put_u8(resp, status);
197325151a3SRui Paulo 	/* Response Data: empty */
198325151a3SRui Paulo 	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
199325151a3SRui Paulo }
200325151a3SRui Paulo 
201325151a3SRui Paulo 
wpas_sd_add_proto_not_avail(struct wpabuf * resp,u8 srv_proto,u8 srv_trans_id)202325151a3SRui Paulo static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
203325151a3SRui Paulo 					u8 srv_trans_id)
204325151a3SRui Paulo {
205325151a3SRui Paulo 	wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
206325151a3SRui Paulo 			  P2P_SD_PROTO_NOT_AVAILABLE);
207325151a3SRui Paulo }
208325151a3SRui Paulo 
209325151a3SRui Paulo 
wpas_sd_add_bad_request(struct wpabuf * resp,u8 srv_proto,u8 srv_trans_id)210325151a3SRui Paulo static void wpas_sd_add_bad_request(struct wpabuf *resp, u8 srv_proto,
211325151a3SRui Paulo 				    u8 srv_trans_id)
212325151a3SRui Paulo {
213325151a3SRui Paulo 	wpas_sd_add_empty(resp, srv_proto, srv_trans_id, P2P_SD_BAD_REQUEST);
214325151a3SRui Paulo }
215325151a3SRui Paulo 
216325151a3SRui Paulo 
wpas_sd_add_not_found(struct wpabuf * resp,u8 srv_proto,u8 srv_trans_id)217325151a3SRui Paulo static void wpas_sd_add_not_found(struct wpabuf *resp, u8 srv_proto,
218325151a3SRui Paulo 				  u8 srv_trans_id)
219325151a3SRui Paulo {
220325151a3SRui Paulo 	wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
221325151a3SRui Paulo 			  P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
222325151a3SRui Paulo }
223325151a3SRui Paulo 
224325151a3SRui Paulo 
wpas_sd_all_bonjour(struct wpa_supplicant * wpa_s,struct wpabuf * resp,u8 srv_trans_id)225325151a3SRui Paulo static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s,
226325151a3SRui Paulo 				struct wpabuf *resp, u8 srv_trans_id)
227325151a3SRui Paulo {
228325151a3SRui Paulo 	struct p2p_srv_bonjour *bsrv;
229325151a3SRui Paulo 	u8 *len_pos;
230325151a3SRui Paulo 
231325151a3SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services");
232325151a3SRui Paulo 
233325151a3SRui Paulo 	if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
234325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
235325151a3SRui Paulo 		return;
236325151a3SRui Paulo 	}
237325151a3SRui Paulo 
238325151a3SRui Paulo 	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
239325151a3SRui Paulo 			 struct p2p_srv_bonjour, list) {
240325151a3SRui Paulo 		if (wpabuf_tailroom(resp) <
241325151a3SRui Paulo 		    5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp))
242325151a3SRui Paulo 			return;
243325151a3SRui Paulo 		/* Length (to be filled) */
244325151a3SRui Paulo 		len_pos = wpabuf_put(resp, 2);
245325151a3SRui Paulo 		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
246325151a3SRui Paulo 		wpabuf_put_u8(resp, srv_trans_id);
247325151a3SRui Paulo 		/* Status Code */
248325151a3SRui Paulo 		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
249325151a3SRui Paulo 		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
250325151a3SRui Paulo 				  wpabuf_head(bsrv->resp),
251325151a3SRui Paulo 				  wpabuf_len(bsrv->resp));
252325151a3SRui Paulo 		/* Response Data */
253325151a3SRui Paulo 		wpabuf_put_buf(resp, bsrv->query); /* Key */
254325151a3SRui Paulo 		wpabuf_put_buf(resp, bsrv->resp); /* Value */
255325151a3SRui Paulo 		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
256325151a3SRui Paulo 			     2);
257325151a3SRui Paulo 	}
258325151a3SRui Paulo }
259325151a3SRui Paulo 
260325151a3SRui Paulo 
match_bonjour_query(struct p2p_srv_bonjour * bsrv,const u8 * query,size_t query_len)261325151a3SRui Paulo static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query,
262325151a3SRui Paulo 			       size_t query_len)
263325151a3SRui Paulo {
264325151a3SRui Paulo 	char str_rx[256], str_srv[256];
265325151a3SRui Paulo 
266325151a3SRui Paulo 	if (query_len < 3 || wpabuf_len(bsrv->query) < 3)
267325151a3SRui Paulo 		return 0; /* Too short to include DNS Type and Version */
268325151a3SRui Paulo 	if (os_memcmp(query + query_len - 3,
269325151a3SRui Paulo 		      wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3,
270325151a3SRui Paulo 		      3) != 0)
271325151a3SRui Paulo 		return 0; /* Mismatch in DNS Type or Version */
272325151a3SRui Paulo 	if (query_len == wpabuf_len(bsrv->query) &&
273325151a3SRui Paulo 	    os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0)
274325151a3SRui Paulo 		return 1; /* Binary match */
275325151a3SRui Paulo 
276325151a3SRui Paulo 	if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3,
277325151a3SRui Paulo 				  0))
278325151a3SRui Paulo 		return 0; /* Failed to uncompress query */
279325151a3SRui Paulo 	if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv),
280325151a3SRui Paulo 				  wpabuf_head(bsrv->query),
281325151a3SRui Paulo 				  wpabuf_len(bsrv->query) - 3, 0))
282325151a3SRui Paulo 		return 0; /* Failed to uncompress service */
283325151a3SRui Paulo 
284325151a3SRui Paulo 	return os_strcmp(str_rx, str_srv) == 0;
285325151a3SRui Paulo }
286325151a3SRui Paulo 
287325151a3SRui Paulo 
wpas_sd_req_bonjour(struct wpa_supplicant * wpa_s,struct wpabuf * resp,u8 srv_trans_id,const u8 * query,size_t query_len)288325151a3SRui Paulo static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
289325151a3SRui Paulo 				struct wpabuf *resp, u8 srv_trans_id,
290325151a3SRui Paulo 				const u8 *query, size_t query_len)
291325151a3SRui Paulo {
292325151a3SRui Paulo 	struct p2p_srv_bonjour *bsrv;
293325151a3SRui Paulo 	u8 *len_pos;
294325151a3SRui Paulo 	int matches = 0;
295325151a3SRui Paulo 
296325151a3SRui Paulo 	wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour",
297325151a3SRui Paulo 			  query, query_len);
298325151a3SRui Paulo 	if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
299325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
300325151a3SRui Paulo 		wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR,
301325151a3SRui Paulo 					    srv_trans_id);
302325151a3SRui Paulo 		return;
303325151a3SRui Paulo 	}
304325151a3SRui Paulo 
305325151a3SRui Paulo 	if (query_len == 0) {
306325151a3SRui Paulo 		wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
307325151a3SRui Paulo 		return;
308325151a3SRui Paulo 	}
309325151a3SRui Paulo 
310325151a3SRui Paulo 	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
311325151a3SRui Paulo 			 struct p2p_srv_bonjour, list) {
312325151a3SRui Paulo 		if (!match_bonjour_query(bsrv, query, query_len))
313325151a3SRui Paulo 			continue;
314325151a3SRui Paulo 
315325151a3SRui Paulo 		if (wpabuf_tailroom(resp) <
316325151a3SRui Paulo 		    5 + query_len + wpabuf_len(bsrv->resp))
317325151a3SRui Paulo 			return;
318325151a3SRui Paulo 
319325151a3SRui Paulo 		matches++;
320325151a3SRui Paulo 
321325151a3SRui Paulo 		/* Length (to be filled) */
322325151a3SRui Paulo 		len_pos = wpabuf_put(resp, 2);
323325151a3SRui Paulo 		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
324325151a3SRui Paulo 		wpabuf_put_u8(resp, srv_trans_id);
325325151a3SRui Paulo 
326325151a3SRui Paulo 		/* Status Code */
327325151a3SRui Paulo 		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
328325151a3SRui Paulo 		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
329325151a3SRui Paulo 				  wpabuf_head(bsrv->resp),
330325151a3SRui Paulo 				  wpabuf_len(bsrv->resp));
331325151a3SRui Paulo 
332325151a3SRui Paulo 		/* Response Data */
333325151a3SRui Paulo 		wpabuf_put_data(resp, query, query_len); /* Key */
334325151a3SRui Paulo 		wpabuf_put_buf(resp, bsrv->resp); /* Value */
335325151a3SRui Paulo 
336325151a3SRui Paulo 		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
337325151a3SRui Paulo 	}
338325151a3SRui Paulo 
339325151a3SRui Paulo 	if (matches == 0) {
340325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not "
341325151a3SRui Paulo 			   "available");
342325151a3SRui Paulo 		if (wpabuf_tailroom(resp) < 5)
343325151a3SRui Paulo 			return;
344325151a3SRui Paulo 
345325151a3SRui Paulo 		/* Length (to be filled) */
346325151a3SRui Paulo 		len_pos = wpabuf_put(resp, 2);
347325151a3SRui Paulo 		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
348325151a3SRui Paulo 		wpabuf_put_u8(resp, srv_trans_id);
349325151a3SRui Paulo 
350325151a3SRui Paulo 		/* Status Code */
351325151a3SRui Paulo 		wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
352325151a3SRui Paulo 		/* Response Data: empty */
353325151a3SRui Paulo 		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
354325151a3SRui Paulo 			     2);
355325151a3SRui Paulo 	}
356325151a3SRui Paulo }
357325151a3SRui Paulo 
358325151a3SRui Paulo 
wpas_sd_all_upnp(struct wpa_supplicant * wpa_s,struct wpabuf * resp,u8 srv_trans_id)359325151a3SRui Paulo static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s,
360325151a3SRui Paulo 			     struct wpabuf *resp, u8 srv_trans_id)
361325151a3SRui Paulo {
362325151a3SRui Paulo 	struct p2p_srv_upnp *usrv;
363325151a3SRui Paulo 	u8 *len_pos;
364325151a3SRui Paulo 
365325151a3SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services");
366325151a3SRui Paulo 
367325151a3SRui Paulo 	if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
368325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
369325151a3SRui Paulo 		return;
370325151a3SRui Paulo 	}
371325151a3SRui Paulo 
372325151a3SRui Paulo 	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
373325151a3SRui Paulo 			 struct p2p_srv_upnp, list) {
374325151a3SRui Paulo 		if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service))
375325151a3SRui Paulo 			return;
376325151a3SRui Paulo 
377325151a3SRui Paulo 		/* Length (to be filled) */
378325151a3SRui Paulo 		len_pos = wpabuf_put(resp, 2);
379325151a3SRui Paulo 		wpabuf_put_u8(resp, P2P_SERV_UPNP);
380325151a3SRui Paulo 		wpabuf_put_u8(resp, srv_trans_id);
381325151a3SRui Paulo 
382325151a3SRui Paulo 		/* Status Code */
383325151a3SRui Paulo 		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
384325151a3SRui Paulo 		/* Response Data */
385325151a3SRui Paulo 		wpabuf_put_u8(resp, usrv->version);
386325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
387325151a3SRui Paulo 			   usrv->service);
388325151a3SRui Paulo 		wpabuf_put_str(resp, usrv->service);
389325151a3SRui Paulo 		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
390325151a3SRui Paulo 			     2);
391325151a3SRui Paulo 	}
392325151a3SRui Paulo }
393325151a3SRui Paulo 
394325151a3SRui Paulo 
wpas_sd_req_upnp(struct wpa_supplicant * wpa_s,struct wpabuf * resp,u8 srv_trans_id,const u8 * query,size_t query_len)395325151a3SRui Paulo static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
396325151a3SRui Paulo 			     struct wpabuf *resp, u8 srv_trans_id,
397325151a3SRui Paulo 			     const u8 *query, size_t query_len)
398325151a3SRui Paulo {
399325151a3SRui Paulo 	struct p2p_srv_upnp *usrv;
400325151a3SRui Paulo 	u8 *len_pos;
401325151a3SRui Paulo 	u8 version;
402325151a3SRui Paulo 	char *str;
403325151a3SRui Paulo 	int count = 0;
404325151a3SRui Paulo 
405325151a3SRui Paulo 	wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP",
406325151a3SRui Paulo 			  query, query_len);
407325151a3SRui Paulo 
408325151a3SRui Paulo 	if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
409325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
410325151a3SRui Paulo 		wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP,
411325151a3SRui Paulo 					    srv_trans_id);
412325151a3SRui Paulo 		return;
413325151a3SRui Paulo 	}
414325151a3SRui Paulo 
415325151a3SRui Paulo 	if (query_len == 0) {
416325151a3SRui Paulo 		wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
417325151a3SRui Paulo 		return;
418325151a3SRui Paulo 	}
419325151a3SRui Paulo 
420325151a3SRui Paulo 	if (wpabuf_tailroom(resp) < 5)
421325151a3SRui Paulo 		return;
422325151a3SRui Paulo 
423325151a3SRui Paulo 	/* Length (to be filled) */
424325151a3SRui Paulo 	len_pos = wpabuf_put(resp, 2);
425325151a3SRui Paulo 	wpabuf_put_u8(resp, P2P_SERV_UPNP);
426325151a3SRui Paulo 	wpabuf_put_u8(resp, srv_trans_id);
427325151a3SRui Paulo 
428325151a3SRui Paulo 	version = query[0];
429325151a3SRui Paulo 	str = os_malloc(query_len);
430325151a3SRui Paulo 	if (str == NULL)
431325151a3SRui Paulo 		return;
432325151a3SRui Paulo 	os_memcpy(str, query + 1, query_len - 1);
433325151a3SRui Paulo 	str[query_len - 1] = '\0';
434325151a3SRui Paulo 
435325151a3SRui Paulo 	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
436325151a3SRui Paulo 			 struct p2p_srv_upnp, list) {
437325151a3SRui Paulo 		if (version != usrv->version)
438325151a3SRui Paulo 			continue;
439325151a3SRui Paulo 
440325151a3SRui Paulo 		if (os_strcmp(str, "ssdp:all") != 0 &&
441325151a3SRui Paulo 		    os_strstr(usrv->service, str) == NULL)
442325151a3SRui Paulo 			continue;
443325151a3SRui Paulo 
444325151a3SRui Paulo 		if (wpabuf_tailroom(resp) < 2)
445325151a3SRui Paulo 			break;
446325151a3SRui Paulo 		if (count == 0) {
447325151a3SRui Paulo 			/* Status Code */
448325151a3SRui Paulo 			wpabuf_put_u8(resp, P2P_SD_SUCCESS);
449325151a3SRui Paulo 			/* Response Data */
450325151a3SRui Paulo 			wpabuf_put_u8(resp, version);
451325151a3SRui Paulo 		} else
452325151a3SRui Paulo 			wpabuf_put_u8(resp, ',');
453325151a3SRui Paulo 
454325151a3SRui Paulo 		count++;
455325151a3SRui Paulo 
456325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
457325151a3SRui Paulo 			   usrv->service);
458325151a3SRui Paulo 		if (wpabuf_tailroom(resp) < os_strlen(usrv->service))
459325151a3SRui Paulo 			break;
460325151a3SRui Paulo 		wpabuf_put_str(resp, usrv->service);
461325151a3SRui Paulo 	}
462325151a3SRui Paulo 	os_free(str);
463325151a3SRui Paulo 
464325151a3SRui Paulo 	if (count == 0) {
465325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
466325151a3SRui Paulo 			   "available");
467325151a3SRui Paulo 		/* Status Code */
468325151a3SRui Paulo 		wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
469325151a3SRui Paulo 		/* Response Data: empty */
470325151a3SRui Paulo 	}
471325151a3SRui Paulo 
472325151a3SRui Paulo 	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
473325151a3SRui Paulo }
474325151a3SRui Paulo 
475325151a3SRui Paulo 
476325151a3SRui Paulo #ifdef CONFIG_WIFI_DISPLAY
wpas_sd_req_wfd(struct wpa_supplicant * wpa_s,struct wpabuf * resp,u8 srv_trans_id,const u8 * query,size_t query_len)477325151a3SRui Paulo static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s,
478325151a3SRui Paulo 			    struct wpabuf *resp, u8 srv_trans_id,
479325151a3SRui Paulo 			    const u8 *query, size_t query_len)
480325151a3SRui Paulo {
481325151a3SRui Paulo 	const u8 *pos;
482325151a3SRui Paulo 	u8 role;
483325151a3SRui Paulo 	u8 *len_pos;
484325151a3SRui Paulo 
485325151a3SRui Paulo 	wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len);
486325151a3SRui Paulo 
487325151a3SRui Paulo 	if (!wpa_s->global->wifi_display) {
488325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available");
489325151a3SRui Paulo 		wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY,
490325151a3SRui Paulo 					    srv_trans_id);
491325151a3SRui Paulo 		return;
492325151a3SRui Paulo 	}
493325151a3SRui Paulo 
494325151a3SRui Paulo 	if (query_len < 1) {
495325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device "
496325151a3SRui Paulo 			   "Role");
497325151a3SRui Paulo 		return;
498325151a3SRui Paulo 	}
499325151a3SRui Paulo 
500325151a3SRui Paulo 	if (wpabuf_tailroom(resp) < 5)
501325151a3SRui Paulo 		return;
502325151a3SRui Paulo 
503325151a3SRui Paulo 	pos = query;
504325151a3SRui Paulo 	role = *pos++;
505325151a3SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role);
506325151a3SRui Paulo 
507325151a3SRui Paulo 	/* TODO: role specific handling */
508325151a3SRui Paulo 
509325151a3SRui Paulo 	/* Length (to be filled) */
510325151a3SRui Paulo 	len_pos = wpabuf_put(resp, 2);
511325151a3SRui Paulo 	wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY);
512325151a3SRui Paulo 	wpabuf_put_u8(resp, srv_trans_id);
513325151a3SRui Paulo 	wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */
514325151a3SRui Paulo 
515325151a3SRui Paulo 	while (pos < query + query_len) {
516325151a3SRui Paulo 		if (*pos < MAX_WFD_SUBELEMS &&
517325151a3SRui Paulo 		    wpa_s->global->wfd_subelem[*pos] &&
518325151a3SRui Paulo 		    wpabuf_tailroom(resp) >=
519325151a3SRui Paulo 		    wpabuf_len(wpa_s->global->wfd_subelem[*pos])) {
520325151a3SRui Paulo 			wpa_printf(MSG_DEBUG, "P2P: Add WSD response "
521325151a3SRui Paulo 				   "subelement %u", *pos);
522325151a3SRui Paulo 			wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]);
523325151a3SRui Paulo 		}
524325151a3SRui Paulo 		pos++;
525325151a3SRui Paulo 	}
526325151a3SRui Paulo 
527325151a3SRui Paulo 	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
528325151a3SRui Paulo }
529325151a3SRui Paulo #endif /* CONFIG_WIFI_DISPLAY */
530325151a3SRui Paulo 
531325151a3SRui Paulo 
find_p2ps_substr(struct p2ps_advertisement * adv_data,const u8 * needle,size_t needle_len)532325151a3SRui Paulo static int find_p2ps_substr(struct p2ps_advertisement *adv_data,
533325151a3SRui Paulo 			    const u8 *needle, size_t needle_len)
534325151a3SRui Paulo {
535325151a3SRui Paulo 	const u8 *haystack = (const u8 *) adv_data->svc_info;
536325151a3SRui Paulo 	size_t haystack_len, i;
537325151a3SRui Paulo 
538325151a3SRui Paulo 	/* Allow search term to be empty */
539325151a3SRui Paulo 	if (!needle || !needle_len)
540325151a3SRui Paulo 		return 1;
541325151a3SRui Paulo 
542325151a3SRui Paulo 	if (!haystack)
543325151a3SRui Paulo 		return 0;
544325151a3SRui Paulo 
545325151a3SRui Paulo 	haystack_len = os_strlen(adv_data->svc_info);
546325151a3SRui Paulo 	for (i = 0; i < haystack_len; i++) {
547325151a3SRui Paulo 		if (haystack_len - i < needle_len)
548325151a3SRui Paulo 			break;
549325151a3SRui Paulo 		if (os_memcmp(haystack + i, needle, needle_len) == 0)
550325151a3SRui Paulo 			return 1;
551325151a3SRui Paulo 	}
552325151a3SRui Paulo 
553325151a3SRui Paulo 	return 0;
554325151a3SRui Paulo }
555325151a3SRui Paulo 
556325151a3SRui Paulo 
wpas_sd_req_asp(struct wpa_supplicant * wpa_s,struct wpabuf * resp,u8 srv_trans_id,const u8 * query,size_t query_len)557325151a3SRui Paulo static void wpas_sd_req_asp(struct wpa_supplicant *wpa_s,
558325151a3SRui Paulo 			    struct wpabuf *resp, u8 srv_trans_id,
559325151a3SRui Paulo 			    const u8 *query, size_t query_len)
560325151a3SRui Paulo {
561325151a3SRui Paulo 	struct p2ps_advertisement *adv_data;
562325151a3SRui Paulo 	const u8 *svc = &query[1];
563325151a3SRui Paulo 	const u8 *info = NULL;
564325151a3SRui Paulo 	size_t svc_len = query[0];
565325151a3SRui Paulo 	size_t info_len = 0;
566325151a3SRui Paulo 	int prefix = 0;
567325151a3SRui Paulo 	u8 *count_pos = NULL;
568325151a3SRui Paulo 	u8 *len_pos = NULL;
569325151a3SRui Paulo 
570325151a3SRui Paulo 	wpa_hexdump(MSG_DEBUG, "P2P: SD Request for ASP", query, query_len);
571325151a3SRui Paulo 
572325151a3SRui Paulo 	if (!wpa_s->global->p2p) {
573325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: ASP protocol not available");
574325151a3SRui Paulo 		wpas_sd_add_proto_not_avail(resp, P2P_SERV_P2PS, srv_trans_id);
575325151a3SRui Paulo 		return;
576325151a3SRui Paulo 	}
577325151a3SRui Paulo 
578325151a3SRui Paulo 	/* Info block is optional */
579325151a3SRui Paulo 	if (svc_len + 1 < query_len) {
580325151a3SRui Paulo 		info = &svc[svc_len];
581325151a3SRui Paulo 		info_len = *info++;
582325151a3SRui Paulo 	}
583325151a3SRui Paulo 
584325151a3SRui Paulo 	/* Range check length of svc string and info block */
585325151a3SRui Paulo 	if (svc_len + (info_len ? info_len + 2 : 1) > query_len) {
586325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: ASP bad request");
587325151a3SRui Paulo 		wpas_sd_add_bad_request(resp, P2P_SERV_P2PS, srv_trans_id);
588325151a3SRui Paulo 		return;
589325151a3SRui Paulo 	}
590325151a3SRui Paulo 
591325151a3SRui Paulo 	/* Detect and correct for prefix search */
592325151a3SRui Paulo 	if (svc_len && svc[svc_len - 1] == '*') {
593325151a3SRui Paulo 		prefix = 1;
594325151a3SRui Paulo 		svc_len--;
595325151a3SRui Paulo 	}
596325151a3SRui Paulo 
597325151a3SRui Paulo 	for (adv_data = p2p_get_p2ps_adv_list(wpa_s->global->p2p);
598325151a3SRui Paulo 	     adv_data; adv_data = adv_data->next) {
599325151a3SRui Paulo 		/* If not a prefix match, reject length mismatches */
600325151a3SRui Paulo 		if (!prefix && svc_len != os_strlen(adv_data->svc_name))
601325151a3SRui Paulo 			continue;
602325151a3SRui Paulo 
603325151a3SRui Paulo 		/* Search each service for request */
604325151a3SRui Paulo 		if (os_memcmp(adv_data->svc_name, svc, svc_len) == 0 &&
605325151a3SRui Paulo 		    find_p2ps_substr(adv_data, info, info_len)) {
606325151a3SRui Paulo 			size_t len = os_strlen(adv_data->svc_name);
607325151a3SRui Paulo 			size_t svc_info_len = 0;
608325151a3SRui Paulo 
609325151a3SRui Paulo 			if (adv_data->svc_info)
610325151a3SRui Paulo 				svc_info_len = os_strlen(adv_data->svc_info);
611325151a3SRui Paulo 
612325151a3SRui Paulo 			if (len > 0xff || svc_info_len > 0xffff)
613325151a3SRui Paulo 				return;
614325151a3SRui Paulo 
615325151a3SRui Paulo 			/* Length & Count to be filled as we go */
616325151a3SRui Paulo 			if (!len_pos && !count_pos) {
617325151a3SRui Paulo 				if (wpabuf_tailroom(resp) <
618325151a3SRui Paulo 				    len + svc_info_len + 16)
619325151a3SRui Paulo 					return;
620325151a3SRui Paulo 
621325151a3SRui Paulo 				len_pos = wpabuf_put(resp, 2);
622325151a3SRui Paulo 				wpabuf_put_u8(resp, P2P_SERV_P2PS);
623325151a3SRui Paulo 				wpabuf_put_u8(resp, srv_trans_id);
624325151a3SRui Paulo 				/* Status Code */
625325151a3SRui Paulo 				wpabuf_put_u8(resp, P2P_SD_SUCCESS);
626325151a3SRui Paulo 				count_pos = wpabuf_put(resp, 1);
627325151a3SRui Paulo 				*count_pos = 0;
628325151a3SRui Paulo 			} else if (wpabuf_tailroom(resp) <
629325151a3SRui Paulo 				   len + svc_info_len + 10)
630325151a3SRui Paulo 				return;
631325151a3SRui Paulo 
632325151a3SRui Paulo 			if (svc_info_len) {
633325151a3SRui Paulo 				wpa_printf(MSG_DEBUG,
634325151a3SRui Paulo 					   "P2P: Add Svc: %s info: %s",
635325151a3SRui Paulo 					   adv_data->svc_name,
636325151a3SRui Paulo 					   adv_data->svc_info);
637325151a3SRui Paulo 			} else {
638325151a3SRui Paulo 				wpa_printf(MSG_DEBUG, "P2P: Add Svc: %s",
639325151a3SRui Paulo 					   adv_data->svc_name);
640325151a3SRui Paulo 			}
641325151a3SRui Paulo 
642325151a3SRui Paulo 			/* Advertisement ID */
643325151a3SRui Paulo 			wpabuf_put_le32(resp, adv_data->id);
644325151a3SRui Paulo 
645325151a3SRui Paulo 			/* Config Methods */
646325151a3SRui Paulo 			wpabuf_put_be16(resp, adv_data->config_methods);
647325151a3SRui Paulo 
648325151a3SRui Paulo 			/* Service Name */
649325151a3SRui Paulo 			wpabuf_put_u8(resp, (u8) len);
650325151a3SRui Paulo 			wpabuf_put_data(resp, adv_data->svc_name, len);
651325151a3SRui Paulo 
652325151a3SRui Paulo 			/* Service State */
653325151a3SRui Paulo 			wpabuf_put_u8(resp, adv_data->state);
654325151a3SRui Paulo 
655325151a3SRui Paulo 			/* Service Information */
656325151a3SRui Paulo 			wpabuf_put_le16(resp, (u16) svc_info_len);
657325151a3SRui Paulo 			wpabuf_put_data(resp, adv_data->svc_info, svc_info_len);
658325151a3SRui Paulo 
659325151a3SRui Paulo 			/* Update length and count */
660325151a3SRui Paulo 			(*count_pos)++;
661325151a3SRui Paulo 			WPA_PUT_LE16(len_pos,
662325151a3SRui Paulo 				     (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
663325151a3SRui Paulo 		}
664325151a3SRui Paulo 	}
665325151a3SRui Paulo 
666325151a3SRui Paulo 	/* Return error if no matching svc found */
667325151a3SRui Paulo 	if (count_pos == NULL) {
668325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: ASP service not found");
669325151a3SRui Paulo 		wpas_sd_add_not_found(resp, P2P_SERV_P2PS, srv_trans_id);
670325151a3SRui Paulo 	}
671325151a3SRui Paulo }
672325151a3SRui Paulo 
673325151a3SRui Paulo 
wpas_sd_all_asp(struct wpa_supplicant * wpa_s,struct wpabuf * resp,u8 srv_trans_id)674325151a3SRui Paulo static void wpas_sd_all_asp(struct wpa_supplicant *wpa_s,
675325151a3SRui Paulo 			    struct wpabuf *resp, u8 srv_trans_id)
676325151a3SRui Paulo {
677325151a3SRui Paulo 	/* Query data to add all P2PS advertisements:
678325151a3SRui Paulo 	 *  - Service name length: 1
679325151a3SRui Paulo 	 *  - Service name: '*'
680325151a3SRui Paulo 	 *  - Service Information Request Length: 0
681325151a3SRui Paulo 	 */
682325151a3SRui Paulo 	const u8 q[] = { 1, (const u8) '*', 0 };
683325151a3SRui Paulo 
684325151a3SRui Paulo 	if (p2p_get_p2ps_adv_list(wpa_s->global->p2p))
685325151a3SRui Paulo 		wpas_sd_req_asp(wpa_s, resp, srv_trans_id, q, sizeof(q));
686325151a3SRui Paulo }
687325151a3SRui Paulo 
688325151a3SRui Paulo 
wpas_sd_request(void * ctx,int freq,const u8 * sa,u8 dialog_token,u16 update_indic,const u8 * tlvs,size_t tlvs_len)689325151a3SRui Paulo void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
690325151a3SRui Paulo 		     u16 update_indic, const u8 *tlvs, size_t tlvs_len)
691325151a3SRui Paulo {
692325151a3SRui Paulo 	struct wpa_supplicant *wpa_s = ctx;
693325151a3SRui Paulo 	const u8 *pos = tlvs;
694325151a3SRui Paulo 	const u8 *end = tlvs + tlvs_len;
695325151a3SRui Paulo 	const u8 *tlv_end;
696325151a3SRui Paulo 	u16 slen;
697325151a3SRui Paulo 	struct wpabuf *resp;
698325151a3SRui Paulo 	u8 srv_proto, srv_trans_id;
699325151a3SRui Paulo 	size_t buf_len;
700325151a3SRui Paulo 	char *buf;
701325151a3SRui Paulo 
702325151a3SRui Paulo 	wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs",
703325151a3SRui Paulo 		    tlvs, tlvs_len);
704325151a3SRui Paulo 	buf_len = 2 * tlvs_len + 1;
705325151a3SRui Paulo 	buf = os_malloc(buf_len);
706325151a3SRui Paulo 	if (buf) {
707325151a3SRui Paulo 		wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
708325151a3SRui Paulo 		wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d "
709325151a3SRui Paulo 			     MACSTR " %u %u %s",
710325151a3SRui Paulo 			     freq, MAC2STR(sa), dialog_token, update_indic,
711325151a3SRui Paulo 			     buf);
712325151a3SRui Paulo 		os_free(buf);
713325151a3SRui Paulo 	}
714325151a3SRui Paulo 
715325151a3SRui Paulo 	if (wpa_s->p2p_sd_over_ctrl_iface) {
716325151a3SRui Paulo 		wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
717325151a3SRui Paulo 					   update_indic, tlvs, tlvs_len);
718325151a3SRui Paulo 		return; /* to be processed by an external program */
719325151a3SRui Paulo 	}
720325151a3SRui Paulo 
721325151a3SRui Paulo 	resp = wpabuf_alloc(10000);
722325151a3SRui Paulo 	if (resp == NULL)
723325151a3SRui Paulo 		return;
724325151a3SRui Paulo 
725780fb4a2SCy Schubert 	while (end - pos > 1) {
726325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
727325151a3SRui Paulo 		slen = WPA_GET_LE16(pos);
728325151a3SRui Paulo 		pos += 2;
729780fb4a2SCy Schubert 		if (slen > end - pos || slen < 2) {
730325151a3SRui Paulo 			wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
731325151a3SRui Paulo 				   "length");
732325151a3SRui Paulo 			wpabuf_free(resp);
733325151a3SRui Paulo 			return;
734325151a3SRui Paulo 		}
735325151a3SRui Paulo 		tlv_end = pos + slen;
736325151a3SRui Paulo 
737325151a3SRui Paulo 		srv_proto = *pos++;
738325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
739325151a3SRui Paulo 			   srv_proto);
740325151a3SRui Paulo 		srv_trans_id = *pos++;
741325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
742325151a3SRui Paulo 			   srv_trans_id);
743325151a3SRui Paulo 
744325151a3SRui Paulo 		wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
745325151a3SRui Paulo 			    pos, tlv_end - pos);
746325151a3SRui Paulo 
747325151a3SRui Paulo 
748325151a3SRui Paulo 		if (wpa_s->force_long_sd) {
749325151a3SRui Paulo 			wpa_printf(MSG_DEBUG, "P2P: SD test - force long "
750325151a3SRui Paulo 				   "response");
751325151a3SRui Paulo 			wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
752325151a3SRui Paulo 			wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
753325151a3SRui Paulo 			wpas_sd_all_asp(wpa_s, resp, srv_trans_id);
754325151a3SRui Paulo 			goto done;
755325151a3SRui Paulo 		}
756325151a3SRui Paulo 
757325151a3SRui Paulo 		switch (srv_proto) {
758325151a3SRui Paulo 		case P2P_SERV_ALL_SERVICES:
759325151a3SRui Paulo 			wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
760325151a3SRui Paulo 				   "for all services");
761325151a3SRui Paulo 			if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) &&
762325151a3SRui Paulo 			    dl_list_empty(&wpa_s->global->p2p_srv_bonjour) &&
763325151a3SRui Paulo 			    !p2p_get_p2ps_adv_list(wpa_s->global->p2p)) {
764325151a3SRui Paulo 				wpa_printf(MSG_DEBUG, "P2P: No service "
765325151a3SRui Paulo 					   "discovery protocols available");
766325151a3SRui Paulo 				wpas_sd_add_proto_not_avail(
767325151a3SRui Paulo 					resp, P2P_SERV_ALL_SERVICES,
768325151a3SRui Paulo 					srv_trans_id);
769325151a3SRui Paulo 				break;
770325151a3SRui Paulo 			}
771325151a3SRui Paulo 			wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
772325151a3SRui Paulo 			wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
773325151a3SRui Paulo 			wpas_sd_all_asp(wpa_s, resp, srv_trans_id);
774325151a3SRui Paulo 			break;
775325151a3SRui Paulo 		case P2P_SERV_BONJOUR:
776325151a3SRui Paulo 			wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id,
777325151a3SRui Paulo 					    pos, tlv_end - pos);
778325151a3SRui Paulo 			break;
779325151a3SRui Paulo 		case P2P_SERV_UPNP:
780325151a3SRui Paulo 			wpas_sd_req_upnp(wpa_s, resp, srv_trans_id,
781325151a3SRui Paulo 					 pos, tlv_end - pos);
782325151a3SRui Paulo 			break;
783325151a3SRui Paulo #ifdef CONFIG_WIFI_DISPLAY
784325151a3SRui Paulo 		case P2P_SERV_WIFI_DISPLAY:
785325151a3SRui Paulo 			wpas_sd_req_wfd(wpa_s, resp, srv_trans_id,
786325151a3SRui Paulo 					pos, tlv_end - pos);
787325151a3SRui Paulo 			break;
788325151a3SRui Paulo #endif /* CONFIG_WIFI_DISPLAY */
789325151a3SRui Paulo 		case P2P_SERV_P2PS:
790325151a3SRui Paulo 			wpas_sd_req_asp(wpa_s, resp, srv_trans_id,
791325151a3SRui Paulo 					pos, tlv_end - pos);
792325151a3SRui Paulo 			break;
793325151a3SRui Paulo 		default:
794325151a3SRui Paulo 			wpa_printf(MSG_DEBUG, "P2P: Unavailable service "
795325151a3SRui Paulo 				   "protocol %u", srv_proto);
796325151a3SRui Paulo 			wpas_sd_add_proto_not_avail(resp, srv_proto,
797325151a3SRui Paulo 						    srv_trans_id);
798325151a3SRui Paulo 			break;
799325151a3SRui Paulo 		}
800325151a3SRui Paulo 
801325151a3SRui Paulo 		pos = tlv_end;
802325151a3SRui Paulo 	}
803325151a3SRui Paulo 
804325151a3SRui Paulo done:
805325151a3SRui Paulo 	wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
806325151a3SRui Paulo 				   update_indic, tlvs, tlvs_len);
807325151a3SRui Paulo 
808325151a3SRui Paulo 	wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
809325151a3SRui Paulo 
810325151a3SRui Paulo 	wpabuf_free(resp);
811325151a3SRui Paulo }
812325151a3SRui Paulo 
813325151a3SRui Paulo 
wpas_sd_p2ps_serv_response(struct wpa_supplicant * wpa_s,const u8 * sa,u8 srv_trans_id,const u8 * pos,const u8 * tlv_end)814325151a3SRui Paulo static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s,
815325151a3SRui Paulo 				       const u8 *sa, u8 srv_trans_id,
816325151a3SRui Paulo 				       const u8 *pos, const u8 *tlv_end)
817325151a3SRui Paulo {
818325151a3SRui Paulo 	u8 left = *pos++;
819325151a3SRui Paulo 	u32 adv_id;
820325151a3SRui Paulo 	u8 svc_status;
821325151a3SRui Paulo 	u16 config_methods;
822325151a3SRui Paulo 	char svc_str[256];
823325151a3SRui Paulo 
824325151a3SRui Paulo 	while (left-- && pos < tlv_end) {
825325151a3SRui Paulo 		char *buf = NULL;
826325151a3SRui Paulo 		size_t buf_len;
827325151a3SRui Paulo 		u8 svc_len;
828325151a3SRui Paulo 
8294b72b91aSCy Schubert 		/* Validity check fixed length+svc_str */
830780fb4a2SCy Schubert 		if (6 >= tlv_end - pos)
831325151a3SRui Paulo 			break;
832325151a3SRui Paulo 		svc_len = pos[6];
833780fb4a2SCy Schubert 		if (svc_len + 10 > tlv_end - pos)
834325151a3SRui Paulo 			break;
835325151a3SRui Paulo 
836325151a3SRui Paulo 		/* Advertisement ID */
837325151a3SRui Paulo 		adv_id = WPA_GET_LE32(pos);
838325151a3SRui Paulo 		pos += sizeof(u32);
839325151a3SRui Paulo 
840325151a3SRui Paulo 		/* Config Methods */
841325151a3SRui Paulo 		config_methods = WPA_GET_BE16(pos);
842325151a3SRui Paulo 		pos += sizeof(u16);
843325151a3SRui Paulo 
844325151a3SRui Paulo 		/* Service Name */
845325151a3SRui Paulo 		pos++; /* svc_len */
846325151a3SRui Paulo 		os_memcpy(svc_str, pos, svc_len);
847325151a3SRui Paulo 		svc_str[svc_len] = '\0';
848325151a3SRui Paulo 		pos += svc_len;
849325151a3SRui Paulo 
850325151a3SRui Paulo 		/* Service Status */
851325151a3SRui Paulo 		svc_status = *pos++;
852325151a3SRui Paulo 
853325151a3SRui Paulo 		/* Service Information Length */
854325151a3SRui Paulo 		buf_len = WPA_GET_LE16(pos);
855325151a3SRui Paulo 		pos += sizeof(u16);
856325151a3SRui Paulo 
8574b72b91aSCy Schubert 		/* Validity check buffer length */
858325151a3SRui Paulo 		if (buf_len > (unsigned int) (tlv_end - pos))
859325151a3SRui Paulo 			break;
860325151a3SRui Paulo 
861325151a3SRui Paulo 		if (buf_len) {
862325151a3SRui Paulo 			buf = os_zalloc(2 * buf_len + 1);
863325151a3SRui Paulo 			if (buf) {
864325151a3SRui Paulo 				utf8_escape((const char *) pos, buf_len, buf,
865325151a3SRui Paulo 					    2 * buf_len + 1);
866325151a3SRui Paulo 			}
867325151a3SRui Paulo 		}
868325151a3SRui Paulo 
869325151a3SRui Paulo 		pos += buf_len;
870325151a3SRui Paulo 
871325151a3SRui Paulo 		if (buf) {
872325151a3SRui Paulo 			wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
873325151a3SRui Paulo 				       MACSTR " %x %x %x %x %s '%s'",
874325151a3SRui Paulo 				       MAC2STR(sa), srv_trans_id, adv_id,
875325151a3SRui Paulo 				       svc_status, config_methods, svc_str,
876325151a3SRui Paulo 				       buf);
877325151a3SRui Paulo 			os_free(buf);
878325151a3SRui Paulo 		} else {
879325151a3SRui Paulo 			wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
880325151a3SRui Paulo 				       MACSTR " %x %x %x %x %s",
881325151a3SRui Paulo 				       MAC2STR(sa), srv_trans_id, adv_id,
882325151a3SRui Paulo 				       svc_status, config_methods, svc_str);
883325151a3SRui Paulo 		}
884325151a3SRui Paulo 	}
885325151a3SRui Paulo }
886325151a3SRui Paulo 
887325151a3SRui Paulo 
wpas_sd_response(void * ctx,const u8 * sa,u16 update_indic,const u8 * tlvs,size_t tlvs_len)888325151a3SRui Paulo void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
889325151a3SRui Paulo 		      const u8 *tlvs, size_t tlvs_len)
890325151a3SRui Paulo {
891325151a3SRui Paulo 	struct wpa_supplicant *wpa_s = ctx;
892325151a3SRui Paulo 	const u8 *pos = tlvs;
893325151a3SRui Paulo 	const u8 *end = tlvs + tlvs_len;
894325151a3SRui Paulo 	const u8 *tlv_end;
895325151a3SRui Paulo 	u16 slen;
896325151a3SRui Paulo 	size_t buf_len;
897325151a3SRui Paulo 	char *buf;
898325151a3SRui Paulo 
899325151a3SRui Paulo 	wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
900325151a3SRui Paulo 		    tlvs, tlvs_len);
901325151a3SRui Paulo 	if (tlvs_len > 1500) {
902325151a3SRui Paulo 		/* TODO: better way for handling this */
903325151a3SRui Paulo 		wpa_msg_ctrl(wpa_s, MSG_INFO,
904325151a3SRui Paulo 			     P2P_EVENT_SERV_DISC_RESP MACSTR
905325151a3SRui Paulo 			     " %u <long response: %u bytes>",
906325151a3SRui Paulo 			     MAC2STR(sa), update_indic,
907325151a3SRui Paulo 			     (unsigned int) tlvs_len);
908325151a3SRui Paulo 	} else {
909325151a3SRui Paulo 		buf_len = 2 * tlvs_len + 1;
910325151a3SRui Paulo 		buf = os_malloc(buf_len);
911325151a3SRui Paulo 		if (buf) {
912325151a3SRui Paulo 			wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
913325151a3SRui Paulo 			wpa_msg_ctrl(wpa_s, MSG_INFO,
914325151a3SRui Paulo 				     P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s",
915325151a3SRui Paulo 				     MAC2STR(sa), update_indic, buf);
916325151a3SRui Paulo 			os_free(buf);
917325151a3SRui Paulo 		}
918325151a3SRui Paulo 	}
919325151a3SRui Paulo 
920780fb4a2SCy Schubert 	while (end - pos >= 2) {
921325151a3SRui Paulo 		u8 srv_proto, srv_trans_id, status;
922325151a3SRui Paulo 
923325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
924325151a3SRui Paulo 		slen = WPA_GET_LE16(pos);
925325151a3SRui Paulo 		pos += 2;
926780fb4a2SCy Schubert 		if (slen > end - pos || slen < 3) {
927325151a3SRui Paulo 			wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
928325151a3SRui Paulo 				   "length");
929325151a3SRui Paulo 			return;
930325151a3SRui Paulo 		}
931325151a3SRui Paulo 		tlv_end = pos + slen;
932325151a3SRui Paulo 
933325151a3SRui Paulo 		srv_proto = *pos++;
934325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
935325151a3SRui Paulo 			   srv_proto);
936325151a3SRui Paulo 		srv_trans_id = *pos++;
937325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
938325151a3SRui Paulo 			   srv_trans_id);
939325151a3SRui Paulo 		status = *pos++;
940325151a3SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u",
941325151a3SRui Paulo 			   status);
942325151a3SRui Paulo 
943325151a3SRui Paulo 		wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data",
944325151a3SRui Paulo 			    pos, tlv_end - pos);
945325151a3SRui Paulo 
946325151a3SRui Paulo 		if (srv_proto == P2P_SERV_P2PS && pos < tlv_end) {
947325151a3SRui Paulo 			wpas_sd_p2ps_serv_response(wpa_s, sa, srv_trans_id,
948325151a3SRui Paulo 						   pos, tlv_end);
949325151a3SRui Paulo 		}
950325151a3SRui Paulo 
951325151a3SRui Paulo 		pos = tlv_end;
952325151a3SRui Paulo 	}
953325151a3SRui Paulo 
954325151a3SRui Paulo 	wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len);
955325151a3SRui Paulo }
956325151a3SRui Paulo 
957325151a3SRui Paulo 
wpas_p2p_sd_request(struct wpa_supplicant * wpa_s,const u8 * dst,const struct wpabuf * tlvs)958325151a3SRui Paulo u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
959325151a3SRui Paulo 			const struct wpabuf *tlvs)
960325151a3SRui Paulo {
961325151a3SRui Paulo 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
962325151a3SRui Paulo 		return 0;
963325151a3SRui Paulo 	return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs);
964325151a3SRui Paulo }
965325151a3SRui Paulo 
966325151a3SRui Paulo 
wpas_p2p_sd_request_upnp(struct wpa_supplicant * wpa_s,const u8 * dst,u8 version,const char * query)967325151a3SRui Paulo u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
968325151a3SRui Paulo 			     u8 version, const char *query)
969325151a3SRui Paulo {
970325151a3SRui Paulo 	struct wpabuf *tlvs;
971325151a3SRui Paulo 	u64 ret;
972325151a3SRui Paulo 
973325151a3SRui Paulo 	tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query));
974325151a3SRui Paulo 	if (tlvs == NULL)
975325151a3SRui Paulo 		return 0;
976325151a3SRui Paulo 	wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query));
977325151a3SRui Paulo 	wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */
978325151a3SRui Paulo 	wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */
979325151a3SRui Paulo 	wpabuf_put_u8(tlvs, version);
980325151a3SRui Paulo 	wpabuf_put_str(tlvs, query);
981325151a3SRui Paulo 	ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
982325151a3SRui Paulo 	wpabuf_free(tlvs);
983325151a3SRui Paulo 	return ret;
984325151a3SRui Paulo }
985325151a3SRui Paulo 
986325151a3SRui Paulo 
wpas_p2p_sd_request_asp(struct wpa_supplicant * wpa_s,const u8 * dst,u8 id,const char * svc_str,const char * info_substr)987325151a3SRui Paulo u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id,
988325151a3SRui Paulo 			    const char *svc_str, const char *info_substr)
989325151a3SRui Paulo {
990325151a3SRui Paulo 	struct wpabuf *tlvs;
991325151a3SRui Paulo 	size_t plen, svc_len, substr_len = 0;
992325151a3SRui Paulo 	u64 ret;
993325151a3SRui Paulo 
994325151a3SRui Paulo 	svc_len = os_strlen(svc_str);
995325151a3SRui Paulo 	if (info_substr)
996325151a3SRui Paulo 		substr_len = os_strlen(info_substr);
997325151a3SRui Paulo 
998325151a3SRui Paulo 	if (svc_len > 0xff || substr_len > 0xff)
999325151a3SRui Paulo 		return 0;
1000325151a3SRui Paulo 
1001325151a3SRui Paulo 	plen = 1 + 1 + 1 + svc_len + 1 + substr_len;
1002325151a3SRui Paulo 	tlvs = wpabuf_alloc(2 + plen);
1003325151a3SRui Paulo 	if (tlvs == NULL)
1004325151a3SRui Paulo 		return 0;
1005325151a3SRui Paulo 
1006325151a3SRui Paulo 	wpabuf_put_le16(tlvs, plen);
1007325151a3SRui Paulo 	wpabuf_put_u8(tlvs, P2P_SERV_P2PS);
1008325151a3SRui Paulo 	wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
1009325151a3SRui Paulo 	wpabuf_put_u8(tlvs, (u8) svc_len); /* Service String Length */
1010325151a3SRui Paulo 	wpabuf_put_data(tlvs, svc_str, svc_len);
1011325151a3SRui Paulo 	wpabuf_put_u8(tlvs, (u8) substr_len); /* Info Substring Length */
1012325151a3SRui Paulo 	wpabuf_put_data(tlvs, info_substr, substr_len);
1013325151a3SRui Paulo 	ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
1014325151a3SRui Paulo 	wpabuf_free(tlvs);
1015325151a3SRui Paulo 
1016325151a3SRui Paulo 	return ret;
1017325151a3SRui Paulo }
1018325151a3SRui Paulo 
1019325151a3SRui Paulo 
1020325151a3SRui Paulo #ifdef CONFIG_WIFI_DISPLAY
1021325151a3SRui Paulo 
wpas_p2p_sd_request_wfd(struct wpa_supplicant * wpa_s,const u8 * dst,const struct wpabuf * tlvs)1022325151a3SRui Paulo static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst,
1023325151a3SRui Paulo 				   const struct wpabuf *tlvs)
1024325151a3SRui Paulo {
1025325151a3SRui Paulo 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
1026325151a3SRui Paulo 		return 0;
1027325151a3SRui Paulo 	return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs);
1028325151a3SRui Paulo }
1029325151a3SRui Paulo 
1030325151a3SRui Paulo 
1031325151a3SRui Paulo #define MAX_WFD_SD_SUBELEMS 20
1032325151a3SRui Paulo 
wfd_add_sd_req_role(struct wpabuf * tlvs,u8 id,u8 role,const char * subelems)1033325151a3SRui Paulo static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role,
1034325151a3SRui Paulo 				const char *subelems)
1035325151a3SRui Paulo {
1036325151a3SRui Paulo 	u8 *len;
1037325151a3SRui Paulo 	const char *pos;
1038325151a3SRui Paulo 	int val;
1039325151a3SRui Paulo 	int count = 0;
1040325151a3SRui Paulo 
1041325151a3SRui Paulo 	len = wpabuf_put(tlvs, 2);
1042325151a3SRui Paulo 	wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */
1043325151a3SRui Paulo 	wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
1044325151a3SRui Paulo 
1045325151a3SRui Paulo 	wpabuf_put_u8(tlvs, role);
1046325151a3SRui Paulo 
1047325151a3SRui Paulo 	pos = subelems;
1048325151a3SRui Paulo 	while (*pos) {
1049325151a3SRui Paulo 		val = atoi(pos);
1050325151a3SRui Paulo 		if (val >= 0 && val < 256) {
1051325151a3SRui Paulo 			wpabuf_put_u8(tlvs, val);
1052325151a3SRui Paulo 			count++;
1053325151a3SRui Paulo 			if (count == MAX_WFD_SD_SUBELEMS)
1054325151a3SRui Paulo 				break;
1055325151a3SRui Paulo 		}
1056325151a3SRui Paulo 		pos = os_strchr(pos + 1, ',');
1057325151a3SRui Paulo 		if (pos == NULL)
1058325151a3SRui Paulo 			break;
1059325151a3SRui Paulo 		pos++;
1060325151a3SRui Paulo 	}
1061325151a3SRui Paulo 
1062325151a3SRui Paulo 	WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2);
1063325151a3SRui Paulo }
1064325151a3SRui Paulo 
1065325151a3SRui Paulo 
wpas_p2p_sd_request_wifi_display(struct wpa_supplicant * wpa_s,const u8 * dst,const char * role)1066325151a3SRui Paulo u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
1067325151a3SRui Paulo 				     const u8 *dst, const char *role)
1068325151a3SRui Paulo {
1069325151a3SRui Paulo 	struct wpabuf *tlvs;
1070325151a3SRui Paulo 	u64 ret;
1071325151a3SRui Paulo 	const char *subelems;
1072325151a3SRui Paulo 	u8 id = 1;
1073325151a3SRui Paulo 
1074325151a3SRui Paulo 	subelems = os_strchr(role, ' ');
1075325151a3SRui Paulo 	if (subelems == NULL)
1076325151a3SRui Paulo 		return 0;
1077325151a3SRui Paulo 	subelems++;
1078325151a3SRui Paulo 
1079325151a3SRui Paulo 	tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS));
1080325151a3SRui Paulo 	if (tlvs == NULL)
1081325151a3SRui Paulo 		return 0;
1082325151a3SRui Paulo 
1083325151a3SRui Paulo 	if (os_strstr(role, "[source]"))
1084325151a3SRui Paulo 		wfd_add_sd_req_role(tlvs, id++, 0x00, subelems);
1085325151a3SRui Paulo 	if (os_strstr(role, "[pri-sink]"))
1086325151a3SRui Paulo 		wfd_add_sd_req_role(tlvs, id++, 0x01, subelems);
1087325151a3SRui Paulo 	if (os_strstr(role, "[sec-sink]"))
1088325151a3SRui Paulo 		wfd_add_sd_req_role(tlvs, id++, 0x02, subelems);
1089325151a3SRui Paulo 	if (os_strstr(role, "[source+sink]"))
1090325151a3SRui Paulo 		wfd_add_sd_req_role(tlvs, id++, 0x03, subelems);
1091325151a3SRui Paulo 
1092325151a3SRui Paulo 	ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs);
1093325151a3SRui Paulo 	wpabuf_free(tlvs);
1094325151a3SRui Paulo 	return ret;
1095325151a3SRui Paulo }
1096325151a3SRui Paulo 
1097325151a3SRui Paulo #endif /* CONFIG_WIFI_DISPLAY */
1098325151a3SRui Paulo 
1099325151a3SRui Paulo 
wpas_p2p_sd_cancel_request(struct wpa_supplicant * wpa_s,u64 req)1100325151a3SRui Paulo int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req)
1101325151a3SRui Paulo {
1102325151a3SRui Paulo 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
1103325151a3SRui Paulo 		return -1;
1104325151a3SRui Paulo 	return p2p_sd_cancel_request(wpa_s->global->p2p,
1105325151a3SRui Paulo 				     (void *) (uintptr_t) req);
1106325151a3SRui Paulo }
1107325151a3SRui Paulo 
1108325151a3SRui Paulo 
wpas_p2p_sd_response(struct wpa_supplicant * wpa_s,int freq,const u8 * dst,u8 dialog_token,const struct wpabuf * resp_tlvs)1109325151a3SRui Paulo void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
1110325151a3SRui Paulo 			  const u8 *dst, u8 dialog_token,
1111325151a3SRui Paulo 			  const struct wpabuf *resp_tlvs)
1112325151a3SRui Paulo {
1113325151a3SRui Paulo 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
1114325151a3SRui Paulo 		return;
1115325151a3SRui Paulo 	p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
1116325151a3SRui Paulo 			resp_tlvs);
1117325151a3SRui Paulo }
1118325151a3SRui Paulo 
1119325151a3SRui Paulo 
wpas_p2p_sd_service_update(struct wpa_supplicant * wpa_s)1120325151a3SRui Paulo void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
1121325151a3SRui Paulo {
1122325151a3SRui Paulo 	if (wpa_s->global->p2p)
1123325151a3SRui Paulo 		p2p_sd_service_update(wpa_s->global->p2p);
1124325151a3SRui Paulo }
1125325151a3SRui Paulo 
1126325151a3SRui Paulo 
wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour * bsrv)1127325151a3SRui Paulo static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv)
1128325151a3SRui Paulo {
1129325151a3SRui Paulo 	dl_list_del(&bsrv->list);
1130325151a3SRui Paulo 	wpabuf_free(bsrv->query);
1131325151a3SRui Paulo 	wpabuf_free(bsrv->resp);
1132325151a3SRui Paulo 	os_free(bsrv);
1133325151a3SRui Paulo }
1134325151a3SRui Paulo 
1135325151a3SRui Paulo 
wpas_p2p_srv_upnp_free(struct p2p_srv_upnp * usrv)1136325151a3SRui Paulo static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv)
1137325151a3SRui Paulo {
1138325151a3SRui Paulo 	dl_list_del(&usrv->list);
1139325151a3SRui Paulo 	os_free(usrv->service);
1140325151a3SRui Paulo 	os_free(usrv);
1141325151a3SRui Paulo }
1142325151a3SRui Paulo 
1143325151a3SRui Paulo 
wpas_p2p_service_flush(struct wpa_supplicant * wpa_s)1144325151a3SRui Paulo void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s)
1145325151a3SRui Paulo {
1146325151a3SRui Paulo 	struct p2p_srv_bonjour *bsrv, *bn;
1147325151a3SRui Paulo 	struct p2p_srv_upnp *usrv, *un;
1148325151a3SRui Paulo 
1149325151a3SRui Paulo 	dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour,
1150325151a3SRui Paulo 			      struct p2p_srv_bonjour, list)
1151325151a3SRui Paulo 		wpas_p2p_srv_bonjour_free(bsrv);
1152325151a3SRui Paulo 
1153325151a3SRui Paulo 	dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp,
1154325151a3SRui Paulo 			      struct p2p_srv_upnp, list)
1155325151a3SRui Paulo 		wpas_p2p_srv_upnp_free(usrv);
1156325151a3SRui Paulo 
1157325151a3SRui Paulo 	wpas_p2p_service_flush_asp(wpa_s);
1158325151a3SRui Paulo 	wpas_p2p_sd_service_update(wpa_s);
1159325151a3SRui Paulo }
1160325151a3SRui Paulo 
1161325151a3SRui Paulo 
wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant * wpa_s,u32 adv_id)1162325151a3SRui Paulo int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id)
1163325151a3SRui Paulo {
1164325151a3SRui Paulo 	if (adv_id == 0)
1165325151a3SRui Paulo 		return 1;
1166325151a3SRui Paulo 
1167325151a3SRui Paulo 	if (p2p_service_p2ps_id(wpa_s->global->p2p, adv_id))
1168325151a3SRui Paulo 		return 1;
1169325151a3SRui Paulo 
1170325151a3SRui Paulo 	return 0;
1171325151a3SRui Paulo }
1172325151a3SRui Paulo 
1173325151a3SRui Paulo 
wpas_p2p_service_del_asp(struct wpa_supplicant * wpa_s,u32 adv_id)1174325151a3SRui Paulo int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id)
1175325151a3SRui Paulo {
1176325151a3SRui Paulo 	int ret;
1177325151a3SRui Paulo 
1178325151a3SRui Paulo 	ret = p2p_service_del_asp(wpa_s->global->p2p, adv_id);
1179325151a3SRui Paulo 	if (ret == 0)
1180325151a3SRui Paulo 		wpas_p2p_sd_service_update(wpa_s);
1181325151a3SRui Paulo 	return ret;
1182325151a3SRui Paulo }
1183325151a3SRui Paulo 
1184325151a3SRui Paulo 
wpas_p2p_service_add_asp(struct wpa_supplicant * wpa_s,int auto_accept,u32 adv_id,const char * adv_str,u8 svc_state,u16 config_methods,const char * svc_info,const u8 * cpt_priority)1185325151a3SRui Paulo int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s,
1186325151a3SRui Paulo 			     int auto_accept, u32 adv_id,
1187325151a3SRui Paulo 			     const char *adv_str, u8 svc_state,
1188325151a3SRui Paulo 			     u16 config_methods, const char *svc_info,
1189325151a3SRui Paulo 			     const u8 *cpt_priority)
1190325151a3SRui Paulo {
1191325151a3SRui Paulo 	int ret;
1192325151a3SRui Paulo 
1193325151a3SRui Paulo 	ret = p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id,
1194325151a3SRui Paulo 				  adv_str, svc_state, config_methods,
1195325151a3SRui Paulo 				  svc_info, cpt_priority);
1196325151a3SRui Paulo 	if (ret == 0)
1197325151a3SRui Paulo 		wpas_p2p_sd_service_update(wpa_s);
1198325151a3SRui Paulo 	return ret;
1199325151a3SRui Paulo }
1200325151a3SRui Paulo 
1201325151a3SRui Paulo 
wpas_p2p_service_flush_asp(struct wpa_supplicant * wpa_s)1202325151a3SRui Paulo void wpas_p2p_service_flush_asp(struct wpa_supplicant *wpa_s)
1203325151a3SRui Paulo {
1204325151a3SRui Paulo 	p2p_service_flush_asp(wpa_s->global->p2p);
1205325151a3SRui Paulo }
1206325151a3SRui Paulo 
1207325151a3SRui Paulo 
wpas_p2p_service_add_bonjour(struct wpa_supplicant * wpa_s,struct wpabuf * query,struct wpabuf * resp)1208325151a3SRui Paulo int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
1209325151a3SRui Paulo 				 struct wpabuf *query, struct wpabuf *resp)
1210325151a3SRui Paulo {
1211325151a3SRui Paulo 	struct p2p_srv_bonjour *bsrv;
1212325151a3SRui Paulo 
1213325151a3SRui Paulo 	bsrv = os_zalloc(sizeof(*bsrv));
1214325151a3SRui Paulo 	if (bsrv == NULL)
1215325151a3SRui Paulo 		return -1;
1216*a90b9d01SCy Schubert 	bsrv->query = wpabuf_dup(query);
1217*a90b9d01SCy Schubert 	if (!bsrv->query)
1218*a90b9d01SCy Schubert 		goto error_bsrv;
1219*a90b9d01SCy Schubert 	bsrv->resp = wpabuf_dup(resp);
1220*a90b9d01SCy Schubert 	if (!bsrv->resp)
1221*a90b9d01SCy Schubert 		goto error_query;
1222325151a3SRui Paulo 	dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
1223325151a3SRui Paulo 
1224325151a3SRui Paulo 	wpas_p2p_sd_service_update(wpa_s);
1225325151a3SRui Paulo 	return 0;
1226*a90b9d01SCy Schubert 
1227*a90b9d01SCy Schubert error_query:
1228*a90b9d01SCy Schubert 	wpabuf_free(bsrv->query);
1229*a90b9d01SCy Schubert error_bsrv:
1230*a90b9d01SCy Schubert 	os_free(bsrv);
1231*a90b9d01SCy Schubert 	return -1;
1232325151a3SRui Paulo }
1233325151a3SRui Paulo 
1234325151a3SRui Paulo 
wpas_p2p_service_del_bonjour(struct wpa_supplicant * wpa_s,const struct wpabuf * query)1235325151a3SRui Paulo int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
1236325151a3SRui Paulo 				 const struct wpabuf *query)
1237325151a3SRui Paulo {
1238325151a3SRui Paulo 	struct p2p_srv_bonjour *bsrv;
1239325151a3SRui Paulo 
1240325151a3SRui Paulo 	bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
1241325151a3SRui Paulo 	if (bsrv == NULL)
1242325151a3SRui Paulo 		return -1;
1243325151a3SRui Paulo 	wpas_p2p_srv_bonjour_free(bsrv);
1244325151a3SRui Paulo 	wpas_p2p_sd_service_update(wpa_s);
1245325151a3SRui Paulo 	return 0;
1246325151a3SRui Paulo }
1247325151a3SRui Paulo 
1248325151a3SRui Paulo 
wpas_p2p_service_add_upnp(struct wpa_supplicant * wpa_s,u8 version,const char * service)1249325151a3SRui Paulo int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
1250325151a3SRui Paulo 			      const char *service)
1251325151a3SRui Paulo {
1252325151a3SRui Paulo 	struct p2p_srv_upnp *usrv;
1253325151a3SRui Paulo 
1254325151a3SRui Paulo 	if (wpas_p2p_service_get_upnp(wpa_s, version, service))
1255325151a3SRui Paulo 		return 0; /* Already listed */
1256325151a3SRui Paulo 	usrv = os_zalloc(sizeof(*usrv));
1257325151a3SRui Paulo 	if (usrv == NULL)
1258325151a3SRui Paulo 		return -1;
1259325151a3SRui Paulo 	usrv->version = version;
1260325151a3SRui Paulo 	usrv->service = os_strdup(service);
1261325151a3SRui Paulo 	if (usrv->service == NULL) {
1262325151a3SRui Paulo 		os_free(usrv);
1263325151a3SRui Paulo 		return -1;
1264325151a3SRui Paulo 	}
1265325151a3SRui Paulo 	dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list);
1266325151a3SRui Paulo 
1267325151a3SRui Paulo 	wpas_p2p_sd_service_update(wpa_s);
1268325151a3SRui Paulo 	return 0;
1269325151a3SRui Paulo }
1270325151a3SRui Paulo 
1271325151a3SRui Paulo 
wpas_p2p_service_del_upnp(struct wpa_supplicant * wpa_s,u8 version,const char * service)1272325151a3SRui Paulo int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
1273325151a3SRui Paulo 			      const char *service)
1274325151a3SRui Paulo {
1275325151a3SRui Paulo 	struct p2p_srv_upnp *usrv;
1276325151a3SRui Paulo 
1277325151a3SRui Paulo 	usrv = wpas_p2p_service_get_upnp(wpa_s, version, service);
1278325151a3SRui Paulo 	if (usrv == NULL)
1279325151a3SRui Paulo 		return -1;
1280325151a3SRui Paulo 	wpas_p2p_srv_upnp_free(usrv);
1281325151a3SRui Paulo 	wpas_p2p_sd_service_update(wpa_s);
1282325151a3SRui Paulo 	return 0;
1283325151a3SRui Paulo }
1284