1/* Copyright JS Foundation and other contributors, http://js.foundation 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "jerryscript-debugger-transport.h" 17#include "jerryscript-ext/debugger.h" 18#include "jext-common.h" 19 20#if defined (JERRY_DEBUGGER) && (JERRY_DEBUGGER == 1) 21 22#include <errno.h> 23 24#ifdef _WIN32 25#include <basetsd.h> 26typedef SSIZE_T ssize_t; 27#include <ws2tcpip.h> 28#include <winsock2.h> 29 30/* On Windows the WSAEWOULDBLOCK value can be returned for non-blocking operations */ 31#define JERRYX_EWOULDBLOCK WSAEWOULDBLOCK 32 33/* On Windows the invalid socket's value of INVALID_SOCKET */ 34#define JERRYX_SOCKET_INVALID INVALID_SOCKET 35 36/* On Windows sockets have a SOCKET typedef */ 37typedef SOCKET jerryx_socket; 38 39#else /* !_WIN32 */ 40 41#include <arpa/inet.h> 42#include <fcntl.h> 43#include <sys/socket.h> 44#include <unistd.h> 45 46/* On *nix the EWOULDBLOCK errno value can be returned for non-blocking operations */ 47#define JERRYX_EWOULDBLOCK EWOULDBLOCK 48 49/* On *nix the invalid socket has a value of -1 */ 50#define JERRYX_SOCKET_INVALID (-1) 51 52/* On *nix the sockets are integer identifiers */ 53typedef int jerryx_socket; 54#endif /* _WIN32 */ 55 56/** 57 * Implementation of transport over tcp/ip. 58 */ 59typedef struct 60{ 61 jerry_debugger_transport_header_t header; /**< transport header */ 62 jerryx_socket tcp_socket; /**< tcp socket */ 63} jerryx_debugger_transport_tcp_t; 64 65/** 66 * Get the network error value. 67 * 68 * On Windows this returns the result of the `WSAGetLastError ()` call and 69 * on any other system the `errno` value. 70 * 71 * 72 * @return last error value. 73 */ 74static inline int 75jerryx_debugger_tcp_get_errno (void) 76{ 77#ifdef _WIN32 78 return WSAGetLastError (); 79#else /* !_WIN32 */ 80 return errno; 81#endif /* _WIN32 */ 82} /* jerryx_debugger_tcp_get_errno */ 83 84/** 85 * Correctly close a single socket. 86 */ 87static inline void 88jerryx_debugger_tcp_close_socket (jerryx_socket socket_id) /**< socket to close */ 89{ 90#ifdef _WIN32 91 closesocket (socket_id); 92#else /* !_WIN32 */ 93 close (socket_id); 94#endif /* _WIN32 */ 95} /* jerryx_debugger_tcp_close_socket */ 96 97/** 98 * Log tcp error message. 99 */ 100static void 101jerryx_debugger_tcp_log_error (int errno_value) /**< error value to log */ 102{ 103 if (errno_value == 0) 104 { 105 return; 106 } 107 108#ifdef _WIN32 109 char *error_message = NULL; 110 FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 111 NULL, 112 (DWORD)errno_value, 113 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), 114 (LPTSTR) &error_message, 115 0, 116 NULL); 117 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "TCP Error: %s\n", error_message); 118 LocalFree (error_message); 119#else /* !_WIN32 */ 120 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "TCP Error: %s\n", strerror (errno_value)); 121#endif /* _WIN32 */ 122} /* jerryx_debugger_tcp_log_error */ 123 124/** 125 * Close a tcp connection. 126 */ 127static void 128jerryx_debugger_tcp_close (jerry_debugger_transport_header_t *header_p) /**< tcp implementation */ 129{ 130 JERRYX_ASSERT (!jerry_debugger_transport_is_connected ()); 131 132 jerryx_debugger_transport_tcp_t *tcp_p = (jerryx_debugger_transport_tcp_t *) header_p; 133 134 JERRYX_DEBUG_MSG ("TCP connection closed.\n"); 135 136 jerryx_debugger_tcp_close_socket (tcp_p->tcp_socket); 137 138 jerry_heap_free ((void *) header_p, sizeof (jerryx_debugger_transport_tcp_t)); 139} /* jerryx_debugger_tcp_close */ 140 141/** 142 * Send data over a tcp connection. 143 * 144 * @return true - if the data has been sent successfully 145 * false - otherwise 146 */ 147static bool 148jerryx_debugger_tcp_send (jerry_debugger_transport_header_t *header_p, /**< tcp implementation */ 149 uint8_t *message_p, /**< message to be sent */ 150 size_t message_length) /**< message length in bytes */ 151{ 152 JERRYX_ASSERT (jerry_debugger_transport_is_connected ()); 153 154 jerryx_debugger_transport_tcp_t *tcp_p = (jerryx_debugger_transport_tcp_t *) header_p; 155 156 do 157 { 158#ifdef __linux__ 159 ssize_t is_err = recv (tcp_p->tcp_socket, NULL, 0, MSG_PEEK); 160 161 if (is_err == 0 && errno != JERRYX_EWOULDBLOCK) 162 { 163 int err_val = errno; 164 jerry_debugger_transport_close (); 165 jerryx_debugger_tcp_log_error (err_val); 166 return false; 167 } 168#endif /* __linux__ */ 169#ifdef _WIN32 170 ssize_t sent_bytes = send (tcp_p->tcp_socket, (char *)message_p, (int)message_length, 0); 171#else 172 ssize_t sent_bytes = send (tcp_p->tcp_socket, message_p, message_length, 0); 173#endif 174 175 if (sent_bytes < 0) 176 { 177 int err_val = jerryx_debugger_tcp_get_errno (); 178 179 if (err_val == JERRYX_EWOULDBLOCK) 180 { 181 continue; 182 } 183 184 jerry_debugger_transport_close (); 185 jerryx_debugger_tcp_log_error (err_val); 186 return false; 187 } 188 189 message_p += sent_bytes; 190 message_length -= (size_t) sent_bytes; 191 } 192 while (message_length > 0); 193 194 return true; 195} /* jerryx_debugger_tcp_send */ 196 197/** 198 * Receive data from a tcp connection. 199 */ 200static bool 201jerryx_debugger_tcp_receive (jerry_debugger_transport_header_t *header_p, /**< tcp implementation */ 202 jerry_debugger_transport_receive_context_t *receive_context_p) /**< receive context */ 203{ 204 jerryx_debugger_transport_tcp_t *tcp_p = (jerryx_debugger_transport_tcp_t *) header_p; 205 206 uint8_t *buffer_p = receive_context_p->buffer_p + receive_context_p->received_length; 207 size_t buffer_size = JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE - receive_context_p->received_length; 208#ifdef _WIN32 209 ssize_t length = recv (tcp_p->tcp_socket, (char *)buffer_p, (int)buffer_size, 0); 210#else 211 ssize_t length = recv (tcp_p->tcp_socket, buffer_p, buffer_size, 0); 212#endif 213 214 if (length <= 0) 215 { 216 int err_val = jerryx_debugger_tcp_get_errno (); 217 218 if (err_val != JERRYX_EWOULDBLOCK || length == 0) 219 { 220 jerry_debugger_transport_close (); 221 jerryx_debugger_tcp_log_error (err_val); 222 return false; 223 } 224 length = 0; 225 } 226 227 receive_context_p->received_length += (size_t) length; 228 229 if (receive_context_p->received_length > 0) 230 { 231 receive_context_p->message_p = receive_context_p->buffer_p; 232 receive_context_p->message_length = receive_context_p->received_length; 233 } 234 235 return true; 236} /* jerryx_debugger_tcp_receive */ 237 238/** 239 * Utility method to prepare the server socket to accept connections. 240 * 241 * The following steps are performed: 242 * * Configure address re-use. 243 * * Bind the socket to the given port 244 * * Start listening on the socket. 245 * 246 * @return true if everything is ok 247 * false if there was an error 248 */ 249static bool 250jerryx_debugger_tcp_configure_socket (jerryx_socket server_socket, /** < socket to configure */ 251 uint16_t port) /** < port number to be used for the socket */ 252{ 253 struct sockaddr_in addr; 254 255 addr.sin_family = AF_INET; 256 addr.sin_port = htons (port); 257 addr.sin_addr.s_addr = INADDR_ANY; 258 259#ifdef _WIN32 260 char opt_value = 1; 261#else 262 int opt_value = 1; 263#endif 264 265 if (setsockopt (server_socket, SOL_SOCKET, SO_REUSEADDR, &opt_value, sizeof (int)) != 0) 266 { 267 return false; 268 } 269 270 if (bind (server_socket, (struct sockaddr *) &addr, sizeof (struct sockaddr_in)) != 0) 271 { 272 return false; 273 } 274 275 if (listen (server_socket, 1) != 0) 276 { 277 return false; 278 } 279 280 return true; 281} /* jerryx_debugger_tcp_configure_socket */ 282 283/** 284 * Create a tcp connection. 285 * 286 * @return true if successful, 287 * false otherwise 288 */ 289bool 290jerryx_debugger_tcp_create (uint16_t port) /**< listening port */ 291{ 292#ifdef _WIN32 293 WSADATA wsaData; 294 int wsa_init_status = WSAStartup (MAKEWORD (2, 2), &wsaData); 295 if (wsa_init_status != NO_ERROR) 296 { 297 JERRYX_ERROR_MSG ("WSA Error: %d\n", wsa_init_status); 298 return false; 299 } 300#endif /* _WIN32 */ 301 302 jerryx_socket server_socket = socket (AF_INET, SOCK_STREAM, 0); 303 if (server_socket == JERRYX_SOCKET_INVALID) 304 { 305 jerryx_debugger_tcp_log_error (jerryx_debugger_tcp_get_errno ()); 306 return false; 307 } 308 309 if (!jerryx_debugger_tcp_configure_socket (server_socket, port)) 310 { 311 int error = jerryx_debugger_tcp_get_errno (); 312 jerryx_debugger_tcp_close_socket (server_socket); 313 jerryx_debugger_tcp_log_error (error); 314 return false; 315 } 316 317 JERRYX_DEBUG_MSG ("Waiting for client connection\n"); 318 319 struct sockaddr_in addr; 320 socklen_t sin_size = sizeof (struct sockaddr_in); 321 322 jerryx_socket tcp_socket = accept (server_socket, (struct sockaddr *) &addr, &sin_size); 323 324 jerryx_debugger_tcp_close_socket (server_socket); 325 326 if (tcp_socket == JERRYX_SOCKET_INVALID) 327 { 328 jerryx_debugger_tcp_log_error (jerryx_debugger_tcp_get_errno ()); 329 return false; 330 } 331 332 /* Set non-blocking mode. */ 333#ifdef _WIN32 334 u_long nonblocking_enabled = 1; 335 if (ioctlsocket (tcp_socket, (long)FIONBIO, &nonblocking_enabled) != NO_ERROR) 336 { 337 jerryx_debugger_tcp_close_socket (tcp_socket); 338 return false; 339 } 340#else /* !_WIN32 */ 341 int socket_flags = fcntl (tcp_socket, F_GETFL, 0); 342 343 if (socket_flags < 0) 344 { 345 close (tcp_socket); 346 return false; 347 } 348 349 if (fcntl (tcp_socket, F_SETFL, socket_flags | O_NONBLOCK) == -1) 350 { 351 close (tcp_socket); 352 return false; 353 } 354#endif /* _WIN32 */ 355 356 JERRYX_DEBUG_MSG ("Connected from: %s\n", inet_ntoa (addr.sin_addr)); 357 358 size_t size = sizeof (jerryx_debugger_transport_tcp_t); 359 360 jerry_debugger_transport_header_t *header_p; 361 header_p = (jerry_debugger_transport_header_t *) jerry_heap_alloc (size); 362 363 if (!header_p) 364 { 365 jerryx_debugger_tcp_close_socket (tcp_socket); 366 return false; 367 } 368 369 header_p->close = jerryx_debugger_tcp_close; 370 header_p->send = jerryx_debugger_tcp_send; 371 header_p->receive = jerryx_debugger_tcp_receive; 372 373 ((jerryx_debugger_transport_tcp_t *) header_p)->tcp_socket = tcp_socket; 374 375 jerry_debugger_transport_add (header_p, 376 0, 377 JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE, 378 0, 379 JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE); 380 381 return true; 382} /* jerryx_debugger_tcp_create */ 383 384#else /* !(defined (JERRY_DEBUGGER) && (JERRY_DEBUGGER == 1)) */ 385 386/** 387 * Dummy function when debugger is disabled. 388 * 389 * @return false 390 */ 391bool 392jerryx_debugger_tcp_create (uint16_t port) 393{ 394 JERRYX_UNUSED (port); 395 return false; 396} /* jerryx_debugger_tcp_create */ 397 398#endif /* defined (JERRY_DEBUGGER) && (JERRY_DEBUGGER == 1) */ 399