diff options
Diffstat (limited to 'hcid')
-rw-r--r-- | hcid/dbus-adapter.c | 13 | ||||
-rw-r--r-- | hcid/dbus-sdp.c | 457 | ||||
-rw-r--r-- | hcid/dbus.h | 2 |
3 files changed, 414 insertions, 58 deletions
diff --git a/hcid/dbus-adapter.c b/hcid/dbus-adapter.c index 94d2c9ba..4dcd629f 100644 --- a/hcid/dbus-adapter.c +++ b/hcid/dbus-adapter.c @@ -986,6 +986,16 @@ static DBusHandlerResult handle_dev_set_name_req(DBusConnection *conn, DBusMessa return send_reply_and_unref(conn, reply); } +static DBusHandlerResult handle_dev_get_remote_svc_rec(DBusConnection *conn, DBusMessage *msg, void *data) +{ + return get_remote_svc_rec(conn, msg, data); +} + +static DBusHandlerResult handle_dev_get_remote_svc_handles(DBusConnection *conn, DBusMessage *msg, void *data) +{ + return get_remote_svc_handles(conn, msg, data); +} + static DBusHandlerResult handle_dev_get_remote_version_req(DBusConnection *conn, DBusMessage *msg, void *data) { struct hci_dbus_data *dbus_data = data; @@ -2404,6 +2414,9 @@ static struct service_data dev_services[] = { { "GetName", handle_dev_get_name_req }, { "SetName", handle_dev_set_name_req }, + { "GetRemoteServiceRecord", handle_dev_get_remote_svc_rec }, + { "GetRemoteServiceHandles", handle_dev_get_remote_svc_handles }, + { "GetRemoteVersion", handle_dev_get_remote_version_req }, { "GetRemoteRevision", handle_dev_get_remote_revision_req }, { "GetRemoteManufacturer", handle_dev_get_remote_manufacturer_req }, diff --git a/hcid/dbus-sdp.c b/hcid/dbus-sdp.c index 8e6b3ac9..e0ad16d4 100644 --- a/hcid/dbus-sdp.c +++ b/hcid/dbus-sdp.c @@ -52,6 +52,10 @@ #define MAX_IDENTIFIER_LEN 29 /* "XX:XX:XX:XX:XX:XX/0xYYYYYYYY\0" */ +/* FIXME: debug purpose */ +#define SERVICE_SEARCH_ATTR_REQ 1 + + struct service_provider { char *owner; /* null for remote services or unique name if local */ char *prov; /* remote Bluetooth address that provides the service */ @@ -63,18 +67,21 @@ struct service_record { sdp_record_t *record; }; -struct pending_connect { +struct transaction_context { DBusConnection *conn; DBusMessage *rq; - char *svc; - char *dst; + sdp_session_t *session; }; -struct transaction_context { + +typedef int connect_cb_t (struct transaction_context *t); +struct pending_connect { DBusConnection *conn; DBusMessage *rq; - sdp_session_t *session; + char *dst; + connect_cb_t *conn_cb; }; + /* FIXME: move to a common file */ typedef struct { char *name; @@ -149,7 +156,7 @@ static struct slist *sdp_cache = NULL; static struct slist *pending_connects = NULL; static struct pending_connect *pending_connect_new(DBusConnection *conn, DBusMessage *msg, - const char *dst, const char *svc) + const char *dst, connect_cb_t *cb) { struct pending_connect *c; @@ -160,11 +167,6 @@ static struct pending_connect *pending_connect_new(DBusConnection *conn, DBusMes memset(c, 0, sizeof(*c)); - if (svc) { - c->svc = strdup(svc); - if (!c->svc) - goto fail; - } if (dst) { c->dst = strdup(dst); if (!c->dst) @@ -173,12 +175,11 @@ static struct pending_connect *pending_connect_new(DBusConnection *conn, DBusMes c->conn = dbus_connection_ref(conn); c->rq = dbus_message_ref(msg); + c->conn_cb = cb; return c; fail: - if (c->svc) - free(c->svc); if (c->dst) free(c->dst); free(c); @@ -191,9 +192,6 @@ static void pending_connect_free(struct pending_connect *c) if (!c) return; - if (c->svc) - free(c->svc); - if (c->rq) dbus_message_unref(c->rq); @@ -474,6 +472,136 @@ fail: return retval; } +static void remote_svc_rec_completed_cb(uint8_t type, uint16_t err, uint8_t *rsp, size_t size, void *udata) +{ + struct transaction_context *ctxt = udata; + char identifier[MAX_IDENTIFIER_LEN]; + sdp_record_t *rec = NULL; + DBusMessage *reply; + DBusMessageIter iter, array_iter; + const char *dst; + int i, scanned; + + if (!ctxt) + return; + + if (type == SDP_ERROR_RSP) { + error_sdp_failed(ctxt->conn, ctxt->rq, err); + return; + } + + /* check response PDU ID */ + if (type != SDP_SVC_ATTR_RSP) { + error("SDP error: %s(%d)", strerror(EPROTO), EPROTO); + error_failed(ctxt->conn, ctxt->rq, EPROTO); + return; + } + + dbus_message_get_args(ctxt->rq, NULL, + DBUS_TYPE_STRING, &dst, + DBUS_TYPE_INVALID); + + reply = dbus_message_new_method_return(ctxt->rq); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, &array_iter); + + /* + * FIXME: check rsp? How check for I/O error or wrong transaction id? + * check the type value(Zero) is not reasonable! + */ + + rec = sdp_extract_pdu(rsp, &scanned); + if (rec == NULL) { + error("SVC REC is null"); + goto done; + } + + sdp_cache_append(NULL, dst, rec); + snprintf(identifier, MAX_IDENTIFIER_LEN, "%s/0x%x", dst, rec->handle); + + /* FIXME: avoid seg fault / out of bound */ + for (i = 0; i < size; i++) + dbus_message_iter_append_basic(&array_iter, + DBUS_TYPE_BYTE, &rsp[i]); + +done: + dbus_message_iter_close_container(&iter, &array_iter); + send_reply_and_unref(ctxt->conn, reply); +} + +static void remote_svc_handles_completed_cb(uint8_t type, uint16_t err, uint8_t *rsp, size_t size, void *udata) +{ + struct transaction_context *ctxt = udata; + char identifier[MAX_IDENTIFIER_LEN]; + DBusMessage *reply; + DBusMessageIter iter, array_iter; + const char *dst; + uint8_t *pdata; + uint8_t dataType = 0; + int scanned, seqlen = 0; + + if (!ctxt) + return; + + if (type == SDP_ERROR_RSP) { + error_sdp_failed(ctxt->conn, ctxt->rq, err); + return; + } + + /* check response PDU ID */ + if (type != SDP_SVC_SEARCH_ATTR_RSP) { + error("SDP error: %s(%d)", strerror(EPROTO), EPROTO); + error_failed(ctxt->conn, ctxt->rq, EPROTO); + return; + } + + dbus_message_get_args(ctxt->rq, NULL, + DBUS_TYPE_STRING, &dst, + DBUS_TYPE_INVALID); + + reply = dbus_message_new_method_return(ctxt->rq); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_UINT32_AS_STRING, &array_iter); + + /* + * FIXME: check rsp? How check for I/O error or wrong transaction id? + * check the type value(Zero) is not reasonable! + */ + pdata = rsp; + scanned = sdp_extract_seqtype(pdata, &dataType, &seqlen); + + if (!scanned || !seqlen) + goto done; + + pdata += scanned; + do { + int recsize = 0; + sdp_record_t *rec = sdp_extract_pdu(pdata, &recsize); + if (rec == NULL) { + error("SVC REC is null"); + goto done; + } + if (!recsize) { + sdp_record_free(rec); + break; + } + + scanned += recsize; + pdata += recsize; + + sdp_cache_append(NULL, dst, rec); + snprintf(identifier, MAX_IDENTIFIER_LEN, "%s/0x%x", dst, rec->handle); + dbus_message_iter_append_basic(&array_iter, + DBUS_TYPE_UINT32, &rec->handle); + } while (scanned < size); + +done: + dbus_message_iter_close_container(&iter, &array_iter); + send_reply_and_unref(ctxt->conn, reply); +} + static void search_completed_cb(uint8_t type, uint16_t err, uint8_t *rsp, size_t size, void *udata) { struct transaction_context *ctxt = udata; @@ -483,8 +611,13 @@ static void search_completed_cb(uint8_t type, uint16_t err, uint8_t *rsp, size_t DBusMessageIter iter, array_iter; const char *dst; uint8_t *pdata; + int scanned = 0; +#ifdef SERVICE_SEARCH_ATTR_REQ uint8_t dataType = 0; - int scanned, seqlen = 0; + int seqlen = 0; +#else + int csrc, tsrc; +#endif if (!ctxt) return; @@ -495,7 +628,11 @@ static void search_completed_cb(uint8_t type, uint16_t err, uint8_t *rsp, size_t } /* check response PDU ID */ +#ifdef SERVICE_SEARCH_ATTR_REQ if (type != SDP_SVC_SEARCH_ATTR_RSP) { +#else + if (type != SDP_SVC_SEARCH_RSP) { +#endif error("SDP error: %s(%d)", strerror(EPROTO), EPROTO); error_failed(ctxt->conn, ctxt->rq, EPROTO); return; @@ -515,12 +652,31 @@ static void search_completed_cb(uint8_t type, uint16_t err, uint8_t *rsp, size_t * check the type value(Zero) is not reasonable! */ pdata = rsp; + +#ifdef SERVICE_SEARCH_ATTR_REQ scanned = sdp_extract_seqtype(pdata, &dataType, &seqlen); if (!scanned || !seqlen) goto done; pdata += scanned; +#else + // net service record match count + tsrc = ntohs(bt_get_unaligned((uint16_t *) pdata)); + pdata += sizeof(uint16_t); + scanned += sizeof(uint16_t); + csrc = ntohs(bt_get_unaligned((uint16_t *) pdata)); + pdata += sizeof(uint16_t); + scanned += sizeof(uint16_t); + + debug("Total svc count: %d\n", tsrc); + debug("Current svc count: %d\n", csrc); + + if (!csrc) + goto done; +#endif + +#ifdef SERVICE_SEARCH_ATTR_REQ do { int recsize = 0; sdp_record_t *rec = sdp_extract_pdu(pdata, &recsize); @@ -541,7 +697,19 @@ static void search_completed_cb(uint8_t type, uint16_t err, uint8_t *rsp, size_t dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &ptr); } while (scanned < size); +#else + do { + uint32_t handle = ntohl(bt_get_unaligned((uint32_t*)pdata)); + scanned+= sizeof(uint32_t); + pdata+=sizeof(uint32_t); + + snprintf(identifier, MAX_IDENTIFIER_LEN, "%s/0x%x", dst, handle); + + dbus_message_iter_append_basic(&array_iter, + DBUS_TYPE_STRING, &ptr); + } while (--tsrc); +#endif done: dbus_message_iter_close_container(&iter, &array_iter); send_reply_and_unref(ctxt->conn, reply); @@ -570,12 +738,8 @@ static gboolean sdp_client_connect_cb(GIOChannel *chan, GIOCondition cond, void { struct pending_connect *c = udata; struct transaction_context *ctxt = NULL; - sdp_list_t *search = NULL, *attrids = NULL; - uint32_t range = 0x0000ffff; - uint16_t class; int err = 0, sk = 0; socklen_t len; - uuid_t uuid; sk = g_io_channel_unix_get_fd(chan); @@ -583,51 +747,31 @@ static gboolean sdp_client_connect_cb(GIOChannel *chan, GIOCondition cond, void if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { error("getsockopt(): %s, (%d)", strerror(errno), errno); - error_connection_attempt_failed(c->conn, c->rq, errno); + err = errno; goto fail; } if (err != 0) { error("connect(): %s(%d)", strerror(err), err); - error_connection_attempt_failed(c->conn, c->rq, err); goto fail; } ctxt = malloc(sizeof(*ctxt)); if (!ctxt) { - error_failed(c->conn, c->rq, ENOMEM); + err = ENOMEM; goto fail; } - memset(ctxt, 0, sizeof(*ctxt)); - /* FIXME: the sdp search list pattern should be created outside */ - if (!c->svc) { - sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); - search = sdp_list_append(0, &uuid); - } else { - class = sdp_str2svclass(c->svc); - sdp_uuid16_create(&uuid, class); - search = sdp_list_append(0, &uuid); - } + memset(ctxt, 0, sizeof(*ctxt)); ctxt->conn = dbus_connection_ref(c->conn); ctxt->rq = dbus_message_ref(c->rq); ctxt->session = sdp_create(sk, 0); - if (!ctxt->session) { - error("error: %s (%d)", strerror(errno), errno); - error_failed(c->conn, c->rq, errno); - goto fail; - } - - sdp_set_notify(ctxt->session, search_completed_cb, ctxt); - attrids = sdp_list_append(NULL, &range); - /* Create/send the search request and set the callback to indicate the request completion */ - if (sdp_service_search_attr_async(ctxt->session, search, SDP_ATTR_REQ_RANGE, attrids) < 0) { - error("send request failed: %s (%d)", strerror(errno), errno); - error_failed(c->conn, c->rq, errno); + /* set the complete transaction callback and starts the search request */ + err = c->conn_cb(ctxt); + if (err) goto fail; - } /* set the callback responsible for update the transaction data */ g_io_add_watch_full(chan, 0, G_IO_IN, search_process_cb, @@ -635,23 +779,20 @@ static gboolean sdp_client_connect_cb(GIOChannel *chan, GIOCondition cond, void goto done; fail: + if (err) + error_connection_attempt_failed(c->conn, c->rq, err); + if (ctxt) transaction_context_free(ctxt); done: - if (search) - sdp_list_free(search, NULL); - - if (attrids) - sdp_list_free(attrids, NULL); - pending_connects = slist_remove(pending_connects, c); pending_connect_free(c); return FALSE; } -static int search_request(DBusConnection *conn, DBusMessage *msg, uint16_t dev_id, - const char *dst, const char *svc, int *err) +static int connect_request(DBusConnection *conn, DBusMessage *msg, uint16_t dev_id, + const char *dst, connect_cb_t *cb, int *err) { struct pending_connect *c = NULL; GIOChannel *chan = NULL; @@ -685,7 +826,7 @@ static int search_request(DBusConnection *conn, DBusMessage *msg, uint16_t dev_i sa.l2_psm = htobs(SDP_PSM); str2ba(dst, &sa.l2_bdaddr); - c = pending_connect_new(conn, msg, dst, svc); + c = pending_connect_new(conn, msg, dst, cb); if (!c) { if (err) *err = ENOMEM; @@ -719,6 +860,158 @@ fail: return -1; } +static int remote_svc_rec_conn_cb(struct transaction_context *ctxt) +{ + sdp_list_t *attrids = NULL; + uint32_t range = 0x0000ffff; + const char *dst; + uint32_t handle; + int err = 0; + + if (sdp_set_notify(ctxt->session, remote_svc_rec_completed_cb, ctxt) < 0) { + error("Invalid session data!"); + err = EINVAL; + goto fail; + } + + dbus_message_get_args(ctxt->rq, NULL, + DBUS_TYPE_STRING, &dst, + DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_INVALID); + + attrids = sdp_list_append(NULL, &range); + /* Create/send the search request and set the callback to indicate the request completion */ + if (sdp_service_attr_async(ctxt->session, handle, SDP_ATTR_REQ_RANGE, attrids) < 0) { + error("send request failed: %s (%d)", strerror(errno), errno); + err = errno; + goto fail; + } + +fail: + if (attrids) + sdp_list_free(attrids, NULL); + + return err; +} + +DBusHandlerResult get_remote_svc_rec(DBusConnection *conn, DBusMessage *msg, void *data) +{ + struct hci_dbus_data *dbus_data = data; + const char *dst; + uint32_t handle; + int err = 0; + + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &dst, + DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_INVALID)) + return error_invalid_arguments(conn, msg); + + if (find_pending_connect(dst)) + return error_service_search_in_progress(conn, msg); + + if (connect_request(conn, msg, dbus_data->dev_id, dst, remote_svc_rec_conn_cb, &err) < 0) { + error("Search request failed: %s (%d)", strerror(err), err); + return error_failed(conn, msg, err); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static int remote_svc_handles_conn_cb(struct transaction_context *ctxt) +{ + sdp_list_t *search = NULL, *attrids = NULL; + uuid_t uuid; + uint32_t range = 0x0000ffff; + int err = 0; + + if (sdp_set_notify(ctxt->session, remote_svc_handles_completed_cb, ctxt) < 0) { + error("Invalid session data!"); + err = EINVAL; + goto fail; + } + + sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); + search = sdp_list_append(0, &uuid); + + attrids = sdp_list_append(NULL, &range); + /* Create/send the search request and set the callback to indicate the request completion */ + if (sdp_service_search_attr_async(ctxt->session, search, SDP_ATTR_REQ_RANGE, attrids) < 0) { + error("send request failed: %s (%d)", strerror(errno), errno); + err = errno; + goto fail; + } + +fail: + if (search) + sdp_list_free(search, NULL); + + if (attrids) + sdp_list_free(attrids, NULL); + + return err; +} + +DBusHandlerResult get_remote_svc_handles(DBusConnection *conn, DBusMessage *msg, void *data) +{ + struct hci_dbus_data *dbus_data = data; + const char *dst; + int err = 0; + + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &dst, + DBUS_TYPE_INVALID)) + return error_invalid_arguments(conn, msg); + + if (find_pending_connect(dst)) + return error_service_search_in_progress(conn, msg); + + if (connect_request(conn, msg, dbus_data->dev_id, dst, remote_svc_handles_conn_cb, &err) < 0) { + error("Search request failed: %s (%d)", strerror(err), err); + return error_failed(conn, msg, err); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static int get_identifiers_conn_cb(struct transaction_context *ctxt) +{ + sdp_list_t *search = NULL, *attrids = NULL; + uuid_t uuid; + uint32_t range = 0x0000ffff; + int err = 0; + + if (sdp_set_notify(ctxt->session, search_completed_cb, ctxt) < 0) { + error("Invalid session data!"); + err = -EINVAL; + goto fail; + } + + sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); + search = sdp_list_append(0, &uuid); + + attrids = sdp_list_append(NULL, &range); + /* Create/send the search request and set the callback to indicate the request completion */ +#ifdef SERVICE_SEARCH_ATTR_REQ + if (sdp_service_search_attr_async(ctxt->session, search, SDP_ATTR_REQ_RANGE, attrids) < 0) { +#else + if (sdp_service_search_async(ctxt->session, search, 64) < 0) { +#endif + error("send request failed: %s (%d)", strerror(errno), errno); + err = -errno; + goto fail; + } + +fail: + if (search) + sdp_list_free(search, NULL); + + if (attrids) + sdp_list_free(attrids, NULL); + + return err; +} + static DBusHandlerResult get_identifiers(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -731,10 +1024,11 @@ static DBusHandlerResult get_identifiers(DBusConnection *conn, DBUS_TYPE_INVALID)) return error_invalid_arguments(conn, msg); + /* in progress is not working properly */ if (find_pending_connect(dst)) return error_service_search_in_progress(conn, msg); - if (search_request(conn, msg, dbus_data->dev_id, dst, NULL, &err) < 0) { + if (connect_request(conn, msg, dbus_data->dev_id, dst, get_identifiers_conn_cb, &err) < 0) { error("Search request failed: %s (%d)", strerror(err), err); return error_failed(conn, msg, err); } @@ -742,6 +1036,52 @@ static DBusHandlerResult get_identifiers(DBusConnection *conn, return DBUS_HANDLER_RESULT_HANDLED; } +static int get_identifiers_by_service_conn_cb(struct transaction_context *ctxt) +{ + sdp_list_t *search = NULL, *attrids = NULL; + uint32_t range = 0x0000ffff; + const char *dst, *svc; + uuid_t uuid; + uint16_t class; + int err = 0; + + if (sdp_set_notify(ctxt->session, search_completed_cb, ctxt) < 0) { + error("Invalid session data!"); + err = -EINVAL; + goto fail; + } + + dbus_message_get_args(ctxt->rq, NULL, + DBUS_TYPE_STRING, &dst, + DBUS_TYPE_STRING, &svc, + DBUS_TYPE_INVALID); + + class = sdp_str2svclass(svc); + sdp_uuid16_create(&uuid, class); + search = sdp_list_append(0, &uuid); + + attrids = sdp_list_append(NULL, &range); + /* Create/send the search request and set the callback to indicate the request completion */ +#ifdef SERVICE_SEARCH_ATTR_REQ + if (sdp_service_search_attr_async(ctxt->session, search, SDP_ATTR_REQ_RANGE, attrids) < 0) { +#else + if (sdp_service_search_async(ctxt->session, search, 64) < 0) { +#endif + error("send request failed: %s (%d)", strerror(errno), errno); + err = -errno; + goto fail; + } + +fail: + if (search) + sdp_list_free(search, NULL); + + if (attrids) + sdp_list_free(attrids, NULL); + + return err; +} + static DBusHandlerResult get_identifiers_by_service(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -823,7 +1163,8 @@ search_request: if (find_pending_connect(dst)) return error_service_search_in_progress(conn, msg); - if (search_request(conn, msg, dbus_data->dev_id, dst, svc, &err) < 0) { + if (connect_request(conn, msg, dbus_data->dev_id, dst, + get_identifiers_by_service_conn_cb, &err) < 0) { error("Search request failed: %s (%d)", strerror(err), err); return error_failed(conn, msg, err); } diff --git a/hcid/dbus.h b/hcid/dbus.h index 7a446caf..4dc0ff51 100644 --- a/hcid/dbus.h +++ b/hcid/dbus.h @@ -196,6 +196,8 @@ DBusHandlerResult handle_security_method(DBusConnection *conn, DBusMessage *msg, DBusHandlerResult handle_rfcomm_method(DBusConnection *conn, DBusMessage *msg, void *data); DBusHandlerResult handle_sdp_method(DBusConnection *conn, DBusMessage *msg, void *data); +DBusHandlerResult get_remote_svc_handles(DBusConnection *conn, DBusMessage *msg, void *data); +DBusHandlerResult get_remote_svc_rec(DBusConnection *conn, DBusMessage *msg, void *data); DBusHandlerResult simple_introspect(DBusConnection *conn, DBusMessage *msg, void *data); |