1 /*- 2 * sar.c 3 * 4 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 5 * 6 * Copyright (c) 2004 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: sar.c,v 1.2 2004/01/08 23:46:51 max Exp $ 31 * $FreeBSD$ 32 */ 33 34 #include <sys/queue.h> 35 #include <sys/uio.h> 36 #include <netinet/in.h> 37 #include <arpa/inet.h> 38 #include <assert.h> 39 #define L2CAP_SOCKET_CHECKED 40 #include <bluetooth.h> 41 #include <errno.h> 42 #include <sdp.h> 43 #include <stdio.h> /* for NULL */ 44 #include "profile.h" 45 #include "provider.h" 46 #include "server.h" 47 48 /* 49 * Prepare SDP attr/value pair. Check if profile implements the attribute 50 * and if so call the attribute value function. 51 * 52 * uint16 value16 - 3 bytes (attribute) 53 * value - N bytes (value) 54 */ 55 56 static int32_t 57 server_prepare_attr_value_pair( 58 provider_p const provider, uint16_t attr, 59 uint8_t *buf, uint8_t const * const eob) 60 { 61 profile_attr_create_p cf = profile_get_attr(provider->profile, attr); 62 int32_t len; 63 64 if (cf == NULL) 65 return (0); /* no attribute */ 66 67 if (buf + 3 > eob) 68 return (-1); 69 70 SDP_PUT8(SDP_DATA_UINT16, buf); 71 SDP_PUT16(attr, buf); 72 73 len = cf(buf, eob, (uint8_t const *) provider, sizeof(*provider)); 74 if (len < 0) 75 return (-1); 76 77 return (3 + len); 78 } 79 80 /* 81 * seq16 value16 - 3 bytes 82 * attr value - 3+ bytes 83 * [ attr value ] 84 */ 85 86 int32_t 87 server_prepare_attr_list(provider_p const provider, 88 uint8_t const *req, uint8_t const * const req_end, 89 uint8_t *rsp, uint8_t const * const rsp_end) 90 { 91 uint8_t *ptr = rsp + 3; 92 int32_t type, hi, lo, len; 93 94 if (ptr > rsp_end) 95 return (-1); 96 97 while (req < req_end) { 98 SDP_GET8(type, req); 99 100 switch (type) { 101 case SDP_DATA_UINT16: 102 if (req + 2 > req_end) 103 return (-1); 104 105 SDP_GET16(lo, req); 106 hi = lo; 107 break; 108 109 case SDP_DATA_UINT32: 110 if (req + 4 > req_end) 111 return (-1); 112 113 SDP_GET16(lo, req); 114 SDP_GET16(hi, req); 115 break; 116 117 default: 118 return (-1); 119 /* NOT REACHED */ 120 } 121 122 for (; lo <= hi; lo ++) { 123 len = server_prepare_attr_value_pair(provider, lo, ptr, rsp_end); 124 if (len < 0) 125 return (-1); 126 127 ptr += len; 128 } 129 } 130 131 len = ptr - rsp; /* we put this much bytes in rsp */ 132 133 /* Fix SEQ16 header for the rsp */ 134 SDP_PUT8(SDP_DATA_SEQ16, rsp); 135 SDP_PUT16(len - 3, rsp); 136 137 return (len); 138 } 139 140 /* 141 * Prepare SDP Service Attribute Response 142 */ 143 144 int32_t 145 server_prepare_service_attribute_response(server_p srv, int32_t fd) 146 { 147 uint8_t const *req = srv->req + sizeof(sdp_pdu_t); 148 uint8_t const *req_end = req + ((sdp_pdu_p)(srv->req))->len; 149 uint8_t *rsp = srv->fdidx[fd].rsp; 150 uint8_t const *rsp_end = rsp + NG_L2CAP_MTU_MAXIMUM; 151 152 uint8_t *ptr = NULL; 153 provider_t *provider = NULL; 154 uint32_t handle; 155 int32_t type, rsp_limit, aidlen, cslen, cs; 156 157 /* 158 * Minimal Service Attribute Request request 159 * 160 * value32 - 4 bytes ServiceRecordHandle 161 * value16 - 2 bytes MaximumAttributeByteCount 162 * seq8 len8 - 2 bytes 163 * uint16 value16 - 3 bytes AttributeIDList 164 * value8 - 1 byte ContinuationState 165 */ 166 167 if (req_end - req < 12) 168 return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); 169 170 /* Get ServiceRecordHandle and MaximumAttributeByteCount */ 171 SDP_GET32(handle, req); 172 SDP_GET16(rsp_limit, req); 173 if (rsp_limit <= 0) 174 return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); 175 176 /* Get size of AttributeIDList */ 177 aidlen = 0; 178 SDP_GET8(type, req); 179 switch (type) { 180 case SDP_DATA_SEQ8: 181 SDP_GET8(aidlen, req); 182 break; 183 184 case SDP_DATA_SEQ16: 185 SDP_GET16(aidlen, req); 186 break; 187 188 case SDP_DATA_SEQ32: 189 SDP_GET32(aidlen, req); 190 break; 191 } 192 if (aidlen <= 0) 193 return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); 194 195 ptr = (uint8_t *) req + aidlen; 196 197 /* Get ContinuationState */ 198 if (ptr + 1 > req_end) 199 return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); 200 201 SDP_GET8(cslen, ptr); 202 if (cslen != 0) { 203 if (cslen != 2 || req_end - ptr != 2) 204 return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX); 205 206 SDP_GET16(cs, ptr); 207 } else 208 cs = 0; 209 210 /* Process the request. First, check continuation state */ 211 if (srv->fdidx[fd].rsp_cs != cs) 212 return (SDP_ERROR_CODE_INVALID_CONTINUATION_STATE); 213 if (srv->fdidx[fd].rsp_size > 0) 214 return (0); 215 216 /* Lookup record handle */ 217 if ((provider = provider_by_handle(handle)) == NULL) 218 return (SDP_ERROR_CODE_INVALID_SERVICE_RECORD_HANDLE); 219 220 /* 221 * Service Attribute Response format 222 * 223 * value16 - 2 bytes AttributeListByteCount (not incl.) 224 * seq8 len16 - 3 bytes 225 * attr value - 3+ bytes AttributeList 226 * [ attr value ] 227 */ 228 229 cs = server_prepare_attr_list(provider, req, req+aidlen, rsp, rsp_end); 230 if (cs < 0) 231 return (SDP_ERROR_CODE_INSUFFICIENT_RESOURCES); 232 233 /* Set reply size (not counting PDU header and continuation state) */ 234 srv->fdidx[fd].rsp_limit = srv->fdidx[fd].omtu - sizeof(sdp_pdu_t) - 2; 235 if (srv->fdidx[fd].rsp_limit > rsp_limit) 236 srv->fdidx[fd].rsp_limit = rsp_limit; 237 238 srv->fdidx[fd].rsp_size = cs; 239 srv->fdidx[fd].rsp_cs = 0; 240 241 return (0); 242 } 243 244 /* 245 * Send SDP Service [Search] Attribute Response 246 */ 247 248 int32_t 249 server_send_service_attribute_response(server_p srv, int32_t fd) 250 { 251 uint8_t *rsp = srv->fdidx[fd].rsp + srv->fdidx[fd].rsp_cs; 252 uint8_t *rsp_end = srv->fdidx[fd].rsp + srv->fdidx[fd].rsp_size; 253 254 struct iovec iov[4]; 255 sdp_pdu_t pdu; 256 uint16_t bcount; 257 uint8_t cs[3]; 258 int32_t size; 259 260 /* First update continuation state (assume we will send all data) */ 261 size = rsp_end - rsp; 262 srv->fdidx[fd].rsp_cs += size; 263 264 if (size + 1 > srv->fdidx[fd].rsp_limit) { 265 /* 266 * We need to split out response. Add 3 more bytes for the 267 * continuation state and move rsp_end and rsp_cs backwards. 268 */ 269 270 while ((rsp_end - rsp) + 3 > srv->fdidx[fd].rsp_limit) { 271 rsp_end --; 272 srv->fdidx[fd].rsp_cs --; 273 } 274 275 cs[0] = 2; 276 cs[1] = srv->fdidx[fd].rsp_cs >> 8; 277 cs[2] = srv->fdidx[fd].rsp_cs & 0xff; 278 } else 279 cs[0] = 0; 280 281 assert(rsp_end >= rsp); 282 283 bcount = rsp_end - rsp; 284 285 if (((sdp_pdu_p)(srv->req))->pid == SDP_PDU_SERVICE_ATTRIBUTE_REQUEST) 286 pdu.pid = SDP_PDU_SERVICE_ATTRIBUTE_RESPONSE; 287 else 288 pdu.pid = SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_RESPONSE; 289 290 pdu.tid = ((sdp_pdu_p)(srv->req))->tid; 291 pdu.len = htons(sizeof(bcount) + bcount + 1 + cs[0]); 292 293 bcount = htons(bcount); 294 295 iov[0].iov_base = &pdu; 296 iov[0].iov_len = sizeof(pdu); 297 298 iov[1].iov_base = &bcount; 299 iov[1].iov_len = sizeof(bcount); 300 301 iov[2].iov_base = rsp; 302 iov[2].iov_len = rsp_end - rsp; 303 304 iov[3].iov_base = cs; 305 iov[3].iov_len = 1 + cs[0]; 306 307 do { 308 size = writev(fd, (struct iovec const *) &iov, sizeof(iov)/sizeof(iov[0])); 309 } while (size < 0 && errno == EINTR); 310 311 /* Check if we have sent (or failed to sent) last response chunk */ 312 if (srv->fdidx[fd].rsp_cs == srv->fdidx[fd].rsp_size) { 313 srv->fdidx[fd].rsp_cs = 0; 314 srv->fdidx[fd].rsp_size = 0; 315 srv->fdidx[fd].rsp_limit = 0; 316 } 317 318 return ((size < 0)? errno : 0); 319 } 320 321