1 /* $NetBSD: record.c,v 1.2 2010/03/07 10:58:40 plunky Exp $ */ 2 3 /*- 4 * Copyright (c) 2009 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Iain Hibbert. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __RCSID("$NetBSD: record.c,v 1.2 2010/03/07 10:58:40 plunky Exp $"); 34 35 #include <bluetooth.h> 36 #include <sdp.h> 37 #include <string.h> 38 39 #include "sdpd.h" 40 41 static bool sdpd_valid_record(sdp_data_t *); 42 43 /* 44 * These record manipulation requests are not part of the SDP 45 * specification, they are a private extension and valid only 46 * for privileged clients on the control socket. 47 */ 48 49 uint16_t 50 record_insert_request(server_t *srv, int fd) 51 { 52 sdp_data_t seq; 53 bdaddr_t bdaddr; 54 55 log_debug("RecordInsertRequest by client on fd#%d", fd); 56 57 seq.next = srv->ibuf; 58 seq.end = srv->ibuf + srv->pdu.len; 59 60 if (!srv->fdidx[fd].control 61 || !srv->fdidx[fd].priv) 62 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 63 64 srv->fdidx[fd].offset = 0; 65 db_unselect(srv, fd); 66 67 /* 68 * extract BluetoothDeviceAddress 69 */ 70 if (seq.next + sizeof(bdaddr_t) > seq.end) 71 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 72 73 memcpy(&bdaddr, seq.next, sizeof(bdaddr_t)); 74 seq.next += sizeof(bdaddr_t); 75 76 /* 77 * extract ServiceRecord and add to database 78 */ 79 if (!sdp_get_seq(&seq, &seq) 80 || !sdpd_valid_record(&seq)) 81 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 82 83 /* (ignores any additional data) */ 84 85 if (!db_create(srv, fd, &bdaddr, srv->handle, &seq)) 86 return SDP_ERROR_CODE_INSUFFICIENT_RESOURCES; 87 88 /* 89 * encode 'success' ErrorCode and ServiceRecordHandle and 90 * bump server handle 91 */ 92 be16enc(srv->obuf, 0x0000); 93 be32enc(srv->obuf + sizeof(uint16_t), srv->handle++); 94 95 /* 96 * fill in PDU header and we are done 97 */ 98 srv->pdu.pid = SDP_PDU_ERROR_RESPONSE; 99 srv->pdu.len = sizeof(uint16_t) + sizeof(uint32_t); 100 return 0; 101 } 102 103 uint16_t 104 record_update_request(server_t *srv, int fd) 105 { 106 record_t *rec; 107 sdp_data_t seq; 108 109 log_debug("RecordUpdateRequest by client on fd#%d", fd); 110 111 seq.next = srv->ibuf; 112 seq.end = srv->ibuf + srv->pdu.len; 113 114 if (!srv->fdidx[fd].control 115 || !srv->fdidx[fd].priv) 116 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 117 118 srv->fdidx[fd].offset = 0; 119 db_unselect(srv, fd); 120 121 /* 122 * extract ServiceRecordHandle and select record 123 */ 124 if (seq.next + sizeof(uint32_t) > seq.end) 125 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 126 127 db_select_handle(srv, fd, be32dec(seq.next)); 128 seq.next += sizeof(uint32_t); 129 130 rec = NULL; 131 db_next(srv, fd, &rec); 132 if (rec == NULL || rec->fd != fd) 133 return SDP_ERROR_CODE_INVALID_SERVICE_RECORD_HANDLE; 134 135 db_unselect(srv, fd); 136 137 /* 138 * extract ServiceRecord and add to database 139 */ 140 if (!sdp_get_seq(&seq, &seq) 141 || !sdpd_valid_record(&seq)) 142 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 143 144 /* (ignores any additional data) */ 145 146 if (!db_create(srv, fd, &rec->bdaddr, rec->handle, &seq)) 147 return SDP_ERROR_CODE_INSUFFICIENT_RESOURCES; 148 149 /* 150 * encode 'success' ErrorCode 151 */ 152 be16enc(srv->obuf, 0x0000); 153 154 /* 155 * fill in PDU header and we are done 156 */ 157 srv->pdu.pid = SDP_PDU_ERROR_RESPONSE; 158 srv->pdu.len = sizeof(uint16_t); 159 return 0; 160 } 161 162 uint16_t 163 record_remove_request(server_t *srv, int fd) 164 { 165 record_t *rec; 166 167 log_debug("RecordRemoveRequest by client on fd#%d", fd); 168 169 if (!srv->fdidx[fd].control 170 || !srv->fdidx[fd].priv) 171 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 172 173 srv->fdidx[fd].offset = 0; 174 db_unselect(srv, fd); 175 176 /* 177 * extract ServiceRecordHandle 178 */ 179 if (srv->pdu.len != sizeof(uint32_t)) 180 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 181 182 db_select_handle(srv, fd, be32dec(srv->ibuf)); 183 184 rec = NULL; 185 db_next(srv, fd, &rec); 186 if (rec == NULL || rec->fd != fd) 187 return SDP_ERROR_CODE_INVALID_SERVICE_RECORD_HANDLE; 188 189 /* 190 * expire the record 191 */ 192 rec->refcnt--; 193 rec->valid = false; 194 rec->fd = -1; 195 db_unselect(srv, fd); 196 197 /* 198 * encode 'success' ErrorCode 199 */ 200 be16enc(srv->obuf, 0x0000); 201 202 /* 203 * fill in PDU header and we are done 204 */ 205 srv->pdu.pid = SDP_PDU_ERROR_RESPONSE; 206 srv->pdu.len = sizeof(uint16_t); 207 return 0; 208 } 209 210 /* 211 * validate ServiceRecord data element list 212 * 213 * The record must contain a list of attribute/value pairs where the 214 * attributes are unsigned 16-bit integer values in ascending order. 215 */ 216 static bool 217 sdpd_valid_record(sdp_data_t *data) 218 { 219 sdp_data_t d, s; 220 uintmax_t a0, a; 221 222 s = *data; 223 if (!sdp_data_valid(&s)) 224 return false; 225 226 /* The first attribute must be ServiceRecordHandle */ 227 if (!sdp_get_data(&s, &d) 228 || sdp_data_type(&d) != SDP_DATA_UINT16 229 || !sdp_get_uint(&d, &a0) 230 || a0 != SDP_ATTR_SERVICE_RECORD_HANDLE 231 || !sdp_get_data(&s, &d) 232 || sdp_data_type(&d) != SDP_DATA_UINT32) 233 return false; 234 235 /* and remaining attribute IDs must be in ascending order */ 236 while (sdp_get_data(&s, &d) 237 && sdp_data_type(&d) == SDP_DATA_UINT16 238 && sdp_get_uint(&d, &a) 239 && a > a0 240 && sdp_get_data(&s, &d)) 241 a0 = a; 242 243 if (s.next != s.end) 244 return false; 245 246 return true; 247 } 248