text: // SPDX-License-Identifier: GPL-2.0-or-later
  /*
   *
   *  BlueZ - Bluetooth protocol stack for Linux
   *
   *  Copyright (C) 2001-2002  Nokia Corporation
   *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
   *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
   *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com>
   *
   *
   */
  
  #ifdef HAVE_CONFIG_H
  #include <config.h>
  #endif
  
  #include <stdlib.h>
  #include <stdbool.h>
  
  #include "lib/bluetooth.h"
  #include "lib/sdp.h"
  #include "lib/sdp_lib.h"
  
  #include "sdpd.h"
  #include "log.h"
  
  static sdp_list_t *service_db;
  static sdp_list_t *access_db;
  
  typedef struct {
  	uint32_t handle;
  	bdaddr_t device;
  } sdp_access_t;
  
  /*
   * Ordering function called when inserting a service record.
   * The service repository is a linked list in sorted order
   * and the service record handle is the sort key
   */
  int record_sort(const void *r1, const void *r2)
  {
  	const sdp_record_t *rec1 = r1;
  	const sdp_record_t *rec2 = r2;
  
  	if (!rec1 || !rec2) {
  		error("NULL RECORD LIST FATAL");
  		return -1;
  	}
  
  	return rec1->handle - rec2->handle;
  }
  
  static int access_sort(const void *r1, const void *r2)
  {
  	const sdp_access_t *rec1 = r1;
  	const sdp_access_t *rec2 = r2;
  
  	if (!rec1 || !rec2) {
  		error("NULL RECORD LIST FATAL");
  		return -1;
  	}
  
  	return rec1->handle - rec2->handle;
  }
  
  static void access_free(void *p)
  {
  	free(p);
  }
  
  /*
   * Reset the service repository by deleting its contents
   */
  void sdp_svcdb_reset(void)
  {
  	sdp_list_free(service_db, (sdp_free_func_t) sdp_record_free);
  	service_db = NULL;
  
  	sdp_list_free(access_db, access_free);
  	access_db = NULL;
  }
  
  typedef struct _indexed {
  	int sock;
  	sdp_record_t *record;
  } sdp_indexed_t;
  
  static sdp_list_t *socket_index;
  
  /*
   * collect all services registered over this socket
   */
  void sdp_svcdb_collect_all(int sock)
  {
  	sdp_list_t *p, *q;
  
  	for (p = socket_index, q = 0; p; ) {
  		sdp_indexed_t *item = p->data;
  		if (item->sock == sock) {
  			sdp_list_t *next = p->next;
  			sdp_record_remove(item->record->handle);
  			sdp_record_free(item->record);
  			free(item);
  			if (q)
  				q->next = next;
  			else
  				socket_index = next;
  			free(p);
  			p = next;
  		} else if (item->sock > sock)
  			return;
  		else {
  			q = p;
  			p = p->next;
  		}
  	}
  }
  
  void sdp_svcdb_collect(sdp_record_t *rec)
  {
  	sdp_list_t *p, *q;
  
  	for (p = socket_index, q = 0; p; q = p, p = p->next) {
  		sdp_indexed_t *item = p->data;
  		if (rec == item->record) {
  			free(item);
  			if (q)
  				q->next = p->next;
  			else
  				socket_index = p->next;
  			free(p);
  			return;
  		}
  	}
  }
  
  static int compare_indices(const void *i1, const void *i2)
  {
  	const sdp_indexed_t *s1 = i1;
  	const sdp_indexed_t *s2 = i2;
  	return s1->sock - s2->sock;
  }
  
  void sdp_svcdb_set_collectable(sdp_record_t *record, int sock)
  {
  	sdp_indexed_t *item = malloc(sizeof(sdp_indexed_t));
  
  	if (!item) {
  		SDPDBG("No memory");
  		return;
  	}
  
  	item->sock = sock;
  	item->record = record;
  	socket_index = sdp_list_insert_sorted(socket_index, item, compare_indices);
  }
  
  /*
   * Add a service record to the repository
   */
  void sdp_record_add(const bdaddr_t *device, sdp_record_t *rec)
  {
  	sdp_access_t *dev;
  
  	SDPDBG("Adding rec : 0x%lx", (long) rec);
  	SDPDBG("with handle : 0x%x", rec->handle);
  
  	service_db = sdp_list_insert_sorted(service_db, rec, record_sort);
  
  	dev = malloc(sizeof(*dev));
  	if (!dev)
  		return;
  
  	bacpy(&dev->device, device);
  	dev->handle = rec->handle;
  
  	access_db = sdp_list_insert_sorted(access_db, dev, access_sort);
  }
  
  static sdp_list_t *record_locate(uint32_t handle)
  {
  	if (service_db) {
  		sdp_list_t *p;
  		sdp_record_t r;
  
  		r.handle = handle;
  		p = sdp_list_find(service_db, &r, record_sort);
  		return p;
  	}
  
  	SDPDBG("Could not find svcRec for : 0x%x", handle);
  	return NULL;
  }
  
  static sdp_list_t *access_locate(uint32_t handle)
  {
  	if (access_db) {
  		sdp_list_t *p;
  		sdp_access_t a;
  
  		a.handle = handle;
  		p = sdp_list_find(access_db, &a, access_sort);
  		return p;
  	}
  
  	SDPDBG("Could not find access data for : 0x%x", handle);
  	return NULL;
  }
  
  /*
   * Given a service record handle, find the record associated with it.
   */
  sdp_record_t *sdp_record_find(uint32_t handle)
  {
  	sdp_list_t *p = record_locate(handle);
  
  	if (!p) {
  		SDPDBG("Couldn't find record for : 0x%x", handle);
  		return 0;
  	}
  
  	return p->data;
  }
  
  /*
   * Given a service record handle, remove its record from the repository
   */
  int sdp_record_remove(uint32_t handle)
  {
  	sdp_list_t *p = record_locate(handle);
  	sdp_record_t *r;
  	sdp_access_t *a;
  
  	if (!p) {
  		error("Remove : Couldn't find record for : 0x%x", handle);
  		return -1;
  	}
  
  	r = p->data;
  	if (r)
  		service_db = sdp_list_remove(service_db, r);
  
  	p = access_locate(handle);
  	if (p == NULL || p->data == NULL)
  		return 0;
  
  	a = p->data;
  
  	access_db = sdp_list_remove(access_db, a);
  	access_free(a);
  
  	return 0;
  }
  
  /*
   * Return a pointer to the linked list containing the records in sorted order
   */
  sdp_list_t *sdp_get_record_list(void)
  {
  	return service_db;
  }
  
  int sdp_check_access(uint32_t handle, bdaddr_t *device)
  {
  	sdp_list_t *p = access_locate(handle);
  	sdp_access_t *a;
  
  	if (!p)
  		return 1;
  
  	a = p->data;
  	if (!a)
  		return 1;
  
  	if (bacmp(&a->device, device) &&
  			bacmp(&a->device, BDADDR_ANY) &&
  			bacmp(device, BDADDR_ANY))
  		return 0;
  
  	return 1;
  }
  
  uint32_t sdp_next_handle(void)
  {
  	uint32_t handle = 0x10000;
  
  	while (sdp_record_find(handle))
  		handle++;
  
  	return handle;
  }
  