diff options
| author | Claudio Takahasi <claudio.takahasi@openbossa.org> | 2006-05-05 17:30:02 +0000 | 
|---|---|---|
| committer | Claudio Takahasi <claudio.takahasi@openbossa.org> | 2006-05-05 17:30:02 +0000 | 
| commit | 7f1664522f8f9feb612f9f0c48cd6212cf906d45 (patch) | |
| tree | cd990c2a75668365e523977d1ef34db318a81c7d | |
| parent | f72f6d99edab2ebab198ea0d7c7dbd4721362e25 (diff) | |
Added GetIdentifiers, GetUUID and GetName.
| -rw-r--r-- | hcid/dbus-sdp.c | 1057 | ||||
| -rw-r--r-- | hcid/dbus.h | 8 | 
2 files changed, 801 insertions, 264 deletions
| diff --git a/hcid/dbus-sdp.c b/hcid/dbus-sdp.c index 3a01b6b7..c31512dc 100644 --- a/hcid/dbus-sdp.c +++ b/hcid/dbus-sdp.c @@ -41,136 +41,385 @@  #include <bluetooth/sdp.h>  #include <bluetooth/sdp_lib.h> +#include <netinet/in.h> +  #include <dbus/dbus.h>  #include "dbus.h"  #include "hcid.h"  #include "textfile.h" -struct dbus_sdp_record { -	char *owner;		/* null for remote services or unique name if local */ -	bdaddr_t provider;	/* remote Bluetooth address or local address */ -	char *name;		/* service name */ +#define SDP_UUID_SEQ_SIZE 256 +#define SDP_MAX_ATTR_LEN 65535 + + +struct service_provider { +	char *owner;	/* null for remote services or unique name if local */ +	bdaddr_t prov;	/* remote Bluetooth address or local address */ +	struct slist *lrec; +}; + +struct service_record {  	uint32_t identifier; -	uint16_t uuid; -	uint8_t channel; +	sdp_record_t *record;  }; -/* list of remote and local service records */ -static struct slist *sdp_records = NULL; +struct pending_connect { +	DBusConnection *conn; +	DBusMessage *rq; +	char *svc; +	bdaddr_t dba; +}; -struct search_request_info *search_request_info_new(bdaddr_t *dba, DBusMessage *msg) -{ -	struct search_request_info *search = malloc(sizeof(struct search_request_info)); -	if (!search) -		return NULL; +struct transaction_context { +	DBusConnection *conn; +	DBusMessage *rq; +	char *svc; +	sdp_session_t *session; +	sdp_cstate_t *cstate; +	uint8_t *reqbuf; +	bdaddr_t dba; +	sdp_buf_t rspbuf; +	uint32_t reqsize; +	int attr_list_len; +}; -	memset(search, 0, sizeof(*search)); +/* FIXME: store the arguments or just the pointer to the message */ -	bacpy(&search->bdaddr, dba); -	search->rq = dbus_message_ref(msg); +/* list of remote and local service records + * FIXME: free the cache when the local sock(sdpd) is closed + */ -	return search; -} -void search_request_info_free(struct search_request_info *search) +static struct slist *sdp_cache = NULL; +static struct slist *pending_connects  = NULL; + +static const char *ecode2str(uint16_t ecode)  { -	if (search->rq) -		dbus_message_unref(search->rq); -	if (search->session) -		free(search->session); -	if (search->io) -		free(search->io); - -	free(search); +	switch (ecode) { +	case 0x0000: +		return "Reserved"; +	case 0x0001: +		return "Invalid/Unsupported SDP version"; +	case 0x0002: +		return "Invalid Service Record Handle"; +	case 0x0003: +		return "Invalid request syntax"; +	case 0x0004: +		return "Invalid PDU size"; +	case 0x0005: +		return "Invalid Continuation State"; +	case 0x0006: +		return "Insufficient Resources to satisfy Request"; +	default: +		return "Reserved"; +	}  } -void dbus_sdp_record_free(struct dbus_sdp_record *rec) +static struct pending_connect *pending_connect_new(DBusConnection *conn, DBusMessage *msg, +							const bdaddr_t *bda, const char *svc)  { -	if (rec->owner) -		free(rec->owner); -	if (rec->name) -		free(rec->name); +	struct pending_connect *c; + +	if (!bda) +		return NULL; + +	c = malloc(sizeof(*c)); + +	memset(c, 0, sizeof(*c)); + +	if (svc) { +		c->svc = strdup(svc); +		if (!c->svc) +			goto failed; +	} -	free(rec); +	bacpy(&c->dba, bda); +	c->conn = dbus_connection_ref(conn); +	c->rq = dbus_message_ref(msg); + +	return c; + +failed: +	if (c) +		free(c); +	return NULL;  } -static void id2str(uint32_t id, char *dst) +static void pending_connect_free(struct pending_connect *c)  { -	snprintf(dst, 9, "%2.2X%2.2X%2.2X%2.2X", (id >> 24) & 0xFF, -			(id >> 16) & 0xFF, (id >> 8) & 0xFF, id & 0xFF); -	debug ("%s, identifier:%s", __PRETTY_FUNCTION__, dst); +	if (!c) +		return; + +	if (c->svc) +		free(c->svc); + +	if (c->rq) +		dbus_message_unref(c->rq); + +	if (c->conn) +		dbus_connection_unref(c->conn); + +	free(c);  } -static uint32_t str2id(const char *id) +static struct pending_connect *find_pending_connect(const bdaddr_t *bda)  { -	return atoi(id); -} +	struct slist *l; +	for (l = pending_connects; l != NULL; l = l->next) { +		struct pending_connect *pending = l->data; +		if (!bacmp(bda, &pending->dba)) +			return pending; +	} -static uint32_t generate_new_id(const bdaddr_t *provider, uint8_t channel) +	return NULL; +} +/* FIXME: duplicated function. Make this function public on bluez-libs */ +static int gen_dataseq_pdu(uint8_t *dst, const sdp_list_t *seq, uint8_t dtd)  { -	/* FIXME: generate the pseudo random id */ -	return 1; +	sdp_data_t *dataseq; +	void **types, **values; +	sdp_buf_t buf; +	int i, seqlen = sdp_list_len(seq); + +	// Fill up the value and the dtd arrays +	memset(&buf, 0, sizeof(sdp_buf_t)); +	buf.data = malloc(SDP_UUID_SEQ_SIZE); +	buf.buf_size = SDP_UUID_SEQ_SIZE; + +	types = malloc(seqlen * sizeof(void *)); +	values = malloc(seqlen * sizeof(void *)); +	for (i = 0; i < seqlen; i++) { +		void *data = seq->data; +		types[i] = &dtd; +		if (SDP_IS_UUID(dtd)) +			data = &((uuid_t *)data)->value; +		values[i] = data; +		seq = seq->next; +	} + +	dataseq = sdp_seq_alloc(types, values, seqlen); +	seqlen = sdp_gen_pdu(&buf, dataseq); +	memcpy(dst, buf.data, buf.data_size); + +	sdp_data_free(dataseq); + +	free(types); +	free(values); +	free(buf.data); +	return seqlen;  } -struct dbus_sdp_record *dbus_sdp_record_new(const char *owner, bdaddr_t *provider, -						const char *name, uint32_t uuid, uint8_t channel) +/* FIXME: duplicated function */ +static int gen_searchseq_pdu(uint8_t *dst, const sdp_list_t *seq)  { -	struct dbus_sdp_record *rec; +	uuid_t *uuid = (uuid_t *) seq->data; +	return gen_dataseq_pdu(dst, seq, uuid->type); +} -	/* FIXME: validate the arguments */ +/* FIXME: duplicated function */ +static int gen_attridseq_pdu(uint8_t *dst, const sdp_list_t *seq, uint8_t dataType) +{ +	return gen_dataseq_pdu(dst, seq, dataType); +} -	rec = malloc(sizeof(struct dbus_sdp_record)); -	if (!rec) +struct transaction_context *transaction_context_new(DBusConnection *conn, DBusMessage *msg, bdaddr_t *dba, +							const char *svc, int sock, uint32_t flags) +{ +	struct transaction_context *ctxt; +	sdp_pdu_hdr_t *reqhdr; +	sdp_list_t *pattern = NULL; +	sdp_list_t *attrids = NULL; +	uint8_t *pdata; +	uuid_t uuid; +	uint32_t range = 0x0000ffff; +	int seqlen; + +	ctxt = malloc(sizeof(*ctxt)); +	if (!ctxt)  		return NULL; -	memset(rec, 0, sizeof(*rec)); -	if (owner) { -		rec->owner = strdup(owner); -		if (!rec->owner) -			goto mem_fail; +	memset(ctxt, 0, sizeof(*ctxt)); + +	if (svc) { +		ctxt->svc = strdup(svc); +		if (!ctxt->svc) +			goto failed;  	} -	bacpy(&rec->provider, provider); +	if (dba) +		bacpy(&ctxt->dba, dba); + +	ctxt->session = malloc(sizeof(sdp_session_t)); +	if (!ctxt->session) +		goto failed; + +	memset(ctxt->session, 0, sizeof(sdp_session_t)); + +	ctxt->conn = dbus_connection_ref(conn); +	ctxt->rq = dbus_message_ref(msg); +	ctxt->session->sock = sock; +	ctxt->session->flags = flags; + +	ctxt->reqbuf = malloc(SDP_REQ_BUFFER_SIZE); +	if (!ctxt->reqbuf) +		goto failed; + +	memset(ctxt->reqbuf, 0, SDP_REQ_BUFFER_SIZE); -	rec->name = strdup(name); -	if(!rec->name) -		goto mem_fail; +	reqhdr = (sdp_pdu_hdr_t *) ctxt->reqbuf; + +	reqhdr->pdu_id = SDP_SVC_SEARCH_ATTR_REQ;  +	reqhdr->tid = 0; + +	// Generate PDU +	pdata = ctxt->reqbuf + sizeof(sdp_pdu_hdr_t); +	ctxt->reqsize = sizeof(sdp_pdu_hdr_t); + +	/* FIXME: it should be generic to handle other kind of search requests */ +	sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); +	pattern = sdp_list_append(0, &uuid); +	attrids = sdp_list_append(0, &range); + +	seqlen = gen_searchseq_pdu(pdata, pattern); -	rec->uuid = uuid; -	rec->channel = channel; -	rec->identifier = generate_new_id(provider, channel); +	// set the length and increment the pointer +	ctxt->reqsize += seqlen; +	pdata +=seqlen; + +	bt_put_unaligned(htons(SDP_MAX_ATTR_LEN), (uint16_t *) pdata); +	ctxt->reqsize += sizeof(uint16_t); +	pdata += sizeof(uint16_t); + +	if (attrids) { +		seqlen = gen_attridseq_pdu(pdata, attrids, SDP_UINT32); +		if (seqlen == -1) +			goto failed; + +	} +	if (pattern) +		sdp_list_free(pattern, 0); +	if (attrids) +		sdp_list_free(attrids, 0); + +	pdata += seqlen; +	ctxt->reqsize += seqlen; -	return rec; +	reqhdr->plen = htons(ctxt->reqsize - sizeof(sdp_pdu_hdr_t)); -mem_fail: -	dbus_sdp_record_free(rec); +	return ctxt; + +failed: +	if (ctxt->session) +		free(ctxt->session); +	if (ctxt->reqbuf) +		free(ctxt->reqbuf); +	free(ctxt); +	  	return NULL;  } -static void owner_exited(const char *owner, struct hci_dbus_data *dbus_data) +void transaction_context_free(struct transaction_context *ctxt)  { -	struct slist *cur, *next; +	if (!ctxt) +		return; -	debug("SDP provider owner %s exited", owner); +	if (ctxt->conn) +		dbus_connection_unref(ctxt->conn); -	for (cur = sdp_records; cur != NULL; cur = next) { -		struct dbus_sdp_record *rec = cur->data; +	if (ctxt->rq) +		dbus_message_unref(ctxt->rq); -		next = cur->next; +	if (ctxt->svc) +		free(ctxt->svc); -		if(!rec->owner) -			continue; +	if (ctxt->session) +		free(ctxt->session); -		if (strcmp(rec->owner, owner)) -			continue; +	if (ctxt->reqbuf) +		free(ctxt->reqbuf); + +	if (ctxt->rspbuf.data) +		free(ctxt->rspbuf.data); + +	free(ctxt); +} + +/* FIXME: generate the pseudo random id */ +static uint32_t gen_next_id(const bdaddr_t *prov, uint32_t handle) +{ +	static uint32_t id; +	return ++id; +} + +static struct service_record *service_record_new(const bdaddr_t *prov, sdp_record_t *rec) +{ +	struct service_record *r; +	 +	if (!prov) +		return NULL; + +	r = malloc(sizeof(*r)); +	if (!r) +		return NULL; + +	memset(r, 0, sizeof(*r)); +	r->identifier = gen_next_id(prov, rec->handle); +	r->record = rec; + +	return r; +} + +static void service_record_free(struct service_record *r, void *data) +{ +	if (!r) +		return; + +	sdp_record_free(r->record); +	free(r); +} + +static void service_provider_free(struct service_provider *p) +{ +	if (p->owner) +		free(p->owner); -		sdp_records = slist_remove(sdp_records, rec); -		dbus_sdp_record_free(rec); +	if (p->lrec) { +		slist_foreach(p->lrec, (slist_func_t)service_record_free, NULL); +		slist_free(p->lrec);  	} + +	free(p);  } -static int record_cmp(const struct dbus_sdp_record *a, const struct dbus_sdp_record *b) +static struct service_provider *service_provider_new(const char *owner, const bdaddr_t *prov) +{ +	struct service_provider *p; + +	if (!prov) +		return NULL; + +	p = malloc(sizeof(struct service_provider)); +	if (!p) +		return NULL; + +	memset(p, 0, sizeof(*p)); +	if (owner) { +		p->owner = strdup(owner); +		if (!p->owner) +			goto fail; +	} + +	bacpy(&p->prov, prov); + +	return p; + +fail: +	service_provider_free(p); +	return NULL; +} + +static int service_provider_cmp(const struct service_provider *a, const struct service_provider *b)  {  	int ret; @@ -182,96 +431,361 @@ static int record_cmp(const struct dbus_sdp_record *a, const struct dbus_sdp_rec  			return ret;  	} -	if (bacmp(&b->provider, BDADDR_ANY)) { -		if (!bacmp(&a->provider, BDADDR_ANY)) +	if (bacmp(&b->prov, BDADDR_ANY)) { +		if (!bacmp(&a->prov, BDADDR_ANY))  			return -1; -		ret = bacmp(&a->provider, &b->provider); +		ret = bacmp(&a->prov, &b->prov);  		if (ret)  			return ret;  	} -	if (b->uuid) { -		ret = (a->uuid - b->uuid); -		if (ret) -			return ret; +	return 0; +} + +static uint32_t sdp_cache_append(const char *owner, const bdaddr_t *prov, sdp_record_t *rec) +{ +	struct slist *l; +	struct service_provider *p; +	struct service_provider *ref; +	struct service_record *r; +	 +	if (!prov || !rec) +		return 0; + +	ref = service_provider_new(owner, prov); +	if (!ref) +		return 0; + +	l = slist_find(sdp_cache, (const void*)ref, (cmp_func_t)service_provider_cmp); +	if (!l) { +		p = service_provider_new(owner, prov); +		sdp_cache = slist_append(sdp_cache, p); +	} else +		p = l->data; + +	r = service_record_new(prov, rec); +	p->lrec = slist_append(p->lrec, r); + +	if (ref) +		service_provider_free(ref); + +	return r->identifier; +} + +static void owner_exited(const char *owner, struct hci_dbus_data *dbus_data) +{ +	struct slist *cur, *next; + +	debug("SDP provider owner %s exited", owner); + +	for (cur = sdp_cache; cur != NULL; cur = next) { +		struct service_provider *p = cur->data; + +		next = cur->next; + +		if(!p->owner) +			continue; + +		if (strcmp(p->owner, owner)) +			continue; + +		sdp_cache = slist_remove(sdp_cache, p); +		service_provider_free(p);  	} +} -	if (b->channel) { -		ret = (a->channel - b->channel); -		if (ret) -			return ret; +/* FIXME: duplicated function */ +static int copy_cstate(uint8_t *pdata, const sdp_cstate_t *cstate) +{ +	if (cstate) { +		*pdata++ = cstate->length; +		memcpy(pdata, cstate->data, cstate->length); +		return cstate->length + 1; +	} +	*pdata = 0; +	return 1; +} + +static int sdp_send_req(struct transaction_context *ctxt, int *err) +{ +	sdp_pdu_hdr_t *reqhdr = (sdp_pdu_hdr_t *) ctxt->reqbuf; +	uint32_t sent = 0; +	uint32_t reqsize; + +	reqhdr->tid = htons(sdp_gen_tid(ctxt->session)); +	reqsize = ctxt->reqsize + copy_cstate(ctxt->reqbuf + ctxt->reqsize, ctxt->cstate); +	reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t)); + +	while (sent < reqsize) { +		int n = send(ctxt->session->sock, ctxt->reqbuf + sent, reqsize - sent, 0); +		if (n < 0) { +			*err = errno; +			return -1; +		} +		sent += n;  	}  	return 0;  } -static gboolean sdp_client_connection_cb(GIOChannel *chan, GIOCondition cond, struct hci_dbus_data *dbus_data) +static DBusMessage *parse_response(struct transaction_context *ctxt) +{ +	DBusMessage *reply = NULL; +	DBusMessageIter iter; +	DBusMessageIter array_iter; +	int scanned, seqlen; +	uint8_t dataType; +	uint8_t *pdata; +	const char *owner; + +	owner = dbus_message_get_sender(ctxt->rq); + +	reply = dbus_message_new_method_return(ctxt->rq); + +	if ((ctxt->attr_list_len <= 0) || (ctxt->rspbuf.data_size == 0)) +		return dbus_message_new_error(ctxt->rq, ERROR_INTERFACE ".DoesNotExist", +						"Record does not exist"); + +	dbus_message_iter_init_append(reply, &iter); +	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, +			DBUS_TYPE_UINT32_AS_STRING, &array_iter); + +	pdata = ctxt->rspbuf.data; + +	scanned = sdp_extract_seqtype(pdata, &dataType, &seqlen); + +	if (scanned && seqlen) { +		pdata += scanned; +		do { +			uint32_t id; +			int recsize = 0; +			sdp_record_t *rec = sdp_extract_pdu(pdata, &recsize); +			if (rec == NULL) +				break; + +			if (!recsize) { +				sdp_record_free(rec); +				break; +			} + +			scanned += recsize; +			pdata += recsize; + +			id = sdp_cache_append(owner, &ctxt->dba, rec); +			dbus_message_iter_append_basic(&array_iter, +					DBUS_TYPE_UINT32, &id); + +		} while (scanned < ctxt->attr_list_len); +	} + +	dbus_message_iter_close_container(&iter, &array_iter); + +	return reply; +} + +static gboolean svc_search_attr_req_cb(GIOChannel *chan, GIOCondition cond, struct transaction_context *ctxt)  { -	debug("%s, line:%d condition:%d", __PRETTY_FUNCTION__, __LINE__, cond); -	/* FIXME: send the request */ +	int sk, err, n; +	uint32_t rsp_count; +	gboolean ret_val = FALSE; +	socklen_t len; +	uint8_t cstate_len; +	uint8_t *pdata; +	uint8_t *rsp = NULL; +	sdp_pdu_hdr_t *reqhdr = (sdp_pdu_hdr_t *) ctxt->reqbuf; +	sdp_pdu_hdr_t *rsphdr; + +	sk = g_io_channel_unix_get_fd(chan); + +	len = sizeof(err); + +	if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { +		error("getsockopt(): %s, (%d)", strerror(errno), errno); +		goto failed; +	} + +	if (err != 0) { +		error("connect(): %s(%d)", strerror(err), err); +		error_connection_attempt_failed(ctxt->conn, ctxt->rq, err); +		goto failed; +	} + +	rsp = malloc(SDP_RSP_BUFFER_SIZE); +	memset(rsp, 0, SDP_RSP_BUFFER_SIZE); + +	n = recv(sk, rsp, SDP_RSP_BUFFER_SIZE, 0); +	if (n <= 0) { +		err = errno; +		goto failed; +	} + +	rsphdr = (sdp_pdu_hdr_t *)rsp; +	if (n == 0 || reqhdr->tid != rsphdr->tid) { +		err = EPROTO; +		goto failed; +	} + +	pdata = rsp + sizeof(sdp_pdu_hdr_t); + +	if (rsphdr->pdu_id == SDP_ERROR_RSP) { +		uint16_t ecode = ntohs(bt_get_unaligned((uint16_t *) pdata)); +		error("Received SDP error response PDU: %s (%d)", ecode2str(ecode), ecode); +		err = EPROTO; +		goto failed; +	} + +	rsp_count = ntohs(bt_get_unaligned((uint16_t *) pdata)); +	ctxt->attr_list_len += rsp_count; +	pdata += sizeof(uint16_t); + +	// if continuation state set need to re-issue request before parsing +	cstate_len = *(uint8_t *) (pdata + rsp_count); + +	if (rsp_count > 0) { +		uint8_t *targetPtr = NULL; + +		ctxt->cstate = cstate_len > 0 ? (sdp_cstate_t *) (pdata + rsp_count) : 0; + +		// build concatenated response buffer +		ctxt->rspbuf.data = realloc(ctxt->rspbuf.data, ctxt->rspbuf.data_size + rsp_count); +		ctxt->rspbuf.buf_size = ctxt->rspbuf.data_size + rsp_count; +		targetPtr = ctxt->rspbuf.data + ctxt->rspbuf.data_size; +		memcpy(targetPtr, pdata, rsp_count); +		ctxt->rspbuf.data_size += rsp_count; +	} + +	if (ctxt->cstate) { +		if (!sdp_send_req(ctxt, &err)) +			ret_val = TRUE; +	} else { +		/* parse the response PDU */ +		send_reply_and_unref(ctxt->conn, parse_response(ctxt)); +	} +failed: +	if (rsp) +		free(rsp); + +	if (err) { +		error_failed(ctxt->conn, ctxt->rq, err); +		error("SDP transaction error: %s (%d)", strerror(err), err); +	} + +	return ret_val; +} + +static gboolean sdp_client_connect_cb(GIOChannel *chan, GIOCondition cond, struct pending_connect *c) +{ +	int err, sk = 0; +	socklen_t len; +	GIOChannel *tchan; +	struct transaction_context *ctxt; + +	sk = g_io_channel_unix_get_fd(chan); + +	len = sizeof(err); + +	if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { +		error("getsockopt(): %s, (%d)", strerror(errno), errno); +		goto failed; +	} + +	if (err != 0) { +		error("connect(): %s(%d)", strerror(err), err); +		error_connection_attempt_failed(c->conn, c->rq, err); +		goto failed; +	} + +	ctxt = transaction_context_new(c->conn, c->rq, &c->dba, c->svc, sk, 0); + +	if (!ctxt) { +		error_failed(c->conn, c->rq, ENOMEM); +		goto failed; +	} + +	tchan = g_io_channel_unix_new(sk); + +	g_io_add_watch_full(tchan, 0, G_IO_IN, (GIOFunc)svc_search_attr_req_cb, +			ctxt, (GDestroyNotify)transaction_context_free); + +	if (sdp_send_req(ctxt, &err) < 0) { +		error("Can't send PDU: %s (%d)", strerror(err), err); +		error_failed(c->conn, c->rq, err); +		goto failed; +	} +failed: +	pending_connects = slist_remove(pending_connects, c); +	pending_connect_free(c); +  	return FALSE;  } -int dbus_sdp_connect(struct hci_dbus_data *dbus_data, const bdaddr_t *sba, -			const bdaddr_t *dba, uint32_t flags, int *err) +static int search_request(DBusConnection *conn, DBusMessage *msg, uint16_t dev_id, +				const char *dst, const char *svc, int *err)  { +	struct pending_connect *c = NULL; +	GIOChannel *chan = NULL; +	bdaddr_t sba;  	struct sockaddr_l2 sa; -	sdp_session_t *session = malloc(sizeof(sdp_session_t)); -	if (!session) { +	int sk, watch = 0; + +	// create L2CAP connection +	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); +	if (sk < 0) {  		if (err) -			*err = ENOMEM; +			*err = errno; +  		return -1;  	} -	memset(session, 0, sizeof(*session)); -	session->flags = flags; +	chan = g_io_channel_unix_new(sk); -	// create L2CAP connection -	session->sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); -	session->local = 0; -	if (session->sock >= 0) { -		sa.l2_family = AF_BLUETOOTH; -		sa.l2_psm = 0; -		if (bacmp(sba, BDADDR_ANY) != 0) { -			sa.l2_bdaddr = *sba; -			if (bind(session->sock, (struct sockaddr *) &sa, sizeof(sa)) < 0) -				goto fail; -		} -		if (flags & SDP_WAIT_ON_CLOSE) { -			struct linger l = { .l_onoff = 1, .l_linger = 1 }; -			setsockopt(session->sock, SOL_SOCKET, SO_LINGER, &l, sizeof(l)); +	sa.l2_family = AF_BLUETOOTH; +	sa.l2_psm = 0; + +	hci_devba(dev_id, &sba); + +	if (bacmp(&sba, BDADDR_ANY) != 0) { +		bacpy(&sa.l2_bdaddr, &sba); +		if (bind(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0) { +			if (err) +				*err = errno; +			goto fail;  		} -		sa.l2_psm = htobs(SDP_PSM); -		sa.l2_bdaddr = *dba; +	} -		debug("%s, line:%d connecting ...", __PRETTY_FUNCTION__, __LINE__); +	sa.l2_psm = htobs(SDP_PSM); +	str2ba(dst, &sa.l2_bdaddr); -		dbus_data->search->io = g_io_channel_unix_new(session->sock); +	c = pending_connect_new(conn, msg, &sa.l2_bdaddr, svc); +	if (!c) { +		if (err) +			*err = ENOMEM; +		goto fail; +	} -		fcntl(session->sock, F_SETFL, fcntl(session->sock, F_GETFL, 0)|O_NONBLOCK); -		if (connect(session->sock, (struct sockaddr *) &sa, sizeof(sa)) < 0) { -			if ( !(errno == EAGAIN || errno == EINPROGRESS)) { -				error("connect() failed:%s (%d)", strerror(errno), errno); -				goto fail; -			} -			g_io_add_watch(dbus_data->search->io, G_IO_OUT, -					(GIOFunc)sdp_client_connection_cb, dbus_data); -		} else { -			debug("Connect completed in the first attempt"); -			sdp_client_connection_cb(dbus_data->search->io, G_IO_OUT, dbus_data); +	fcntl(sk, F_SETFL, fcntl(sk, F_GETFL, 0)|O_NONBLOCK); +	if (connect(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0) { +		if ( !(errno == EAGAIN || errno == EINPROGRESS)) { +			if (err) +				*err = errno; +			error("connect() failed:%s (%d)", strerror(errno), errno); +			goto fail;  		} -		dbus_data->search->session = session; -		return 0; +		watch = g_io_add_watch(chan, G_IO_OUT, +				(GIOFunc)sdp_client_connect_cb, c); +		pending_connects = slist_append(pending_connects, c); +	} else { +		sdp_client_connect_cb(chan, G_IO_OUT, c);  	} + +	return 0;  fail: -	if (err) -		*err = errno; +	if (chan) +		g_io_channel_close(chan); -	if (session->sock >= 0) -		close(session->sock); -	free(session); -	errno = *err; +	if (c) +		pending_connect_free(c);  	return -1;  } @@ -281,89 +795,73 @@ static DBusHandlerResult get_identifiers(DBusConnection *conn,  {  	char filename[PATH_MAX + 1];  	struct hci_dbus_data *dbus_data = data; -	struct dbus_sdp_record *rec; +	struct service_provider *p;  	struct slist *l; -	const char *peer; +	const char *dst;  	char *str;  	DBusMessage *reply;  	DBusMessageIter iter, array_iter; -	DBusError err; -	bdaddr_t sba, dba; -	uint32_t flags = 0; -	int conn_err, found = 0; +	bdaddr_t dba; +	int err = 0; -	dbus_error_init(&err); - -	dbus_message_get_args(msg, &err, -			DBUS_TYPE_STRING, &peer, -			DBUS_TYPE_INVALID); - -	if (dbus_error_is_set(&err)) { -		error("Can't extract message arguments:%s", err.message); -		dbus_error_free(&err); +	if (!dbus_message_get_args(msg, NULL, +			DBUS_TYPE_STRING, &dst, +			DBUS_TYPE_INVALID))  		return error_invalid_arguments(conn, msg); -	} +	 +	/* FIXME: validate Bluetooth address(dst) */ -	str2ba(peer, &dba); +	str2ba(dst, &dba);  	reply = dbus_message_new_method_return(msg);  	if (!reply) -		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - -	/* check the cache */ -	dbus_message_iter_init_append(reply, &iter); -	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, -						DBUS_TYPE_STRING_AS_STRING, &array_iter); - -	for (l = sdp_records; l; l = l->next) { -		char id_str[9]; -		char *id_ptr = id_str; +		return DBUS_HANDLER_RESULT_NEED_MEMORY; -		rec = l->data; -		if (bacmp(&rec->provider, &dba)) -			continue; -		id2str(rec->identifier, id_ptr); -		dbus_message_iter_append_basic(&array_iter, -					DBUS_TYPE_STRING, &id_ptr); -		found = 1; +	p = service_provider_new(NULL, &dba); +	if (!p) { +		dbus_message_unref(reply); +		return DBUS_HANDLER_RESULT_NEED_MEMORY;  	} - -	dbus_message_iter_close_container(&iter, &array_iter); -	if (found) -		return send_reply_and_unref(conn, reply); +	l = slist_find(sdp_cache, p, (cmp_func_t)service_provider_cmp); +	service_provider_free(p); + +	if (l) { +		struct slist *lr; +		struct service_record *r; + +		/* check the cache */ +		dbus_message_iter_init_append(reply, &iter); +		dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, +				DBUS_TYPE_UINT32_AS_STRING, &array_iter); + +		p = l->data; +		for (lr = p->lrec; lr; lr = lr->next) { +			r = lr->data; +			dbus_message_iter_append_basic(&array_iter, +					DBUS_TYPE_UINT32, &r->identifier); +		} -	dbus_message_unref(reply); +		dbus_message_iter_close_container(&iter, &array_iter); -	if (dbus_data->search) +		return send_reply_and_unref(conn, reply); +	} + +	if (find_pending_connect(&dba))  		return error_service_search_in_progress(conn, msg);  	/* check if it is a unknown address */  	snprintf(filename, PATH_MAX, "%s/%s/lastseen", STORAGEDIR, dbus_data->address); -	str = textfile_get(filename, peer); +	str = textfile_get(filename, dst);  	if (!str)  		return error_unknown_address(conn, msg);  	free(str); -	/* FIXME: if found, when it is invalid/expired? */ -	 -	/* FIXME: check if there is an active connection */ -	 -	/* Check if there is an inquiry/bonding in progress */ - -	/* Background search */ -	dbus_data->search = search_request_info_new(&dba, msg); -	if (!dbus_data->search) -		return DBUS_HANDLER_RESULT_NEED_MEMORY; - -	hci_devba(dbus_data->dev_id, &sba); - -	if (dbus_sdp_connect(dbus_data, &sba, &dba, flags, &conn_err) < 0) { -		search_request_info_free(dbus_data->search); -		dbus_data->search = NULL; -		return error_failed(conn, msg, conn_err); +	if (search_request(conn, msg, dbus_data->dev_id, dst, NULL, &err) < 0) { +		error("Search request failed: %s (%d)", strerror(err), err); +		return error_failed(conn, msg, err);  	}  	return DBUS_HANDLER_RESULT_HANDLED; @@ -375,78 +873,133 @@ static DBusHandlerResult get_identifiers_by_service(DBusConnection *conn,  	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;  } +sdp_record_t *find_record(uint32_t identifier) +{ +	struct slist *lp, *lr; +	struct service_provider *p; +	struct service_record *r; + +	for (lp = sdp_cache; lp; lp = lp->next) { +		p = lp->data; +		for (lr = p->lrec; lr; lr = lr->next) { +			r = lr->data; +			if (r->identifier == identifier) +				return r->record; +		} +	} + +	return NULL; +} +  static DBusHandlerResult get_uuid(DBusConnection *conn,  					 DBusMessage *msg, void *data)  { -	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +	char uuid_str[MAX_LEN_UUID_STR]; +	sdp_list_t *l; +	DBusMessage *reply; +	sdp_record_t *rec; +	char *ptr = uuid_str; +	uint32_t identifier; + +	if (!dbus_message_get_args(msg, NULL, +				DBUS_TYPE_UINT32, &identifier, +				DBUS_TYPE_INVALID)) +		return error_invalid_arguments(conn, msg); + +	rec = find_record(identifier); +	if (!rec) +		return error_record_does_not_exist(conn, msg); + +	memset(uuid_str, 0, MAX_LEN_UUID_STR); + +	reply = dbus_message_new_method_return(msg); + +	if (sdp_get_profile_descs(rec, &l) == 0) { +		sdp_profile_desc_t *desc = (sdp_profile_desc_t *)l->data; + +		sdp_uuid2strn(&desc->uuid, uuid_str, MAX_LEN_UUID_STR); +		sdp_list_free(l, free); +	} + +	dbus_message_append_args(reply, +			DBUS_TYPE_STRING, &ptr, +			DBUS_TYPE_INVALID); + +	return send_reply_and_unref(conn, reply);  }  static DBusHandlerResult get_name(DBusConnection *conn, -					DBusMessage *msg, void *data) +		DBusMessage *msg, void *data)  { -	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} +	DBusMessage *reply; +	sdp_record_t *rec; +	sdp_data_t *d; +	char name[] = ""; +	char *ptr = name; +	uint32_t identifier; + +	if (!dbus_message_get_args(msg, NULL, +				DBUS_TYPE_UINT32, &identifier, +				DBUS_TYPE_INVALID)) +		return error_invalid_arguments(conn, msg); +	rec = find_record(identifier); +	if (!rec) +		return error_record_does_not_exist(conn, msg); + +	reply = dbus_message_new_method_return(msg); +	d = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY); +	if (d && d->val.str) +		ptr = d->val.str; + +	dbus_message_append_args(reply, +			DBUS_TYPE_STRING, &ptr, +			DBUS_TYPE_INVALID); + +	return send_reply_and_unref(conn, reply); +}  static DBusHandlerResult register_rfcomm(DBusConnection *conn, -						DBusMessage *msg, void *data) +		DBusMessage *msg, void *data)  {  	struct hci_dbus_data *dbus_data = data; -	struct dbus_sdp_record *rec, ref; +	struct service_provider ref;  	DBusMessage *reply; -	DBusError err;  	const char *owner, *name; -	bdaddr_t provider; -	char id_str[9]; -	char *id_ptr = id_str; +	bdaddr_t sba; +	uint32_t identifier = 0, handle = 0;  	uint8_t channel;  	owner = dbus_message_get_sender(msg); -	dbus_error_init(&err); -	dbus_message_get_args(msg, &err, +	if (!dbus_message_get_args(msg, NULL,  			DBUS_TYPE_STRING, &name,  			DBUS_TYPE_BYTE, &channel, -			DBUS_TYPE_INVALID); -	 -	if (dbus_error_is_set(&err)) { -		error("Can't extract message arguments:%s", err.message); -		dbus_error_free(&err); +			DBUS_TYPE_INVALID))  		return error_invalid_arguments(conn, msg); -	} -	hci_devba(dbus_data->dev_id, &provider); -	 -	rec = dbus_sdp_record_new(owner, &provider, name, RFCOMM_UUID, channel); -	if (!rec) -		return DBUS_HANDLER_RESULT_NEED_MEMORY; -	 -	if (slist_find(sdp_records, &rec, (cmp_func_t)record_cmp)) { -		dbus_sdp_record_free(rec); -		return error_service_already_exists(conn, msg); -	} +	/* FIXME: register the service */ + +	hci_devba(dbus_data->dev_id, &sba); +	identifier = gen_next_id(&sba, handle); -	id2str(rec->identifier, id_ptr);  	reply = dbus_message_new_method_return(msg); -	dbus_message_append_args(msg, -			DBUS_TYPE_STRING, &id_ptr, -			DBUS_TYPE_INVALID); -	if (!reply) { -		dbus_sdp_record_free(rec); +	if (!reply)  		return DBUS_HANDLER_RESULT_NEED_MEMORY; -	} -	/* FIXME: register the service */ +	dbus_message_append_args(msg, +			DBUS_TYPE_UINT32, &identifier, +			DBUS_TYPE_INVALID);  	/* Only add a D-Bus unique name listener if there isn't one already registered */  	memset(&ref, 0, sizeof(ref)); -	bacpy(&ref.provider, BDADDR_ANY); - -	if (!slist_find(sdp_records, &ref, (cmp_func_t)record_cmp)) -		name_listener_add(conn, rec->owner, (name_cb_t)owner_exited, dbus_data); +	bacpy(&ref.prov, BDADDR_ANY); -	sdp_records = slist_append(sdp_records, rec); +	if (!slist_find(sdp_cache, &ref, (cmp_func_t)service_provider_cmp)) +		name_listener_add(conn, owner, (name_cb_t)owner_exited, dbus_data); +	/* FIXME: register the RFCOMM service */ +	  	return send_reply_and_unref(conn, reply);  } @@ -454,38 +1007,31 @@ static DBusHandlerResult unregister_rfcomm(DBusConnection *conn,  						DBusMessage *msg, void *data)  {  	struct hci_dbus_data *dbus_data = data; -	struct dbus_sdp_record *rec, ref; +	struct service_provider *p, ref;  	struct slist *match;  	DBusMessage *reply; -	DBusError err;  	const char *owner, *identifier;  	owner = dbus_message_get_sender(msg); -	dbus_error_init(&err); -	dbus_message_get_args(msg, &err, +	if (!dbus_message_get_args(msg, NULL,  			DBUS_TYPE_STRING, &identifier, -			DBUS_TYPE_INVALID); -	 -	if (dbus_error_is_set(&err)) { -		error("Can't extract message arguments:%s", err.message); -		dbus_error_free(&err); +			DBUS_TYPE_INVALID))  		return error_invalid_arguments(conn, msg); -	}  	memset(&ref, 0, sizeof(ref)); -	ref.uuid = RFCOMM_UUID; -	ref.identifier = str2id(identifier); -	hci_devba(dbus_data->dev_id, &ref.provider); +	hci_devba(dbus_data->dev_id, &ref.prov); +	ref.owner = (char *) owner; -	match = slist_find(sdp_records, &ref, (cmp_func_t)record_cmp); +	match = slist_find(sdp_cache, &ref, (cmp_func_t)service_provider_cmp);  	if (!match)  		return error_service_does_not_exist(conn, msg); -	rec = match->data; +	/* FIXME: find the RFCOMM UUID in the list */ +	p = match->data; -	if (strcmp(rec->owner, owner)) +	if (strcmp(p->owner, owner))  		return error_not_authorized(conn, msg);  	reply = dbus_message_new_method_return(msg); @@ -494,15 +1040,14 @@ static DBusHandlerResult unregister_rfcomm(DBusConnection *conn,  	/* FIXME: unregister the service */ -	sdp_records = slist_remove(sdp_records, rec); -	dbus_sdp_record_free(rec); +	sdp_cache = slist_remove(sdp_cache, p); +	service_provider_free(p); -	bacpy(&ref.provider, BDADDR_ANY); -	ref.uuid = 0x0000; +	bacpy(&ref.prov, BDADDR_ANY);  	/* Only remove the D-Bus unique name listener if there are no more record using this name */ -	if (!slist_find(sdp_records, &ref, (cmp_func_t)record_cmp)) -		name_listener_remove(conn, ref.name, (name_cb_t)owner_exited, dbus_data); +	if (!slist_find(sdp_cache, &ref, (cmp_func_t)service_provider_cmp)) +		name_listener_remove(conn, owner, (name_cb_t)owner_exited, dbus_data);  	return send_reply_and_unref(conn, reply);  } diff --git a/hcid/dbus.h b/hcid/dbus.h index 2c33cae9..9539b922 100644 --- a/hcid/dbus.h +++ b/hcid/dbus.h @@ -86,13 +86,6 @@ struct bonding_request_info {  	int disconnect; /* disconnect after finish */  }; -struct search_request_info { -	bdaddr_t bdaddr; -	DBusMessage *rq; -	GIOChannel *io; -	sdp_session_t *session; -}; -  struct active_conn_info {  	bdaddr_t bdaddr;  	uint16_t handle; @@ -110,7 +103,6 @@ struct hci_dbus_data {  	char *requestor_name;	           /* requestor unique name */  	struct slist *passkey_agents;  	struct bonding_request_info *bonding; -	struct search_request_info *search;  	struct slist *active_conn;  	int pairing_active;  }; | 
