// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "src/shared/mainloop.h" #include "btdev.h" #include "server.h" #define uninitialized_var(x) x = x struct server { enum server_type type; uint16_t id; int fd; }; struct client { int fd; struct btdev *btdev; uint8_t *pkt_data; uint8_t pkt_type; uint16_t pkt_expect; uint16_t pkt_len; uint16_t pkt_offset; }; static void server_destroy(void *user_data) { struct server *server = user_data; close(server->fd); free(server); } static void client_destroy(void *user_data) { struct client *client = user_data; btdev_destroy(client->btdev); close(client->fd); free(client); } static void client_write_callback(const struct iovec *iov, int iovlen, void *user_data) { struct client *client = user_data; struct msghdr msg; ssize_t written; memset(&msg, 0, sizeof(msg)); msg.msg_iov = (struct iovec *) iov; msg.msg_iovlen = iovlen; written = sendmsg(client->fd, &msg, MSG_DONTWAIT); if (written < 0) return; } static void client_read_callback(int fd, uint32_t events, void *user_data) { struct client *client = user_data; static uint8_t buf[4096]; uint8_t *ptr = buf; ssize_t len; uint16_t count; if (events & (EPOLLERR | EPOLLHUP)) { mainloop_remove_fd(client->fd); return; } again: len = recv(fd, buf + client->pkt_offset, sizeof(buf) - client->pkt_offset, MSG_DONTWAIT); if (len < 0) { if (errno == EAGAIN) goto again; return; } if (!client->btdev) return; count = client->pkt_offset + len; while (count > 0) { hci_command_hdr *cmd_hdr; hci_acl_hdr *acl_hdr; if (!client->pkt_data) { client->pkt_type = ptr[0]; switch (client->pkt_type) { case HCI_COMMAND_PKT: if (count < HCI_COMMAND_HDR_SIZE + 1) { client->pkt_offset += len; return; } cmd_hdr = (hci_command_hdr *) (ptr + 1); client->pkt_expect = HCI_COMMAND_HDR_SIZE + cmd_hdr->plen + 1; client->pkt_data = malloc(client->pkt_expect); client->pkt_len = 0; break; case HCI_ACLDATA_PKT: acl_hdr = (hci_acl_hdr*)(ptr + 1); client->pkt_expect = HCI_ACL_HDR_SIZE + acl_hdr->dlen + 1; client->pkt_data = malloc(client->pkt_expect); client->pkt_len = 0; break; default: printf("packet error\n"); return; } client->pkt_offset = 0; } if (count >= client->pkt_expect) { memcpy(client->pkt_data + client->pkt_len, ptr, client->pkt_expect); ptr += client->pkt_expect; count -= client->pkt_expect; btdev_receive_h4(client->btdev, client->pkt_data, client->pkt_len + client->pkt_expect); free(client->pkt_data); client->pkt_data = NULL; } else { memcpy(client->pkt_data + client->pkt_len, ptr, count); client->pkt_len += count; client->pkt_expect -= count; count = 0; } } } static int accept_client(int fd) { struct sockaddr_un addr; socklen_t len; int nfd; memset(&addr, 0, sizeof(addr)); len = sizeof(addr); if (getsockname(fd, (struct sockaddr *) &addr, &len) < 0) { perror("Failed to get socket name"); return -1; } printf("Request for %s\n", addr.sun_path); nfd = accept(fd, (struct sockaddr *) &addr, &len); if (nfd < 0) { perror("Failed to accept client socket"); return -1; } return nfd; } static void server_accept_callback(int fd, uint32_t events, void *user_data) { struct server *server = user_data; struct client *client; enum btdev_type uninitialized_var(type); if (events & (EPOLLERR | EPOLLHUP)) { mainloop_remove_fd(server->fd); return; } client = malloc(sizeof(*client)); if (!client) return; memset(client, 0, sizeof(*client)); client->fd = accept_client(server->fd); if (client->fd < 0) { free(client); return; } switch (server->type) { case SERVER_TYPE_BREDRLE: type = BTDEV_TYPE_BREDRLE; break; case SERVER_TYPE_BREDR: type = BTDEV_TYPE_BREDR; break; case SERVER_TYPE_LE: type = BTDEV_TYPE_LE; break; case SERVER_TYPE_AMP: type = BTDEV_TYPE_AMP; break; case SERVER_TYPE_MONITOR: goto done; } client->btdev = btdev_create(type, server->id); if (!client->btdev) { close(client->fd); free(client); return; } btdev_set_send_handler(client->btdev, client_write_callback, client); done: if (mainloop_add_fd(client->fd, EPOLLIN, client_read_callback, client, client_destroy) < 0) { btdev_destroy(client->btdev); close(client->fd); free(client); } } static int open_unix(const char *path) { struct sockaddr_un addr; int fd; unlink(path); fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) { perror("Failed to open server socket"); return -1; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, path); if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("Failed to bind server socket"); close(fd); return -1; } if (listen(fd, 5) < 0) { perror("Failed to listen server socket"); close(fd); return -1; } return fd; } struct server *server_open_unix(enum server_type type, const char *path) { struct server *server; server = malloc(sizeof(*server)); if (!server) return NULL; memset(server, 0, sizeof(*server)); server->type = type; server->id = 0x42; server->fd = open_unix(path); if (server->fd < 0) { free(server); return NULL; } if (mainloop_add_fd(server->fd, EPOLLIN, server_accept_callback, server, server_destroy) < 0) { close(server->fd); free(server); return NULL; } return server; } static int open_tcp(void) { struct sockaddr_in addr; int fd, opt = 1; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("Failed to open server socket"); return -1; } if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { perror("Failed to set socket option"); close(fd); return -1; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); addr.sin_port = htons(45550); if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("Failed to bind server socket"); close(fd); return -1; } if (listen(fd, 5) < 0) { perror("Failed to listen server socket"); close(fd); return -1; } return fd; } struct server *server_open_tcp(enum server_type type) { struct server *server; server = malloc(sizeof(*server)); if (!server) return server; memset(server, 0, sizeof(*server)); server->type = type; server->id = 0x43; server->fd = open_tcp(); if (server->fd < 0) { free(server); return NULL; } if (mainloop_add_fd(server->fd, EPOLLIN, server_accept_callback, server, server_destroy) < 0) { close(server->fd); free(server); return NULL; } return server; } void server_close(struct server *server) { if (!server) return; mainloop_remove_fd(server->fd); }