/****************************************************************************** * * Copyright 2013 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ #define LOG_TAG "bt_snoop_net" #include <base/logging.h> #include <errno.h> #include <netinet/in.h> #include <pthread.h> #include <stdbool.h> #include <stdio.h> #include <string.h> #include <sys/prctl.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> #include <mutex> #include "osi/include/log.h" #include "osi/include/osi.h" static void safe_close_(int* fd); static void* listen_fn_(void* context); static const char* LISTEN_THREAD_NAME_ = "btsnoop_net_listen"; static const int LOCALHOST_ = 0x7F000001; static const int LISTEN_PORT_ = 8872; static pthread_t listen_thread_; static bool listen_thread_valid_ = false; static std::mutex client_socket_mutex_; static int listen_socket_ = -1; static int client_socket_ = -1; void btsnoop_net_open() { #if (BT_NET_DEBUG != TRUE) return; // Disable using network sockets for security reasons #endif listen_thread_valid_ = (pthread_create(&listen_thread_, NULL, listen_fn_, NULL) == 0); if (!listen_thread_valid_) LOG_ERROR(LOG_TAG, "%s pthread_create failed: %s", __func__, strerror(errno)); } void btsnoop_net_close() { #if (BT_NET_DEBUG != TRUE) return; // Disable using network sockets for security reasons #endif if (listen_thread_valid_) { shutdown(listen_socket_, SHUT_RDWR); pthread_join(listen_thread_, NULL); safe_close_(&client_socket_); listen_thread_valid_ = false; } } void btsnoop_net_write(const void* data, size_t length) { #if (BT_NET_DEBUG != TRUE) return; // Disable using network sockets for security reasons #endif std::lock_guard<std::mutex> lock(client_socket_mutex_); if (client_socket_ != -1) { ssize_t ret; OSI_NO_INTR(ret = send(client_socket_, data, length, 0)); if (ret == -1 && errno == ECONNRESET) { safe_close_(&client_socket_); } } } static void* listen_fn_(UNUSED_ATTR void* context) { int enable = 1; prctl(PR_SET_NAME, (unsigned long)LISTEN_THREAD_NAME_, 0, 0, 0); listen_socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (listen_socket_ == -1) { LOG_ERROR(LOG_TAG, "%s socket creation failed: %s", __func__, strerror(errno)); goto cleanup; } if (setsockopt(listen_socket_, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) == -1) { LOG_ERROR(LOG_TAG, "%s unable to set SO_REUSEADDR: %s", __func__, strerror(errno)); goto cleanup; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(LOCALHOST_); addr.sin_port = htons(LISTEN_PORT_); if (bind(listen_socket_, (struct sockaddr*)&addr, sizeof(addr)) == -1) { LOG_ERROR(LOG_TAG, "%s unable to bind listen socket: %s", __func__, strerror(errno)); goto cleanup; } if (listen(listen_socket_, 10) == -1) { LOG_ERROR(LOG_TAG, "%s unable to listen: %s", __func__, strerror(errno)); goto cleanup; } for (;;) { int client_socket; OSI_NO_INTR(client_socket = accept(listen_socket_, NULL, NULL)); if (client_socket == -1) { if (errno == EINVAL || errno == EBADF) { break; } LOG_WARN(LOG_TAG, "%s error accepting socket: %s", __func__, strerror(errno)); continue; } /* When a new client connects, we have to send the btsnoop file header. This * allows a decoder to treat the session as a new, valid btsnoop file. */ std::lock_guard<std::mutex> lock(client_socket_mutex_); safe_close_(&client_socket_); client_socket_ = client_socket; OSI_NO_INTR(send(client_socket_, "btsnoop\0\0\0\0\1\0\0\x3\xea", 16, 0)); } cleanup: safe_close_(&listen_socket_); return NULL; } static void safe_close_(int* fd) { CHECK(fd != NULL); if (*fd != -1) { close(*fd); *fd = -1; } }