12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247 |
- /*
- * wpa_supplicant - P2P service discovery
- * Copyright (c) 2009-2010, Atheros Communications
- * Copyright (c) 2010-2014, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
- #include "utils/includes.h"
- #include "utils/common.h"
- #include "p2p/p2p.h"
- #include "wpa_supplicant_i.h"
- #include "notify.h"
- #include "p2p_supplicant.h"
- /*
- * DNS Header section is used only to calculate compression pointers, so the
- * contents of this data does not matter, but the length needs to be reserved
- * in the virtual packet.
- */
- #define DNS_HEADER_LEN 12
- /*
- * 27-octet in-memory packet from P2P specification containing two implied
- * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN
- */
- #define P2P_SD_IN_MEMORY_LEN 27
- static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start,
- u8 **spos, const u8 *end)
- {
- while (*spos < end) {
- u8 val = ((*spos)[0] & 0xc0) >> 6;
- int len;
- if (val == 1 || val == 2) {
- /* These are reserved values in RFC 1035 */
- wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
- "sequence starting with 0x%x", val);
- return -1;
- }
- if (val == 3) {
- u16 offset;
- u8 *spos_tmp;
- /* Offset */
- if (*spos + 2 > end) {
- wpa_printf(MSG_DEBUG, "P2P: No room for full "
- "DNS offset field");
- return -1;
- }
- offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1];
- if (offset >= *spos - start) {
- wpa_printf(MSG_DEBUG, "P2P: Invalid DNS "
- "pointer offset %u", offset);
- return -1;
- }
- (*spos) += 2;
- spos_tmp = start + offset;
- return p2p_sd_dns_uncompress_label(upos, uend, start,
- &spos_tmp,
- *spos - 2);
- }
- /* Label */
- len = (*spos)[0] & 0x3f;
- if (len == 0)
- return 0;
- (*spos)++;
- if (*spos + len > end) {
- wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
- "sequence - no room for label with length "
- "%u", len);
- return -1;
- }
- if (*upos + len + 2 > uend)
- return -2;
- os_memcpy(*upos, *spos, len);
- *spos += len;
- *upos += len;
- (*upos)[0] = '.';
- (*upos)++;
- (*upos)[0] = '\0';
- }
- return 0;
- }
- /* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet.
- * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is
- * not large enough */
- static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg,
- size_t msg_len, size_t offset)
- {
- /* 27-octet in-memory packet from P2P specification */
- const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01"
- "\x04_udp\xC0\x11\x00\x0C\x00\x01";
- u8 *tmp, *end, *spos;
- char *upos, *uend;
- int ret = 0;
- if (buf_len < 2)
- return -1;
- if (offset > msg_len)
- return -1;
- tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len);
- if (tmp == NULL)
- return -1;
- spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN;
- end = spos + msg_len;
- spos += offset;
- os_memset(tmp, 0, DNS_HEADER_LEN);
- os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN);
- os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len);
- upos = buf;
- uend = buf + buf_len;
- ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end);
- if (ret) {
- os_free(tmp);
- return ret;
- }
- if (upos == buf) {
- upos[0] = '.';
- upos[1] = '\0';
- } else if (upos[-1] == '.')
- upos[-1] = '\0';
- os_free(tmp);
- return 0;
- }
- static struct p2p_srv_bonjour *
- wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s,
- const struct wpabuf *query)
- {
- struct p2p_srv_bonjour *bsrv;
- size_t len;
- len = wpabuf_len(query);
- dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
- struct p2p_srv_bonjour, list) {
- if (len == wpabuf_len(bsrv->query) &&
- os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query),
- len) == 0)
- return bsrv;
- }
- return NULL;
- }
- static struct p2p_srv_upnp *
- wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version,
- const char *service)
- {
- struct p2p_srv_upnp *usrv;
- dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
- struct p2p_srv_upnp, list) {
- if (version == usrv->version &&
- os_strcmp(service, usrv->service) == 0)
- return usrv;
- }
- return NULL;
- }
- static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto,
- u8 srv_trans_id, u8 status)
- {
- u8 *len_pos;
- if (wpabuf_tailroom(resp) < 5)
- return;
- /* Length (to be filled) */
- len_pos = wpabuf_put(resp, 2);
- wpabuf_put_u8(resp, srv_proto);
- wpabuf_put_u8(resp, srv_trans_id);
- /* Status Code */
- wpabuf_put_u8(resp, status);
- /* Response Data: empty */
- WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
- }
- static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
- u8 srv_trans_id)
- {
- wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
- P2P_SD_PROTO_NOT_AVAILABLE);
- }
- static void wpas_sd_add_bad_request(struct wpabuf *resp, u8 srv_proto,
- u8 srv_trans_id)
- {
- wpas_sd_add_empty(resp, srv_proto, srv_trans_id, P2P_SD_BAD_REQUEST);
- }
- static void wpas_sd_add_not_found(struct wpabuf *resp, u8 srv_proto,
- u8 srv_trans_id)
- {
- wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
- P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
- }
- static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s,
- struct wpabuf *resp, u8 srv_trans_id)
- {
- struct p2p_srv_bonjour *bsrv;
- u8 *len_pos;
- wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services");
- if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
- wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
- return;
- }
- dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
- struct p2p_srv_bonjour, list) {
- if (wpabuf_tailroom(resp) <
- 5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp))
- return;
- /* Length (to be filled) */
- len_pos = wpabuf_put(resp, 2);
- wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
- wpabuf_put_u8(resp, srv_trans_id);
- /* Status Code */
- wpabuf_put_u8(resp, P2P_SD_SUCCESS);
- wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
- wpabuf_head(bsrv->resp),
- wpabuf_len(bsrv->resp));
- /* Response Data */
- wpabuf_put_buf(resp, bsrv->query); /* Key */
- wpabuf_put_buf(resp, bsrv->resp); /* Value */
- WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
- 2);
- }
- }
- static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query,
- size_t query_len)
- {
- char str_rx[256], str_srv[256];
- if (query_len < 3 || wpabuf_len(bsrv->query) < 3)
- return 0; /* Too short to include DNS Type and Version */
- if (os_memcmp(query + query_len - 3,
- wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3,
- 3) != 0)
- return 0; /* Mismatch in DNS Type or Version */
- if (query_len == wpabuf_len(bsrv->query) &&
- os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0)
- return 1; /* Binary match */
- if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3,
- 0))
- return 0; /* Failed to uncompress query */
- if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv),
- wpabuf_head(bsrv->query),
- wpabuf_len(bsrv->query) - 3, 0))
- return 0; /* Failed to uncompress service */
- return os_strcmp(str_rx, str_srv) == 0;
- }
- static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
- struct wpabuf *resp, u8 srv_trans_id,
- const u8 *query, size_t query_len)
- {
- struct p2p_srv_bonjour *bsrv;
- u8 *len_pos;
- int matches = 0;
- wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour",
- query, query_len);
- if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
- wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
- wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR,
- srv_trans_id);
- return;
- }
- if (query_len == 0) {
- wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
- return;
- }
- dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
- struct p2p_srv_bonjour, list) {
- if (!match_bonjour_query(bsrv, query, query_len))
- continue;
- if (wpabuf_tailroom(resp) <
- 5 + query_len + wpabuf_len(bsrv->resp))
- return;
- matches++;
- /* Length (to be filled) */
- len_pos = wpabuf_put(resp, 2);
- wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
- wpabuf_put_u8(resp, srv_trans_id);
- /* Status Code */
- wpabuf_put_u8(resp, P2P_SD_SUCCESS);
- wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
- wpabuf_head(bsrv->resp),
- wpabuf_len(bsrv->resp));
- /* Response Data */
- wpabuf_put_data(resp, query, query_len); /* Key */
- wpabuf_put_buf(resp, bsrv->resp); /* Value */
- WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
- }
- if (matches == 0) {
- wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not "
- "available");
- if (wpabuf_tailroom(resp) < 5)
- return;
- /* Length (to be filled) */
- len_pos = wpabuf_put(resp, 2);
- wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
- wpabuf_put_u8(resp, srv_trans_id);
- /* Status Code */
- wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
- /* Response Data: empty */
- WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
- 2);
- }
- }
- static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s,
- struct wpabuf *resp, u8 srv_trans_id)
- {
- struct p2p_srv_upnp *usrv;
- u8 *len_pos;
- wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services");
- if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
- wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
- return;
- }
- dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
- struct p2p_srv_upnp, list) {
- if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service))
- return;
- /* Length (to be filled) */
- len_pos = wpabuf_put(resp, 2);
- wpabuf_put_u8(resp, P2P_SERV_UPNP);
- wpabuf_put_u8(resp, srv_trans_id);
- /* Status Code */
- wpabuf_put_u8(resp, P2P_SD_SUCCESS);
- /* Response Data */
- wpabuf_put_u8(resp, usrv->version);
- wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
- usrv->service);
- wpabuf_put_str(resp, usrv->service);
- WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
- 2);
- }
- }
- static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
- struct wpabuf *resp, u8 srv_trans_id,
- const u8 *query, size_t query_len)
- {
- struct p2p_srv_upnp *usrv;
- u8 *len_pos;
- u8 version;
- char *str;
- int count = 0;
- wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP",
- query, query_len);
- if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
- wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
- wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP,
- srv_trans_id);
- return;
- }
- if (query_len == 0) {
- wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
- return;
- }
- if (wpabuf_tailroom(resp) < 5)
- return;
- /* Length (to be filled) */
- len_pos = wpabuf_put(resp, 2);
- wpabuf_put_u8(resp, P2P_SERV_UPNP);
- wpabuf_put_u8(resp, srv_trans_id);
- version = query[0];
- str = os_malloc(query_len);
- if (str == NULL)
- return;
- os_memcpy(str, query + 1, query_len - 1);
- str[query_len - 1] = '\0';
- dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
- struct p2p_srv_upnp, list) {
- if (version != usrv->version)
- continue;
- if (os_strcmp(str, "ssdp:all") != 0 &&
- os_strstr(usrv->service, str) == NULL)
- continue;
- if (wpabuf_tailroom(resp) < 2)
- break;
- if (count == 0) {
- /* Status Code */
- wpabuf_put_u8(resp, P2P_SD_SUCCESS);
- /* Response Data */
- wpabuf_put_u8(resp, version);
- } else
- wpabuf_put_u8(resp, ',');
- count++;
- wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
- usrv->service);
- if (wpabuf_tailroom(resp) < os_strlen(usrv->service))
- break;
- wpabuf_put_str(resp, usrv->service);
- }
- os_free(str);
- if (count == 0) {
- wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
- "available");
- /* Status Code */
- wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
- /* Response Data: empty */
- }
- WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
- }
- #ifdef CONFIG_WIFI_DISPLAY
- static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s,
- struct wpabuf *resp, u8 srv_trans_id,
- const u8 *query, size_t query_len)
- {
- const u8 *pos;
- u8 role;
- u8 *len_pos;
- wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len);
- if (!wpa_s->global->wifi_display) {
- wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available");
- wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY,
- srv_trans_id);
- return;
- }
- if (query_len < 1) {
- wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device "
- "Role");
- return;
- }
- if (wpabuf_tailroom(resp) < 5)
- return;
- pos = query;
- role = *pos++;
- wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role);
- /* TODO: role specific handling */
- /* Length (to be filled) */
- len_pos = wpabuf_put(resp, 2);
- wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY);
- wpabuf_put_u8(resp, srv_trans_id);
- wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */
- while (pos < query + query_len) {
- if (*pos < MAX_WFD_SUBELEMS &&
- wpa_s->global->wfd_subelem[*pos] &&
- wpabuf_tailroom(resp) >=
- wpabuf_len(wpa_s->global->wfd_subelem[*pos])) {
- wpa_printf(MSG_DEBUG, "P2P: Add WSD response "
- "subelement %u", *pos);
- wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]);
- }
- pos++;
- }
- WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
- }
- #endif /* CONFIG_WIFI_DISPLAY */
- static int find_p2ps_substr(struct p2ps_advertisement *adv_data,
- const u8 *needle, size_t needle_len)
- {
- const u8 *haystack = (const u8 *) adv_data->svc_info;
- size_t haystack_len, i;
- /* Allow search term to be empty */
- if (!needle || !needle_len)
- return 1;
- if (!haystack)
- return 0;
- haystack_len = os_strlen(adv_data->svc_info);
- for (i = 0; i < haystack_len; i++) {
- if (haystack_len - i < needle_len)
- break;
- if (os_memcmp(haystack + i, needle, needle_len) == 0)
- return 1;
- }
- return 0;
- }
- static void wpas_sd_req_asp(struct wpa_supplicant *wpa_s,
- struct wpabuf *resp, u8 srv_trans_id,
- const u8 *query, size_t query_len)
- {
- struct p2ps_advertisement *adv_data;
- const u8 *svc = &query[1];
- const u8 *info = NULL;
- size_t svc_len = query[0];
- size_t info_len = 0;
- int prefix = 0;
- u8 *count_pos = NULL;
- u8 *len_pos = NULL;
- wpa_hexdump(MSG_DEBUG, "P2P: SD Request for ASP", query, query_len);
- if (!wpa_s->global->p2p) {
- wpa_printf(MSG_DEBUG, "P2P: ASP protocol not available");
- wpas_sd_add_proto_not_avail(resp, P2P_SERV_P2PS, srv_trans_id);
- return;
- }
- /* Info block is optional */
- if (svc_len + 1 < query_len) {
- info = &svc[svc_len];
- info_len = *info++;
- }
- /* Range check length of svc string and info block */
- if (svc_len + (info_len ? info_len + 2 : 1) > query_len) {
- wpa_printf(MSG_DEBUG, "P2P: ASP bad request");
- wpas_sd_add_bad_request(resp, P2P_SERV_P2PS, srv_trans_id);
- return;
- }
- /* Detect and correct for prefix search */
- if (svc_len && svc[svc_len - 1] == '*') {
- prefix = 1;
- svc_len--;
- }
- for (adv_data = p2p_get_p2ps_adv_list(wpa_s->global->p2p);
- adv_data; adv_data = adv_data->next) {
- /* If not a prefix match, reject length mismatches */
- if (!prefix && svc_len != os_strlen(adv_data->svc_name))
- continue;
- /* Search each service for request */
- if (os_memcmp(adv_data->svc_name, svc, svc_len) == 0 &&
- find_p2ps_substr(adv_data, info, info_len)) {
- size_t len = os_strlen(adv_data->svc_name);
- size_t svc_info_len = 0;
- if (adv_data->svc_info)
- svc_info_len = os_strlen(adv_data->svc_info);
- if (len > 0xff || svc_info_len > 0xffff)
- return;
- /* Length & Count to be filled as we go */
- if (!len_pos && !count_pos) {
- if (wpabuf_tailroom(resp) <
- len + svc_info_len + 16)
- return;
- len_pos = wpabuf_put(resp, 2);
- wpabuf_put_u8(resp, P2P_SERV_P2PS);
- wpabuf_put_u8(resp, srv_trans_id);
- /* Status Code */
- wpabuf_put_u8(resp, P2P_SD_SUCCESS);
- count_pos = wpabuf_put(resp, 1);
- *count_pos = 0;
- } else if (wpabuf_tailroom(resp) <
- len + svc_info_len + 10)
- return;
- if (svc_info_len) {
- wpa_printf(MSG_DEBUG,
- "P2P: Add Svc: %s info: %s",
- adv_data->svc_name,
- adv_data->svc_info);
- } else {
- wpa_printf(MSG_DEBUG, "P2P: Add Svc: %s",
- adv_data->svc_name);
- }
- /* Advertisement ID */
- wpabuf_put_le32(resp, adv_data->id);
- /* Config Methods */
- wpabuf_put_be16(resp, adv_data->config_methods);
- /* Service Name */
- wpabuf_put_u8(resp, (u8) len);
- wpabuf_put_data(resp, adv_data->svc_name, len);
- /* Service State */
- wpabuf_put_u8(resp, adv_data->state);
- /* Service Information */
- wpabuf_put_le16(resp, (u16) svc_info_len);
- wpabuf_put_data(resp, adv_data->svc_info, svc_info_len);
- /* Update length and count */
- (*count_pos)++;
- WPA_PUT_LE16(len_pos,
- (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
- }
- }
- /* Return error if no matching svc found */
- if (count_pos == NULL) {
- wpa_printf(MSG_DEBUG, "P2P: ASP service not found");
- wpas_sd_add_not_found(resp, P2P_SERV_P2PS, srv_trans_id);
- }
- }
- void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
- u16 update_indic, const u8 *tlvs, size_t tlvs_len)
- {
- struct wpa_supplicant *wpa_s = ctx;
- const u8 *pos = tlvs;
- const u8 *end = tlvs + tlvs_len;
- const u8 *tlv_end;
- u16 slen;
- struct wpabuf *resp;
- u8 srv_proto, srv_trans_id;
- size_t buf_len;
- char *buf;
- wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs",
- tlvs, tlvs_len);
- buf_len = 2 * tlvs_len + 1;
- buf = os_malloc(buf_len);
- if (buf) {
- wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
- wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d "
- MACSTR " %u %u %s",
- freq, MAC2STR(sa), dialog_token, update_indic,
- buf);
- os_free(buf);
- }
- if (wpa_s->p2p_sd_over_ctrl_iface) {
- wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
- update_indic, tlvs, tlvs_len);
- return; /* to be processed by an external program */
- }
- resp = wpabuf_alloc(10000);
- if (resp == NULL)
- return;
- while (pos + 1 < end) {
- wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
- slen = WPA_GET_LE16(pos);
- pos += 2;
- if (pos + slen > end || slen < 2) {
- wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
- "length");
- wpabuf_free(resp);
- return;
- }
- tlv_end = pos + slen;
- srv_proto = *pos++;
- wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
- srv_proto);
- srv_trans_id = *pos++;
- wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
- srv_trans_id);
- wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
- pos, tlv_end - pos);
- if (wpa_s->force_long_sd) {
- wpa_printf(MSG_DEBUG, "P2P: SD test - force long "
- "response");
- wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
- wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
- goto done;
- }
- switch (srv_proto) {
- case P2P_SERV_ALL_SERVICES:
- wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
- "for all services");
- if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) &&
- dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
- wpa_printf(MSG_DEBUG, "P2P: No service "
- "discovery protocols available");
- wpas_sd_add_proto_not_avail(
- resp, P2P_SERV_ALL_SERVICES,
- srv_trans_id);
- break;
- }
- wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
- wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
- break;
- case P2P_SERV_BONJOUR:
- wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id,
- pos, tlv_end - pos);
- break;
- case P2P_SERV_UPNP:
- wpas_sd_req_upnp(wpa_s, resp, srv_trans_id,
- pos, tlv_end - pos);
- break;
- #ifdef CONFIG_WIFI_DISPLAY
- case P2P_SERV_WIFI_DISPLAY:
- wpas_sd_req_wfd(wpa_s, resp, srv_trans_id,
- pos, tlv_end - pos);
- break;
- #endif /* CONFIG_WIFI_DISPLAY */
- case P2P_SERV_P2PS:
- wpas_sd_req_asp(wpa_s, resp, srv_trans_id,
- pos, tlv_end - pos);
- break;
- default:
- wpa_printf(MSG_DEBUG, "P2P: Unavailable service "
- "protocol %u", srv_proto);
- wpas_sd_add_proto_not_avail(resp, srv_proto,
- srv_trans_id);
- break;
- }
- pos = tlv_end;
- }
- done:
- wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
- update_indic, tlvs, tlvs_len);
- wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
- wpabuf_free(resp);
- }
- static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s,
- const u8 *sa, u8 srv_trans_id,
- const u8 *pos, const u8 *tlv_end)
- {
- u8 left = *pos++;
- u32 adv_id;
- u8 svc_status;
- u16 config_methods;
- char svc_str[256];
- while (left-- && pos < tlv_end) {
- char *buf = NULL;
- size_t buf_len;
- u8 svc_len;
- /* Sanity check fixed length+svc_str */
- if (pos + 6 >= tlv_end)
- break;
- svc_len = pos[6];
- if (pos + svc_len + 10 > tlv_end)
- break;
- /* Advertisement ID */
- adv_id = WPA_GET_LE32(pos);
- pos += sizeof(u32);
- /* Config Methods */
- config_methods = WPA_GET_BE16(pos);
- pos += sizeof(u16);
- /* Service Name */
- pos++; /* svc_len */
- os_memcpy(svc_str, pos, svc_len);
- svc_str[svc_len] = '\0';
- pos += svc_len;
- /* Service Status */
- svc_status = *pos++;
- /* Service Information Length */
- buf_len = WPA_GET_LE16(pos);
- pos += sizeof(u16);
- /* Sanity check buffer length */
- if (buf_len > (unsigned int) (tlv_end - pos))
- break;
- if (buf_len) {
- buf = os_zalloc(2 * buf_len + 1);
- if (buf) {
- utf8_escape((const char *) pos, buf_len, buf,
- 2 * buf_len + 1);
- }
- }
- pos += buf_len;
- if (buf) {
- wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
- MACSTR " %x %x %x %x %s '%s'",
- MAC2STR(sa), srv_trans_id, adv_id,
- svc_status, config_methods, svc_str,
- buf);
- os_free(buf);
- } else {
- wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
- MACSTR " %x %x %x %x %s",
- MAC2STR(sa), srv_trans_id, adv_id,
- svc_status, config_methods, svc_str);
- }
- }
- }
- void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
- const u8 *tlvs, size_t tlvs_len)
- {
- struct wpa_supplicant *wpa_s = ctx;
- const u8 *pos = tlvs;
- const u8 *end = tlvs + tlvs_len;
- const u8 *tlv_end;
- u16 slen;
- size_t buf_len;
- char *buf;
- wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
- tlvs, tlvs_len);
- if (tlvs_len > 1500) {
- /* TODO: better way for handling this */
- wpa_msg_ctrl(wpa_s, MSG_INFO,
- P2P_EVENT_SERV_DISC_RESP MACSTR
- " %u <long response: %u bytes>",
- MAC2STR(sa), update_indic,
- (unsigned int) tlvs_len);
- } else {
- buf_len = 2 * tlvs_len + 1;
- buf = os_malloc(buf_len);
- if (buf) {
- wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
- wpa_msg_ctrl(wpa_s, MSG_INFO,
- P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s",
- MAC2STR(sa), update_indic, buf);
- os_free(buf);
- }
- }
- while (pos < end) {
- u8 srv_proto, srv_trans_id, status;
- wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
- slen = WPA_GET_LE16(pos);
- pos += 2;
- if (pos + slen > end || slen < 3) {
- wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
- "length");
- return;
- }
- tlv_end = pos + slen;
- srv_proto = *pos++;
- wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
- srv_proto);
- srv_trans_id = *pos++;
- wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
- srv_trans_id);
- status = *pos++;
- wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u",
- status);
- wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data",
- pos, tlv_end - pos);
- if (srv_proto == P2P_SERV_P2PS && pos < tlv_end) {
- wpas_sd_p2ps_serv_response(wpa_s, sa, srv_trans_id,
- pos, tlv_end);
- }
- pos = tlv_end;
- }
- wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len);
- }
- u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
- const struct wpabuf *tlvs)
- {
- if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
- return 0;
- return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs);
- }
- u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
- u8 version, const char *query)
- {
- struct wpabuf *tlvs;
- u64 ret;
- tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query));
- if (tlvs == NULL)
- return 0;
- wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query));
- wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */
- wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */
- wpabuf_put_u8(tlvs, version);
- wpabuf_put_str(tlvs, query);
- ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
- wpabuf_free(tlvs);
- return ret;
- }
- u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id,
- const char *svc_str, const char *info_substr)
- {
- struct wpabuf *tlvs;
- size_t plen, svc_len, substr_len = 0;
- u64 ret;
- svc_len = os_strlen(svc_str);
- if (info_substr)
- substr_len = os_strlen(info_substr);
- if (svc_len > 0xff || substr_len > 0xff)
- return 0;
- plen = 1 + 1 + 1 + svc_len + 1 + substr_len;
- tlvs = wpabuf_alloc(2 + plen);
- if (tlvs == NULL)
- return 0;
- wpabuf_put_le16(tlvs, plen);
- wpabuf_put_u8(tlvs, P2P_SERV_P2PS);
- wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
- wpabuf_put_u8(tlvs, (u8) svc_len); /* Service String Length */
- wpabuf_put_data(tlvs, svc_str, svc_len);
- wpabuf_put_u8(tlvs, (u8) substr_len); /* Info Substring Length */
- wpabuf_put_data(tlvs, info_substr, substr_len);
- ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
- wpabuf_free(tlvs);
- return ret;
- }
- #ifdef CONFIG_WIFI_DISPLAY
- static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst,
- const struct wpabuf *tlvs)
- {
- if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
- return 0;
- return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs);
- }
- #define MAX_WFD_SD_SUBELEMS 20
- static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role,
- const char *subelems)
- {
- u8 *len;
- const char *pos;
- int val;
- int count = 0;
- len = wpabuf_put(tlvs, 2);
- wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */
- wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
- wpabuf_put_u8(tlvs, role);
- pos = subelems;
- while (*pos) {
- val = atoi(pos);
- if (val >= 0 && val < 256) {
- wpabuf_put_u8(tlvs, val);
- count++;
- if (count == MAX_WFD_SD_SUBELEMS)
- break;
- }
- pos = os_strchr(pos + 1, ',');
- if (pos == NULL)
- break;
- pos++;
- }
- WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2);
- }
- u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
- const u8 *dst, const char *role)
- {
- struct wpabuf *tlvs;
- u64 ret;
- const char *subelems;
- u8 id = 1;
- subelems = os_strchr(role, ' ');
- if (subelems == NULL)
- return 0;
- subelems++;
- tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS));
- if (tlvs == NULL)
- return 0;
- if (os_strstr(role, "[source]"))
- wfd_add_sd_req_role(tlvs, id++, 0x00, subelems);
- if (os_strstr(role, "[pri-sink]"))
- wfd_add_sd_req_role(tlvs, id++, 0x01, subelems);
- if (os_strstr(role, "[sec-sink]"))
- wfd_add_sd_req_role(tlvs, id++, 0x02, subelems);
- if (os_strstr(role, "[source+sink]"))
- wfd_add_sd_req_role(tlvs, id++, 0x03, subelems);
- ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs);
- wpabuf_free(tlvs);
- return ret;
- }
- #endif /* CONFIG_WIFI_DISPLAY */
- int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req)
- {
- if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
- return -1;
- return p2p_sd_cancel_request(wpa_s->global->p2p,
- (void *) (uintptr_t) req);
- }
- void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
- const u8 *dst, u8 dialog_token,
- const struct wpabuf *resp_tlvs)
- {
- if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
- return;
- p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
- resp_tlvs);
- }
- void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
- {
- if (wpa_s->global->p2p)
- p2p_sd_service_update(wpa_s->global->p2p);
- }
- static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv)
- {
- dl_list_del(&bsrv->list);
- wpabuf_free(bsrv->query);
- wpabuf_free(bsrv->resp);
- os_free(bsrv);
- }
- static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv)
- {
- dl_list_del(&usrv->list);
- os_free(usrv->service);
- os_free(usrv);
- }
- void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s)
- {
- struct p2p_srv_bonjour *bsrv, *bn;
- struct p2p_srv_upnp *usrv, *un;
- dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour,
- struct p2p_srv_bonjour, list)
- wpas_p2p_srv_bonjour_free(bsrv);
- dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp,
- struct p2p_srv_upnp, list)
- wpas_p2p_srv_upnp_free(usrv);
- wpas_p2p_sd_service_update(wpa_s);
- }
- int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id)
- {
- if (adv_id == 0)
- return 1;
- if (p2p_service_p2ps_id(wpa_s->global->p2p, adv_id))
- return 1;
- return 0;
- }
- int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id)
- {
- int ret;
- ret = p2p_service_del_asp(wpa_s->global->p2p, adv_id);
- if (ret == 0)
- wpas_p2p_sd_service_update(wpa_s);
- return ret;
- }
- int 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)
- {
- int ret;
- ret = p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id,
- adv_str, svc_state, config_methods,
- svc_info);
- if (ret == 0)
- wpas_p2p_sd_service_update(wpa_s);
- return ret;
- }
- int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
- struct wpabuf *query, struct wpabuf *resp)
- {
- struct p2p_srv_bonjour *bsrv;
- bsrv = os_zalloc(sizeof(*bsrv));
- if (bsrv == NULL)
- return -1;
- bsrv->query = query;
- bsrv->resp = resp;
- dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
- wpas_p2p_sd_service_update(wpa_s);
- return 0;
- }
- int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
- const struct wpabuf *query)
- {
- struct p2p_srv_bonjour *bsrv;
- bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
- if (bsrv == NULL)
- return -1;
- wpas_p2p_srv_bonjour_free(bsrv);
- wpas_p2p_sd_service_update(wpa_s);
- return 0;
- }
- int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
- const char *service)
- {
- struct p2p_srv_upnp *usrv;
- if (wpas_p2p_service_get_upnp(wpa_s, version, service))
- return 0; /* Already listed */
- usrv = os_zalloc(sizeof(*usrv));
- if (usrv == NULL)
- return -1;
- usrv->version = version;
- usrv->service = os_strdup(service);
- if (usrv->service == NULL) {
- os_free(usrv);
- return -1;
- }
- dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list);
- wpas_p2p_sd_service_update(wpa_s);
- return 0;
- }
- int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
- const char *service)
- {
- struct p2p_srv_upnp *usrv;
- usrv = wpas_p2p_service_get_upnp(wpa_s, version, service);
- if (usrv == NULL)
- return -1;
- wpas_p2p_srv_upnp_free(usrv);
- wpas_p2p_sd_service_update(wpa_s);
- return 0;
- }
|