static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, u8 dialog_token, u16 config_methods, struct p2p_device *go) { struct wpabuf *buf; u8 *len; buf = wpabuf_alloc(1000); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token); len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_capability(buf, p2p->dev_capab, 0); p2p_buf_add_device_info(buf, p2p, NULL); if (go) { p2p_buf_add_group_id(buf, go->info.p2p_device_addr, go->oper_ssid, go->oper_ssid_len); } p2p_buf_update_ie_hdr(buf, len); /* WPS IE with Config Methods attribute */ p2p_build_wps_ie_config_methods(buf, config_methods); return buf; }
static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, struct p2p_device *peer, u8 dialog_token, u8 status, const u8 *group_bssid, u8 reg_class, u8 channel, struct p2p_channels *channels) { struct wpabuf *buf; u8 *len; buf = wpabuf_alloc(1000); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_RESP, dialog_token); len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_status(buf, status); p2p_buf_add_config_timeout(buf, 0, 0); /* FIX */ if (reg_class && channel) p2p_buf_add_operating_channel(buf, p2p->cfg->country, reg_class, channel); if (group_bssid) p2p_buf_add_group_bssid(buf, group_bssid); if (channels) p2p_buf_add_channel_list(buf, p2p->cfg->country, channels); p2p_buf_update_ie_hdr(buf, len); return buf; }
static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, struct p2p_device *peer, u8 dialog_token, u8 status, const u8 *group_bssid, u8 reg_class, u8 channel, struct p2p_channels *channels) { struct wpabuf *buf; u8 *len; size_t extra = 0; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie = p2p->wfd_ie_invitation; if (wfd_ie && group_bssid) { size_t i; for (i = 0; i < p2p->num_groups; i++) { struct p2p_group *g = p2p->groups[i]; struct wpabuf *ie; if (!p2p_group_is_group_id_match(g, group_bssid, ETH_ALEN)) continue; ie = p2p_group_get_wfd_ie(g); if (ie) { wfd_ie = ie; break; } } } if (wfd_ie) extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_RESP, dialog_token); len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_status(buf, status); p2p_buf_add_config_timeout(buf, 0, 0); /* FIX */ if (reg_class && channel) p2p_buf_add_operating_channel(buf, p2p->cfg->country, reg_class, channel); if (group_bssid) p2p_buf_add_group_bssid(buf, group_bssid); if (channels) p2p_buf_add_channel_list(buf, p2p->cfg->country, channels); p2p_buf_update_ie_hdr(buf, len); #ifdef CONFIG_WIFI_DISPLAY if (wfd_ie) wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ return buf; }
static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, u8 dialog_token, u16 config_methods, const u8 *group_id, size_t group_id_len) { struct wpabuf *buf; size_t extra = 0; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp; if (wfd_ie && group_id) { size_t i; for (i = 0; i < p2p->num_groups; i++) { struct p2p_group *g = p2p->groups[i]; struct wpabuf *ie; if (!p2p_group_is_group_id_match(g, group_id, group_id_len)) continue; ie = p2p_group_get_wfd_ie(g); if (ie) { wfd_ie = ie; break; } } } if (wfd_ie) extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]); buf = wpabuf_alloc(100 + extra); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token); /* WPS IE with Config Methods attribute */ p2p_build_wps_ie_config_methods(buf, config_methods); #ifdef CONFIG_WIFI_DISPLAY if (wfd_ie) wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]) wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]); return buf; }
static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, u8 dialog_token, u16 config_methods, struct p2p_device *go) { struct wpabuf *buf; u8 *len; size_t extra = 0; #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_prov_disc_req) extra = wpabuf_len(p2p->wfd_ie_prov_disc_req); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]); buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token); len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_capability(buf, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); p2p_buf_add_device_info(buf, p2p, NULL); if (go) { p2p_buf_add_group_id(buf, go->info.p2p_device_addr, go->oper_ssid, go->oper_ssid_len); } p2p_buf_update_ie_hdr(buf, len); /* WPS IE with Config Methods attribute */ p2p_build_wps_ie_config_methods(buf, config_methods); #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_prov_disc_req) wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]) wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]); return buf; }
static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, u8 dialog_token, u16 config_methods) { struct wpabuf *buf; buf = wpabuf_alloc(100); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token); /* WPS IE with Config Methods attribute */ p2p_build_wps_ie_config_methods(buf, config_methods); return buf; }
static struct wpabuf * p2p_build_dev_disc_resp(u8 dialog_token, u8 status) { struct wpabuf *buf; u8 *len; buf = wpabuf_alloc(100); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_RESP, dialog_token); len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_status(buf, status); p2p_buf_update_ie_hdr(buf, len); return buf; }
static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, struct p2p_device *peer, const u8 *go_dev_addr) { struct wpabuf *buf; u8 *len; const u8 *dev_addr; buf = wpabuf_alloc(1000); if (buf == NULL) return NULL; peer->dialog_token++; if (peer->dialog_token == 0) peer->dialog_token = 1; p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_REQ, peer->dialog_token); len = p2p_buf_add_ie_hdr(buf); if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO || !p2p->inv_persistent) p2p_buf_add_config_timeout(buf, 0, 0); else p2p_buf_add_config_timeout(buf, 100, 20); p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ? P2P_INVITATION_FLAGS_TYPE : 0); p2p_buf_add_operating_channel(buf, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); if (p2p->inv_bssid_set) p2p_buf_add_group_bssid(buf, p2p->inv_bssid); p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); if (go_dev_addr) dev_addr = go_dev_addr; else if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT) dev_addr = peer->info.p2p_device_addr; else dev_addr = p2p->cfg->dev_addr; p2p_buf_add_group_id(buf, dev_addr, p2p->inv_ssid, p2p->inv_ssid_len); p2p_buf_add_device_info(buf, p2p, peer); p2p_buf_update_ie_hdr(buf, len); return buf; }
static struct wpabuf * p2p_build_dev_disc_req(struct p2p_data *p2p, struct p2p_device *go, const u8 *dev_id) { struct wpabuf *buf; u8 *len; buf = wpabuf_alloc(100); if (buf == NULL) return NULL; go->dialog_token++; if (go->dialog_token == 0) go->dialog_token = 1; p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_REQ, go->dialog_token); len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_device_id(buf, dev_id); p2p_buf_add_group_id(buf, go->info.p2p_device_addr, go->oper_ssid, go->oper_ssid_len); p2p_buf_update_ie_hdr(buf, len); return buf; }
static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, struct p2p_device *dev, u8 dialog_token, enum p2p_status_code status, u16 config_methods, u32 adv_id, const u8 *group_id, size_t group_id_len, const u8 *persist_ssid, size_t persist_ssid_len, const u8 *fcap, u16 fcap_len) { struct wpabuf *buf; size_t extra = 0; int persist = 0; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp; if (wfd_ie && group_id) { size_t i; for (i = 0; i < p2p->num_groups; i++) { struct p2p_group *g = p2p->groups[i]; struct wpabuf *ie; if (!p2p_group_is_group_id_match(g, group_id, group_id_len)) continue; ie = p2p_group_get_wfd_ie(g); if (ie) { wfd_ie = ie; break; } } } if (wfd_ie) extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]); buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token); /* Add P2P IE for P2PS */ if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) { u8 *len = p2p_buf_add_ie_hdr(buf); struct p2ps_provision *prov = p2p->p2ps_prov; u8 group_capab; u8 conncap = 0; if (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) conncap = prov->conncap; if (!status && prov->status != -1) status = prov->status; p2p_buf_add_status(buf, status); group_capab = P2P_GROUP_CAPAB_PERSISTENT_GROUP | P2P_GROUP_CAPAB_PERSISTENT_RECONN; if (p2p->cross_connect) group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; if (p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; p2p_buf_add_capability(buf, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, group_capab); p2p_buf_add_device_info(buf, p2p, NULL); if (persist_ssid && p2p->cfg->get_persistent_group && dev && (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED)) { u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u8 go_dev_addr[ETH_ALEN]; u8 intended_addr[ETH_ALEN]; persist = p2p->cfg->get_persistent_group( p2p->cfg->cb_ctx, dev->info.p2p_device_addr, persist_ssid, persist_ssid_len, go_dev_addr, ssid, &ssid_len, intended_addr); if (persist) { p2p_buf_add_persistent_group_info( buf, go_dev_addr, ssid, ssid_len); if (!is_zero_ether_addr(intended_addr)) p2p_buf_add_intended_addr( buf, intended_addr); } } if (!persist && (conncap & P2PS_SETUP_GROUP_OWNER)) p2ps_add_new_group_info(p2p, dev, buf); /* Add Operating Channel if conncap indicates GO */ if (persist || (conncap & P2PS_SETUP_GROUP_OWNER)) { if (p2p->op_reg_class && p2p->op_channel) p2p_buf_add_operating_channel( buf, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); else p2p_buf_add_operating_channel( buf, p2p->cfg->country, p2p->cfg->op_reg_class, p2p->cfg->op_channel); } if (persist || (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER))) p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); if (!persist && conncap) p2p_buf_add_connection_capability(buf, conncap); p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac); if (persist || (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER))) p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); p2p_buf_add_feature_capability(buf, fcap_len, fcap); p2p_buf_update_ie_hdr(buf, len); } else if (status != P2P_SC_SUCCESS || adv_id) { u8 *len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_status(buf, status); if (p2p->p2ps_prov) p2p_buf_add_advertisement_id(buf, adv_id, p2p->p2ps_prov->adv_mac); p2p_buf_update_ie_hdr(buf, len); } /* WPS IE with Config Methods attribute */ p2p_build_wps_ie_config_methods(buf, config_methods); #ifdef CONFIG_WIFI_DISPLAY if (wfd_ie) wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]) wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]); return buf; }
static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, int join) { struct wpabuf *buf; u8 *len; size_t extra = 0; u8 dialog_token = dev->dialog_token; u16 config_methods = dev->req_config_methods; struct p2p_device *go = join ? dev : NULL; u8 group_capab; #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_prov_disc_req) extra = wpabuf_len(p2p->wfd_ie_prov_disc_req); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]); if (p2p->p2ps_prov) extra += os_strlen(p2p->p2ps_prov->info) + 1 + sizeof(struct p2ps_provision); buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token); len = p2p_buf_add_ie_hdr(buf); group_capab = 0; if (p2p->p2ps_prov) { group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN; if (p2p->cross_connect) group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; if (p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; } p2p_buf_add_capability(buf, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, group_capab); p2p_buf_add_device_info(buf, p2p, NULL); if (p2p->p2ps_prov) { p2ps_add_pd_req_attrs(p2p, dev, buf, config_methods); } else if (go) { p2p_buf_add_group_id(buf, go->info.p2p_device_addr, go->oper_ssid, go->oper_ssid_len); } p2p_buf_update_ie_hdr(buf, len); /* WPS IE with Config Methods attribute */ p2p_build_wps_ie_config_methods(buf, config_methods); #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_prov_disc_req) wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]) wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]); return buf; }
static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, struct p2p_device *peer, const u8 *go_dev_addr) { struct wpabuf *buf; u8 *len; const u8 *dev_addr; size_t extra = 0; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie = p2p->wfd_ie_invitation; if (wfd_ie && p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) { size_t i; for (i = 0; i < p2p->num_groups; i++) { struct p2p_group *g = p2p->groups[i]; struct wpabuf *ie; ie = p2p_group_get_wfd_ie(g); if (ie) { wfd_ie = ie; break; } } } if (wfd_ie) extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; peer->dialog_token++; if (peer->dialog_token == 0) peer->dialog_token = 1; p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_REQ, peer->dialog_token); len = p2p_buf_add_ie_hdr(buf); if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO || !p2p->inv_persistent) p2p_buf_add_config_timeout(buf, 0, 0); else p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ? P2P_INVITATION_FLAGS_TYPE : 0); p2p_buf_add_operating_channel(buf, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); if (p2p->inv_bssid_set) p2p_buf_add_group_bssid(buf, p2p->inv_bssid); p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); if (go_dev_addr) dev_addr = go_dev_addr; else if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT) dev_addr = peer->info.p2p_device_addr; else dev_addr = p2p->cfg->dev_addr; p2p_buf_add_group_id(buf, dev_addr, p2p->inv_ssid, p2p->inv_ssid_len); p2p_buf_add_device_info(buf, p2p, peer); p2p_buf_update_ie_hdr(buf, len); #ifdef CONFIG_WIFI_DISPLAY if (wfd_ie) wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ return buf; }
static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, struct p2p_device *peer, const u8 *go_dev_addr, int dev_pw_id) { struct wpabuf *buf; u8 *len; const u8 *dev_addr; size_t extra = 0; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie = p2p->wfd_ie_invitation; if (wfd_ie && p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) { size_t i; for (i = 0; i < p2p->num_groups; i++) { struct p2p_group *g = p2p->groups[i]; struct wpabuf *ie; if (os_memcmp(p2p_group_get_interface_addr(g), p2p->inv_bssid, ETH_ALEN) != 0) continue; ie = p2p_group_get_wfd_ie(g); if (ie) { wfd_ie = ie; break; } } } if (wfd_ie) extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]); buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; peer->dialog_token++; if (peer->dialog_token == 0) peer->dialog_token = 1; p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_REQ, peer->dialog_token); len = p2p_buf_add_ie_hdr(buf); if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO || !p2p->inv_persistent) p2p_buf_add_config_timeout(buf, 0, 0); else p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ? P2P_INVITATION_FLAGS_TYPE : 0); if (p2p->inv_role != P2P_INVITE_ROLE_CLIENT || !(peer->flags & P2P_DEV_NO_PREF_CHAN)) p2p_buf_add_operating_channel(buf, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); if (p2p->inv_bssid_set) p2p_buf_add_group_bssid(buf, p2p->inv_bssid); p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); if (go_dev_addr) dev_addr = go_dev_addr; else if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT) dev_addr = peer->info.p2p_device_addr; else dev_addr = p2p->cfg->dev_addr; p2p_buf_add_group_id(buf, dev_addr, p2p->inv_ssid, p2p->inv_ssid_len); p2p_buf_add_device_info(buf, p2p, peer); p2p_buf_update_ie_hdr(buf, len); p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list, p2p->num_pref_freq); #ifdef CONFIG_WIFI_DISPLAY if (wfd_ie) wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]) wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]); if (dev_pw_id >= 0) { /* WSC IE in Invitation Request for NFC static handover */ p2p_build_wps_ie(p2p, buf, dev_pw_id, 0); } return buf; }