diff options
Diffstat (limited to 'hcid/security.c')
-rw-r--r-- | hcid/security.c | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/hcid/security.c b/hcid/security.c index 8946b1c5..d2d7ac1e 100644 --- a/hcid/security.c +++ b/hcid/security.c @@ -49,6 +49,8 @@ #include "hcid.h" #include "lib.h" +#include "textfile.h" +#include "list.h" struct g_io_info { GIOChannel *channel; @@ -60,6 +62,78 @@ static struct g_io_info io_data[HCI_MAX_DEV]; static int pairing = HCID_PAIRING_MULTI; +struct hci_req_data { + int dev; + uint16_t ogf; + uint16_t ocf; + void *cparam; + int clen; +}; + +static struct slist *hci_req_queue = NULL; + +static struct hci_req_data *hci_req_data_new(int dev, uint16_t ogf, uint16_t ocf, const void *cparam, int clen) +{ + struct hci_req_data *data; + + data = malloc(sizeof(*data)); + if (!data) + return NULL; + + memset(data, 0, sizeof(*data)); + + data->cparam = malloc(clen); + if (!data->cparam) { + free(data); + return NULL; + } + + memcpy(data->cparam, cparam, clen); + + data->dev = dev; + data->ogf = ogf; + data->ocf = ocf; + data->clen = clen; + + return data; +} + +static int hci_req_find_by_dev(const void *data, const void *user_data) +{ + const struct hci_req_data *req = data; + const int *dev = user_data; + + if (req->dev == *dev) + return 0; + + return -1; +} + +static int check_pending_hci_req(int dev) +{ + struct slist *l; + + if (!hci_req_queue) + return -1; + + l = slist_find(hci_req_queue, &dev, hci_req_find_by_dev); + + if (l) { + struct hci_req_data *data = l->data; + + hci_send_cmd(dev, data->ogf, data->ocf, data->clen, data->cparam); + + hci_req_queue = slist_remove(hci_req_queue, data); + + free(data->cparam); + free(data); + + return 0; + } + + return -1; +} + static inline int get_bdaddr(int dev, bdaddr_t *sba, uint16_t handle, bdaddr_t *dba) { struct hci_conn_list_req *cl; @@ -339,6 +413,9 @@ static inline void remote_name_information(int dev, bdaddr_t *sba, void *ptr) } hcid_dbus_remote_name(sba, &dba, evt->status, name); + + /* pending remote version or remote features */ + check_pending_hci_req(dev); } static inline void remote_version_information(int dev, bdaddr_t *sba, void *ptr) @@ -354,6 +431,9 @@ static inline void remote_version_information(int dev, bdaddr_t *sba, void *ptr) write_version_info(sba, &dba, btohs(evt->manufacturer), evt->lmp_ver, btohs(evt->lmp_subver)); + + /* pending remote features */ + check_pending_hci_req(dev); } static inline void inquiry_complete(int dev, bdaddr_t *sba, void *ptr) @@ -467,6 +547,9 @@ static inline void name_resolve(int dev, bdaddr_t *bdaddr) static inline void conn_complete(int dev, bdaddr_t *sba, void *ptr) { evt_conn_complete *evt = ptr; + char filename[PATH_MAX]; + bdaddr_t tmp; + char *str, *local_addr, *peer_addr; hcid_dbus_conn_complete(sba, evt->status, evt->handle, &evt->bdaddr); @@ -476,6 +559,53 @@ static inline void conn_complete(int dev, bdaddr_t *sba, void *ptr) update_lastused(sba, &evt->bdaddr); name_resolve(dev, &evt->bdaddr); + + /* check if the remote version needs be requested */ + baswap(&tmp, sba); local_addr = batostr(&tmp); + baswap(&tmp, &evt->bdaddr); peer_addr = batostr(&tmp); + + snprintf(filename, sizeof(filename), "%s/%s/manufacturers", + STORAGEDIR, local_addr); + + str = textfile_get(filename, peer_addr); + if (!str) { + struct hci_req_data *data; + read_remote_features_cp cp; + + cp.handle = evt->handle; + + data = hci_req_data_new(dev, OGF_LINK_CTL, OCF_READ_REMOTE_VERSION, + &cp, READ_REMOTE_VERSION_CP_SIZE); + + hci_req_queue = slist_append(hci_req_queue, data); + } else { + /* skip: remote version found */ + free(str); + } + + /* check if the remote features needs be requested */ + snprintf(filename, sizeof(filename), "%s/%s/features", + STORAGEDIR, local_addr); + + str = textfile_get(filename, peer_addr); + if (!str) { + struct hci_req_data *data; + read_remote_features_cp cp; + + cp.handle = evt->handle; + + data = hci_req_data_new(dev, OGF_LINK_CTL, OCF_READ_REMOTE_FEATURES, + &cp, READ_REMOTE_FEATURES_CP_SIZE); + + hci_req_queue = slist_append(hci_req_queue, data); + } else { + /* skip: remote features found */ + free(str); + } + + free(local_addr); + free(peer_addr); + } static inline void disconn_complete(int dev, bdaddr_t *sba, void *ptr) |