summaryrefslogtreecommitdiffstats
path: root/hcid/dbus-sdp.c
diff options
context:
space:
mode:
authorClaudio Takahasi <claudio.takahasi@openbossa.org>2006-05-05 17:30:02 +0000
committerClaudio Takahasi <claudio.takahasi@openbossa.org>2006-05-05 17:30:02 +0000
commit7f1664522f8f9feb612f9f0c48cd6212cf906d45 (patch)
treecd990c2a75668365e523977d1ef34db318a81c7d /hcid/dbus-sdp.c
parentf72f6d99edab2ebab198ea0d7c7dbd4721362e25 (diff)
Added GetIdentifiers, GetUUID and GetName.
Diffstat (limited to 'hcid/dbus-sdp.c')
-rw-r--r--hcid/dbus-sdp.c1057
1 files changed, 801 insertions, 256 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);
}