1 /*- 2 * search.c 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 * 6 * Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.com> 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 * $Id: search.c,v 1.2 2003/09/04 22:12:13 max Exp $ 31 */ 32 33 #include <sys/uio.h> 34 #include <netinet/in.h> 35 #include <arpa/inet.h> 36 #define L2CAP_SOCKET_CHECKED 37 #include <bluetooth.h> 38 #include <errno.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <unistd.h> 43 44 #include <sdp-int.h> 45 #include <sdp.h> 46 47 int32_t 48 sdp_search(void *xss, 49 uint32_t plen, uint16_t const *pp, 50 uint32_t alen, uint32_t const *ap, 51 uint32_t vlen, sdp_attr_t *vp) 52 { 53 struct sdp_xpdu { 54 sdp_pdu_t pdu; 55 uint16_t len; 56 } __attribute__ ((packed)) xpdu; 57 58 sdp_session_p ss = (sdp_session_p) xss; 59 uint8_t *req = NULL, *rsp = NULL, *rsp_tmp = NULL; 60 int32_t t, len; 61 uint16_t lo, hi; 62 63 if (ss == NULL) 64 return (-1); 65 66 if (ss->req == NULL || ss->rsp == NULL || 67 plen == 0 || pp == NULL || alen == 0 || ap == NULL) { 68 ss->error = EINVAL; 69 return (-1); 70 } 71 72 req = ss->req; 73 74 /* Calculate ServiceSearchPattern length */ 75 plen = plen * (sizeof(pp[0]) + 1); 76 77 /* Calculate AttributeIDList length */ 78 for (len = 0, t = 0; t < alen; t ++) { 79 lo = (uint16_t) (ap[t] >> 16); 80 hi = (uint16_t) (ap[t]); 81 82 if (lo > hi) { 83 ss->error = EINVAL; 84 return (-1); 85 } 86 87 if (lo != hi) 88 len += (sizeof(ap[t]) + 1); 89 else 90 len += (sizeof(lo) + 1); 91 } 92 alen = len; 93 94 /* Calculate length of the request */ 95 len = plen + sizeof(uint8_t) + sizeof(uint16_t) + 96 /* ServiceSearchPattern */ 97 sizeof(uint16_t) + 98 /* MaximumAttributeByteCount */ 99 alen + sizeof(uint8_t) + sizeof(uint16_t); 100 /* AttributeIDList */ 101 102 if (ss->req_e - req < len) { 103 ss->error = ENOBUFS; 104 return (-1); 105 } 106 107 /* Put ServiceSearchPattern */ 108 SDP_PUT8(SDP_DATA_SEQ16, req); 109 SDP_PUT16(plen, req); 110 for (; plen > 0; pp ++, plen -= (sizeof(pp[0]) + 1)) { 111 SDP_PUT8(SDP_DATA_UUID16, req); 112 SDP_PUT16(*pp, req); 113 } 114 115 /* Put MaximumAttributeByteCount */ 116 SDP_PUT16(0xffff, req); 117 118 /* Put AttributeIDList */ 119 SDP_PUT8(SDP_DATA_SEQ16, req); 120 SDP_PUT16(alen, req); 121 for (; alen > 0; ap ++) { 122 lo = (uint16_t) (*ap >> 16); 123 hi = (uint16_t) (*ap); 124 125 if (lo != hi) { 126 /* Put attribute range */ 127 SDP_PUT8(SDP_DATA_UINT32, req); 128 SDP_PUT32(*ap, req); 129 alen -= (sizeof(ap[0]) + 1); 130 } else { 131 /* Put attribute */ 132 SDP_PUT8(SDP_DATA_UINT16, req); 133 SDP_PUT16(lo, req); 134 alen -= (sizeof(lo) + 1); 135 } 136 } 137 138 /* Submit ServiceSearchAttributeRequest and wait for response */ 139 ss->cslen = 0; 140 rsp = ss->rsp; 141 142 do { 143 struct iovec iov[2]; 144 uint8_t *req_cs = req; 145 146 /* Add continuation state (if any) */ 147 if (ss->req_e - req_cs < ss->cslen + 1) { 148 ss->error = ENOBUFS; 149 return (-1); 150 } 151 152 SDP_PUT8(ss->cslen, req_cs); 153 if (ss->cslen > 0) { 154 memcpy(req_cs, ss->cs, ss->cslen); 155 req_cs += ss->cslen; 156 } 157 158 /* Prepare SDP PDU header */ 159 xpdu.pdu.pid = SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST; 160 xpdu.pdu.tid = htons(ss->tid); 161 xpdu.pdu.len = htons(req_cs - ss->req); 162 163 /* Submit request */ 164 iov[0].iov_base = (void *) &xpdu; 165 iov[0].iov_len = sizeof(xpdu.pdu); 166 iov[1].iov_base = (void *) ss->req; 167 iov[1].iov_len = req_cs - ss->req; 168 169 do { 170 len = writev(ss->s, iov, sizeof(iov)/sizeof(iov[0])); 171 } while (len < 0 && errno == EINTR); 172 173 if (len < 0) { 174 ss->error = errno; 175 return (-1); 176 } 177 178 /* Read response */ 179 iov[0].iov_base = (void *) &xpdu; 180 iov[0].iov_len = sizeof(xpdu); 181 iov[1].iov_base = (void *) rsp; 182 iov[1].iov_len = ss->imtu; 183 184 do { 185 len = readv(ss->s, iov, sizeof(iov)/sizeof(iov[0])); 186 } while (len < 0 && errno == EINTR); 187 188 if (len < 0) { 189 ss->error = errno; 190 return (-1); 191 } 192 if (len < sizeof(xpdu)) { 193 ss->error = ENOMSG; 194 return (-1); 195 } 196 197 xpdu.pdu.tid = ntohs(xpdu.pdu.tid); 198 xpdu.pdu.len = ntohs(xpdu.pdu.len); 199 xpdu.len = ntohs(xpdu.len); 200 201 if (xpdu.pdu.pid == SDP_PDU_ERROR_RESPONSE || 202 xpdu.pdu.tid != ss->tid || 203 xpdu.pdu.len > len || 204 xpdu.len > xpdu.pdu.len) { 205 ss->error = EIO; 206 return (-1); 207 } 208 209 rsp += xpdu.len; 210 ss->tid ++; 211 212 /* Save continuation state (if any) */ 213 ss->cslen = rsp[0]; 214 if (ss->cslen > 0) { 215 if (ss->cslen > sizeof(ss->cs)) { 216 ss->error = ENOBUFS; 217 return (-1); 218 } 219 220 memcpy(ss->cs, rsp + 1, ss->cslen); 221 222 /* 223 * Ensure that we always have ss->imtu bytes 224 * available in the ss->rsp buffer 225 */ 226 227 if (ss->rsp_e - rsp <= ss->imtu) { 228 uint32_t size, offset; 229 230 size = ss->rsp_e - ss->rsp + ss->imtu; 231 offset = rsp - ss->rsp; 232 233 rsp_tmp = realloc(ss->rsp, size); 234 if (rsp_tmp == NULL) { 235 ss->error = ENOMEM; 236 return (-1); 237 } 238 239 ss->rsp = rsp_tmp; 240 ss->rsp_e = ss->rsp + size; 241 rsp = ss->rsp + offset; 242 } 243 } 244 } while (ss->cslen > 0); 245 246 /* 247 * If we got here then we have completed SDP transaction and now 248 * we must populate attribute values into vp array. At this point 249 * ss->rsp points to the beginning of the response and rsp points 250 * to the end of the response. 251 * 252 * From Bluetooth v1.1 spec page 364 253 * 254 * The AttributeLists is a data element sequence where each element 255 * in turn is a data element sequence representing an attribute list. 256 * Each attribute list contains attribute IDs and attribute values 257 * from one service record. The first element in each attribute list 258 * contains the attribute ID of the first attribute to be returned for 259 * that service record. The second element in each attribute list 260 * contains the corresponding attribute value. Successive pairs of 261 * elements in each attribute list contain additional attribute ID 262 * and value pairs. Only attributes that have non-null values within 263 * the service record and whose attribute IDs were specified in the 264 * SDP_ServiceSearchAttributeRequest are contained in the AttributeLists 265 * Neither an attribute ID nor attribute value is placed in 266 * AttributeLists for attributes in the service record that have no 267 * value. Within each attribute list, the attributes are listed in 268 * ascending order of attribute ID value. 269 */ 270 271 if (vp == NULL) 272 goto done; 273 274 rsp_tmp = ss->rsp; 275 276 /* Skip the first SEQ */ 277 SDP_GET8(t, rsp_tmp); 278 switch (t) { 279 case SDP_DATA_SEQ8: 280 SDP_GET8(len, rsp_tmp); 281 break; 282 283 case SDP_DATA_SEQ16: 284 SDP_GET16(len, rsp_tmp); 285 break; 286 287 case SDP_DATA_SEQ32: 288 SDP_GET32(len, rsp_tmp); 289 break; 290 291 default: 292 ss->error = ENOATTR; 293 return (-1); 294 /* NOT REACHED */ 295 } 296 297 for (; rsp_tmp < rsp && vlen > 0; ) { 298 /* Get set of attributes for the next record */ 299 SDP_GET8(t, rsp_tmp); 300 switch (t) { 301 case SDP_DATA_SEQ8: 302 SDP_GET8(len, rsp_tmp); 303 break; 304 305 case SDP_DATA_SEQ16: 306 SDP_GET16(len, rsp_tmp); 307 break; 308 309 case SDP_DATA_SEQ32: 310 SDP_GET32(len, rsp_tmp); 311 break; 312 313 default: 314 ss->error = ENOATTR; 315 return (-1); 316 /* NOT REACHED */ 317 } 318 319 /* Now rsp_tmp points to list of (attr,value) pairs */ 320 for (; len > 0 && vlen > 0; vp ++, vlen --) { 321 /* Attribute */ 322 SDP_GET8(t, rsp_tmp); 323 if (t != SDP_DATA_UINT16) { 324 ss->error = ENOATTR; 325 return (-1); 326 } 327 SDP_GET16(vp->attr, rsp_tmp); 328 329 /* Attribute value */ 330 switch (rsp_tmp[0]) { 331 case SDP_DATA_NIL: 332 alen = 0; 333 break; 334 335 case SDP_DATA_UINT8: 336 case SDP_DATA_INT8: 337 case SDP_DATA_BOOL: 338 alen = sizeof(uint8_t); 339 break; 340 341 case SDP_DATA_UINT16: 342 case SDP_DATA_INT16: 343 case SDP_DATA_UUID16: 344 alen = sizeof(uint16_t); 345 break; 346 347 case SDP_DATA_UINT32: 348 case SDP_DATA_INT32: 349 case SDP_DATA_UUID32: 350 alen = sizeof(uint32_t); 351 break; 352 353 case SDP_DATA_UINT64: 354 case SDP_DATA_INT64: 355 alen = sizeof(uint64_t); 356 break; 357 358 case SDP_DATA_UINT128: 359 case SDP_DATA_INT128: 360 case SDP_DATA_UUID128: 361 alen = sizeof(uint128_t); 362 break; 363 364 case SDP_DATA_STR8: 365 case SDP_DATA_URL8: 366 case SDP_DATA_SEQ8: 367 case SDP_DATA_ALT8: 368 alen = rsp_tmp[1] + sizeof(uint8_t); 369 break; 370 371 case SDP_DATA_STR16: 372 case SDP_DATA_URL16: 373 case SDP_DATA_SEQ16: 374 case SDP_DATA_ALT16: 375 alen = ((uint16_t)rsp_tmp[1] << 8) 376 | ((uint16_t)rsp_tmp[2]); 377 alen += sizeof(uint16_t); 378 break; 379 380 case SDP_DATA_STR32: 381 case SDP_DATA_URL32: 382 case SDP_DATA_SEQ32: 383 case SDP_DATA_ALT32: 384 alen = ((uint32_t)rsp_tmp[1] << 24) 385 | ((uint32_t)rsp_tmp[2] << 16) 386 | ((uint32_t)rsp_tmp[3] << 8) 387 | ((uint32_t)rsp_tmp[4]); 388 alen += sizeof(uint32_t); 389 break; 390 391 default: 392 ss->error = ENOATTR; 393 return (-1); 394 /* NOT REACHED */ 395 } 396 397 alen += sizeof(uint8_t); 398 399 if (vp->value != NULL) { 400 if (alen <= vp->vlen) { 401 vp->flags = SDP_ATTR_OK; 402 vp->vlen = alen; 403 } else 404 vp->flags = SDP_ATTR_TRUNCATED; 405 406 memcpy(vp->value, rsp_tmp, vp->vlen); 407 } else 408 vp->flags = SDP_ATTR_INVALID; 409 410 len -= ( 411 sizeof(uint8_t) + sizeof(uint16_t) + 412 alen 413 ); 414 415 rsp_tmp += alen; 416 } 417 } 418 done: 419 ss->error = 0; 420 421 return (0); 422 } 423 424