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 /hcid/dbus-sdp.c | |
parent | f72f6d99edab2ebab198ea0d7c7dbd4721362e25 (diff) |
Added GetIdentifiers, GetUUID and GetName.
Diffstat (limited to 'hcid/dbus-sdp.c')
-rw-r--r-- | hcid/dbus-sdp.c | 1057 |
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); } |