1/*
2 *
3 *  BlueZ - Bluetooth protocol stack for Linux
4 *
5 *  Copyright (C) 2001-2002  Nokia Corporation
6 *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
7 *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
8 *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com>
9 *
10 *
11 *  This program is free software; you can redistribute it and/or modify
12 *  it under the terms of the GNU General Public License as published by
13 *  the Free Software Foundation; either version 2 of the License, or
14 *  (at your option) any later version.
15 *
16 *  This program is distributed in the hope that it will be useful,
17 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 *  GNU General Public License for more details.
20 *
21 *  You should have received a copy of the GNU General Public License
22 *  along with this program; if not, write to the Free Software
23 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
24 *
25 */
26
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#endif
30
31#include <stdio.h>
32#include <errno.h>
33#include <stdlib.h>
34#include <sys/socket.h>
35
36#include <bluetooth/bluetooth.h>
37#include <bluetooth/l2cap.h>
38#include <bluetooth/sdp.h>
39#include <bluetooth/sdp_lib.h>
40
41#include "sdpd.h"
42#include "log.h"
43#include "adapter.h"
44#include "manager.h"
45
46static sdp_list_t *service_db;
47static sdp_list_t *access_db;
48
49typedef struct {
50	uint32_t handle;
51	bdaddr_t device;
52} sdp_access_t;
53
54/*
55 * Ordering function called when inserting a service record.
56 * The service repository is a linked list in sorted order
57 * and the service record handle is the sort key
58 */
59int record_sort(const void *r1, const void *r2)
60{
61	const sdp_record_t *rec1 = r1;
62	const sdp_record_t *rec2 = r2;
63
64	if (!rec1 || !rec2) {
65		error("NULL RECORD LIST FATAL");
66		return -1;
67	}
68
69	return rec1->handle - rec2->handle;
70}
71
72static int access_sort(const void *r1, const void *r2)
73{
74	const sdp_access_t *rec1 = r1;
75	const sdp_access_t *rec2 = r2;
76
77	if (!rec1 || !rec2) {
78		error("NULL RECORD LIST FATAL");
79		return -1;
80	}
81
82	return rec1->handle - rec2->handle;
83}
84
85static void access_free(void *p)
86{
87	free(p);
88}
89
90/*
91 * Reset the service repository by deleting its contents
92 */
93void sdp_svcdb_reset(void)
94{
95	sdp_list_free(service_db, (sdp_free_func_t) sdp_record_free);
96	sdp_list_free(access_db, access_free);
97}
98
99typedef struct _indexed {
100	int sock;
101	sdp_record_t *record;
102} sdp_indexed_t;
103
104static sdp_list_t *socket_index;
105
106/*
107 * collect all services registered over this socket
108 */
109void sdp_svcdb_collect_all(int sock)
110{
111	sdp_list_t *p, *q;
112
113	for (p = socket_index, q = 0; p; ) {
114		sdp_indexed_t *item = p->data;
115		if (item->sock == sock) {
116			sdp_list_t *next = p->next;
117			sdp_record_remove(item->record->handle);
118			sdp_record_free(item->record);
119			free(item);
120			if (q)
121				q->next = next;
122			else
123				socket_index = next;
124			free(p);
125			p = next;
126		} else if (item->sock > sock)
127			return;
128		else {
129			q = p;
130			p = p->next;
131		}
132	}
133}
134
135void sdp_svcdb_collect(sdp_record_t *rec)
136{
137	sdp_list_t *p, *q;
138
139	for (p = socket_index, q = 0; p; q = p, p = p->next) {
140		sdp_indexed_t *item = p->data;
141		if (rec == item->record) {
142			free(item);
143			if (q)
144				q->next = p->next;
145			else
146				socket_index = p->next;
147			free(p);
148			return;
149		}
150	}
151}
152
153static int compare_indices(const void *i1, const void *i2)
154{
155	const sdp_indexed_t *s1 = i1;
156	const sdp_indexed_t *s2 = i2;
157	return s1->sock - s2->sock;
158}
159
160void sdp_svcdb_set_collectable(sdp_record_t *record, int sock)
161{
162	sdp_indexed_t *item = malloc(sizeof(sdp_indexed_t));
163	item->sock = sock;
164	item->record = record;
165	socket_index = sdp_list_insert_sorted(socket_index, item, compare_indices);
166}
167
168/*
169 * Add a service record to the repository
170 */
171void sdp_record_add(const bdaddr_t *device, sdp_record_t *rec)
172{
173	struct btd_adapter *adapter;
174	sdp_access_t *dev;
175
176	SDPDBG("Adding rec : 0x%lx", (long) rec);
177	SDPDBG("with handle : 0x%x", rec->handle);
178
179	service_db = sdp_list_insert_sorted(service_db, rec, record_sort);
180
181	dev = malloc(sizeof(*dev));
182	if (!dev)
183		return;
184
185	bacpy(&dev->device, device);
186	dev->handle = rec->handle;
187
188	access_db = sdp_list_insert_sorted(access_db, dev, access_sort);
189
190	if (bacmp(device, BDADDR_ANY) == 0) {
191		manager_foreach_adapter(adapter_service_insert, rec);
192		return;
193	}
194
195	adapter = manager_find_adapter(device);
196	if (adapter)
197		adapter_service_insert(adapter, rec);
198}
199
200static sdp_list_t *record_locate(uint32_t handle)
201{
202	if (service_db) {
203		sdp_list_t *p;
204		sdp_record_t r;
205
206		r.handle = handle;
207		p = sdp_list_find(service_db, &r, record_sort);
208		return p;
209	}
210
211	SDPDBG("Could not find svcRec for : 0x%x", handle);
212	return NULL;
213}
214
215static sdp_list_t *access_locate(uint32_t handle)
216{
217	if (access_db) {
218		sdp_list_t *p;
219		sdp_access_t a;
220
221		a.handle = handle;
222		p = sdp_list_find(access_db, &a, access_sort);
223		return p;
224	}
225
226	SDPDBG("Could not find access data for : 0x%x", handle);
227	return NULL;
228}
229
230/*
231 * Given a service record handle, find the record associated with it.
232 */
233sdp_record_t *sdp_record_find(uint32_t handle)
234{
235	sdp_list_t *p = record_locate(handle);
236
237	if (!p) {
238		SDPDBG("Couldn't find record for : 0x%x", handle);
239		return 0;
240	}
241
242	return p->data;
243}
244
245/*
246 * Given a service record handle, remove its record from the repository
247 */
248int sdp_record_remove(uint32_t handle)
249{
250	sdp_list_t *p = record_locate(handle);
251	sdp_record_t *r;
252	sdp_access_t *a;
253
254	if (!p) {
255		error("Remove : Couldn't find record for : 0x%x", handle);
256		return -1;
257	}
258
259	r = p->data;
260	if (r)
261		service_db = sdp_list_remove(service_db, r);
262
263	p = access_locate(handle);
264	if (p == NULL || p->data == NULL)
265		return 0;
266
267	a = p->data;
268
269	if (bacmp(&a->device, BDADDR_ANY) != 0) {
270		struct btd_adapter *adapter = manager_find_adapter(&a->device);
271		if (adapter)
272			adapter_service_remove(adapter, r);
273	} else
274		manager_foreach_adapter(adapter_service_remove, r);
275
276	access_db = sdp_list_remove(access_db, a);
277	access_free(a);
278
279	return 0;
280}
281
282/*
283 * Return a pointer to the linked list containing the records in sorted order
284 */
285sdp_list_t *sdp_get_record_list(void)
286{
287	return service_db;
288}
289
290sdp_list_t *sdp_get_access_list(void)
291{
292	return access_db;
293}
294
295int sdp_check_access(uint32_t handle, bdaddr_t *device)
296{
297	sdp_list_t *p = access_locate(handle);
298	sdp_access_t *a;
299
300	if (!p)
301		return 1;
302
303	a = p->data;
304	if (!a)
305		return 1;
306
307	if (bacmp(&a->device, device) &&
308			bacmp(&a->device, BDADDR_ANY) &&
309			bacmp(device, BDADDR_ANY))
310		return 0;
311
312	return 1;
313}
314
315uint32_t sdp_next_handle(void)
316{
317	uint32_t handle = 0x10000;
318
319	while (sdp_record_find(handle))
320		handle++;
321
322	return handle;
323}
324
325void sdp_init_services_list(bdaddr_t *device)
326{
327	sdp_list_t *p;
328
329	DBG("");
330
331	for (p = access_db; p != NULL; p = p->next) {
332		sdp_access_t *access = p->data;
333		sdp_record_t *rec;
334
335		if (bacmp(BDADDR_ANY, &access->device))
336			continue;
337
338		rec = sdp_record_find(access->handle);
339		if (rec == NULL)
340			continue;
341
342		SDPDBG("adding record with handle %x", access->handle);
343
344		manager_foreach_adapter(adapter_service_insert, rec);
345	}
346}
347