1 /******************************************************************************
2  *
3  *  Copyright (C) 2022 Google, Inc.
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at:
8  *
9  *  http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  ******************************************************************************/
18 
19 #include "hal/snoop_logger_socket.h"
20 
21 #include <arpa/inet.h>
22 #include <bluetooth/log.h>
23 #include <fcntl.h>
24 #include <netinet/in.h>
25 #include <pthread.h>
26 #include <stdbool.h>
27 #include <string.h>
28 #include <sys/prctl.h>
29 #include <sys/socket.h>
30 #include <sys/types.h>
31 #include <sys/un.h>
32 #include <unistd.h>
33 
34 #include <cerrno>
35 #include <mutex>
36 
37 #include "hal/snoop_logger_common.h"
38 #include "os/utils.h"
39 
40 namespace bluetooth {
41 namespace hal {
42 
43 using bluetooth::hal::SnoopLoggerCommon;
44 
45 static constexpr int INVALID_FD = -1;
46 
47 constexpr int INCOMING_SOCKET_CONNECTIONS_QUEUE_SIZE_ = 10;
48 
SnoopLoggerSocket(SyscallWrapperInterface * syscall_if,int socket_address,int socket_port)49 SnoopLoggerSocket::SnoopLoggerSocket(SyscallWrapperInterface* syscall_if, int socket_address,
50                                      int socket_port)
51     : syscall_if_(syscall_if), socket_address_(socket_address), socket_port_(socket_port) {
52   log::info("address {} port {}", socket_address, socket_port);
53   ResetPollFds();
54 }
55 
~SnoopLoggerSocket()56 SnoopLoggerSocket::~SnoopLoggerSocket() { Cleanup(); }
57 
ResetPollFds()58 void SnoopLoggerSocket::ResetPollFds() {
59   for (int i = 0; i < kNumPollFd; i++) {
60     poll_fds_[i].fd = -1;
61   }
62 }
63 
Write(int & client_socket,const void * data,size_t length)64 void SnoopLoggerSocket::Write(int& client_socket, const void* data, size_t length) {
65   if (client_socket == -1) {
66     return;
67   }
68 
69   ssize_t ret;
70   RUN_NO_INTR(ret = syscall_if_->Send(client_socket, data, length, MSG_DONTWAIT));
71 
72   if (ret == -1 && syscall_if_->GetErrno() == ECONNRESET) {
73     SafeCloseSocket(client_socket);
74   } else if (ret == -1 && syscall_if_->GetErrno() == EAGAIN) {
75     log::error("Dropping snoop pkts because of congestion");
76   }
77 }
78 
Write(const void * data,size_t length)79 void SnoopLoggerSocket::Write(const void* data, size_t length) {
80   std::lock_guard<std::mutex> lock(client_socket_mutex_);
81   Write(client_socket_, data, length);
82 }
83 
InitializeCommunications()84 int SnoopLoggerSocket::InitializeCommunications() {
85   int self_pipe_fds[2];
86   int ret;
87 
88   // Set up the communication channel
89   ret = syscall_if_->Pipe2(self_pipe_fds, O_NONBLOCK | O_CLOEXEC);
90   if (ret < 0) {
91     log::error("Unable to establish a communication channel to the listen thread.");
92     return ret;
93   }
94 
95   notification_listen_fd_ = self_pipe_fds[0];
96   notification_write_fd_ = self_pipe_fds[1];
97 
98   ResetPollFds();
99   poll_fds_[kNotificationFd].fd = notification_listen_fd_;
100   poll_fds_[kNotificationFd].events = POLLIN;
101 
102   listen_socket_ = CreateSocket();
103   if (listen_socket_ == INVALID_FD) {
104     log::error("Unable to create a listen socket.");
105     poll_fds_[kNotificationFd].fd = -1;
106     SafeCloseSocket(notification_listen_fd_);
107     SafeCloseSocket(notification_write_fd_);
108     return -1;
109   }
110 
111   return 0;
112 }
113 
ProcessIncomingRequest()114 bool SnoopLoggerSocket::ProcessIncomingRequest() {
115   if (syscall_if_->Poll(poll_fds_, kNumPollFd, -1) == -1) {
116     log::error("Poll failed {}", strerror(syscall_if_->GetErrno()));
117     return syscall_if_->GetErrno() == EINTR;
118   }
119 
120   if (poll_fds_[kNotificationFd].revents) {
121     log::warn("exiting from listen_fn_ thread");
122     return false;
123   }
124 
125   if (poll_fds_[kSocketFd].revents) {
126     int client_socket = -1;
127     int ret = AcceptIncomingConnection(listen_socket_, client_socket);
128     if (ret != 0) {
129       // Unrecoverable error, stop the thread.
130       return false;
131     }
132 
133     if (client_socket < 0) {
134       return true;
135     }
136 
137     InitializeClientSocket(client_socket);
138     ClientSocketConnected(client_socket);
139   }
140 
141   return true;
142 }
143 
Cleanup()144 void SnoopLoggerSocket::Cleanup() {
145   SafeCloseSocket(notification_listen_fd_);
146   SafeCloseSocket(notification_write_fd_);
147   SafeCloseSocket(client_socket_);
148   SafeCloseSocket(listen_socket_);
149   ResetPollFds();
150 }
151 
AcceptIncomingConnection(int listen_socket,int & client_socket)152 int SnoopLoggerSocket::AcceptIncomingConnection(int listen_socket, int& client_socket) {
153   socklen_t clen;
154   struct sockaddr_in client_addr;
155 
156   RUN_NO_INTR(client_socket = syscall_if_->Accept(listen_socket, (struct sockaddr*)&client_addr,
157                                                   &clen, SOCK_CLOEXEC));
158   if (client_socket == -1) {
159     int errno_ = syscall_if_->GetErrno();
160     log::warn("error accepting socket: {}", strerror(errno_));
161     if (errno_ == EINVAL || errno_ == EBADF) {
162       return errno_;
163     }
164     return 0;
165   }
166 
167   log::info("Client socket fd: {}, IP address: {}, port: {}", client_socket,
168             inet_ntoa(client_addr.sin_addr), (int)ntohs(client_addr.sin_port));
169 
170   return 0;
171 }
172 
InitializeClientSocket(int client_socket)173 void SnoopLoggerSocket::InitializeClientSocket(int client_socket) {
174   /* When a new client connects, we have to send the btsnoop file header. This
175    * allows a decoder to treat the session as a new, valid btsnoop file. */
176   Write(client_socket, reinterpret_cast<const char*>(&SnoopLoggerCommon::kBtSnoopFileHeader),
177         sizeof(SnoopLoggerCommon::FileHeaderType));
178 }
179 
ClientSocketConnected(int client_socket)180 void SnoopLoggerSocket::ClientSocketConnected(int client_socket) {
181   std::lock_guard<std::mutex> lock(client_socket_mutex_);
182   SafeCloseSocket(client_socket_);
183   client_socket_ = client_socket;
184   client_socket_cv_.notify_one();
185 }
186 
WaitForClientSocketConnected()187 bool SnoopLoggerSocket::WaitForClientSocketConnected() {
188   std::unique_lock<std::mutex> lk(client_socket_mutex_);
189   client_socket_cv_.wait(lk, [this] { return IsClientSocketConnected(); });
190   return IsClientSocketConnected();
191 }
192 
IsClientSocketConnected() const193 bool SnoopLoggerSocket::IsClientSocketConnected() const { return client_socket_ != INVALID_FD; }
194 
CreateSocket()195 int SnoopLoggerSocket::CreateSocket() {
196   log::debug("");
197   int ret;
198 
199   // Create a TCP socket file descriptor
200   int socket_fd = syscall_if_->Socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
201   if (socket_fd < 0) {
202     log::error("can't create socket: {}", strerror(syscall_if_->GetErrno()));
203     return INVALID_FD;
204   }
205 
206   // Enable REUSEADDR
207   int enable = 1;
208   ret = syscall_if_->Setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
209   if (ret < 0) {
210     log::error("unable to set SO_REUSEADDR: {}", strerror(syscall_if_->GetErrno()));
211     SafeCloseSocket(socket_fd);
212     return INVALID_FD;
213   }
214 
215   struct sockaddr_in addr;
216   addr.sin_family = AF_INET;
217   addr.sin_addr.s_addr = htonl(socket_address_);
218   addr.sin_port = htons(socket_port_);
219 
220   // Bind socket to an address
221   ret = syscall_if_->Bind(socket_fd, (struct sockaddr*)&addr, sizeof(addr));
222   if (ret < 0) {
223     log::error("unable to bind snoop socket to address: {}", strerror(syscall_if_->GetErrno()));
224     SafeCloseSocket(socket_fd);
225     return INVALID_FD;
226   }
227 
228   // Mark this socket as a socket that will accept connections.
229   ret = syscall_if_->Listen(socket_fd, INCOMING_SOCKET_CONNECTIONS_QUEUE_SIZE_);
230   if (ret < 0) {
231     log::error("unable to listen: {}", strerror(syscall_if_->GetErrno()));
232     SafeCloseSocket(socket_fd);
233     return INVALID_FD;
234   }
235 
236   poll_fds_[kSocketFd].fd = socket_fd;
237   poll_fds_[kSocketFd].events = POLLIN;
238   return socket_fd;
239 }
240 
NotifySocketListener()241 int SnoopLoggerSocket::NotifySocketListener() {
242   log::debug("");
243   char buffer = '0';
244   int ret = -1;
245 
246   if (notification_write_fd_ == -1) {
247     return 0;
248   }
249 
250   RUN_NO_INTR(ret = syscall_if_->Write(notification_write_fd_, &buffer, 1));
251   if (ret < 0) {
252     log::error("Error in notifying the listen thread to exit ({})", ret);
253     return -1;
254   }
255 
256   return 0;
257 }
258 
SafeCloseSocket(int & fd)259 void SnoopLoggerSocket::SafeCloseSocket(int& fd) {
260   log::debug("{}", fd);
261   if (fd != -1) {
262     syscall_if_->Close(fd);
263     fd = -1;
264   }
265 }
266 
GetSyscallWrapperInterface() const267 SyscallWrapperInterface* SnoopLoggerSocket::GetSyscallWrapperInterface() const {
268   return syscall_if_;
269 }
270 
271 }  // namespace hal
272 }  // namespace bluetooth
273