1 #include "inspector_socket_server.h"
2 #include <unistd.h>
3
4 #include "node.h"
5 #include "util-inl.h"
6 #include "uv.h"
7 #include "zlib.h"
8
9 #include <algorithm>
10 #include <cstdio>
11 #include <iostream>
12 #include <map>
13 #include <set>
14 #include <sstream>
15 #include <string>
16
17 namespace node {
18 namespace inspector {
19
20 // Function is declared in inspector_io.h so the rest of the node does not
21 // depend on inspector_socket_server.h
22 std::string FormatWsAddress(const std::string& host, int port,
23 const std::string& target_id,
24 bool include_protocol);
25 namespace {
26
27 static const uint8_t PROTOCOL_JSON[] = {
28 #include "v8_inspector_protocol_json.h" // NOLINT(build/include_order)
29 };
30
Escape(std::string* string)31 void Escape(std::string* string) {
32 for (char& c : *string) {
33 c = (c == '\"' || c == '\\') ? '_' : c;
34 }
35 }
36
FormatHostPort(const std::string& host, int port)37 std::string FormatHostPort(const std::string& host, int port) {
38 // Host is valid (socket was bound) so colon means it's a v6 IP address
39 bool v6 = host.find(':') != std::string::npos;
40 std::ostringstream url;
41 if (v6) {
42 url << '[';
43 }
44 url << host;
45 if (v6) {
46 url << ']';
47 }
48 url << ':' << port;
49 return url.str();
50 }
51
FormatAddress(const std::string& host, const std::string& target_id, bool include_protocol)52 std::string FormatAddress(const std::string& host,
53 const std::string& target_id,
54 bool include_protocol) {
55 std::ostringstream url;
56 if (include_protocol)
57 url << "ws://";
58 url << host << '/' << target_id;
59 return url.str();
60 }
61
MapToString(const std::map<std::string, std::string>& object)62 std::string MapToString(const std::map<std::string, std::string>& object) {
63 bool first = true;
64 std::ostringstream json;
65 json << "{\n";
66 for (const auto& name_value : object) {
67 if (!first)
68 json << ",\n";
69 first = false;
70 json << " \"" << name_value.first << "\": \"";
71 json << name_value.second << "\"";
72 }
73 json << "\n} ";
74 return json.str();
75 }
76
MapsToString( const std::vector<std::map<std::string, std::string>>& array)77 std::string MapsToString(
78 const std::vector<std::map<std::string, std::string>>& array) {
79 bool first = true;
80 std::ostringstream json;
81 json << "[ ";
82 for (const auto& object : array) {
83 if (!first)
84 json << ", ";
85 first = false;
86 json << MapToString(object);
87 }
88 json << "]\n\n";
89 return json.str();
90 }
91
MatchPathSegment(const char* path, const char* expected)92 const char* MatchPathSegment(const char* path, const char* expected) {
93 size_t len = strlen(expected);
94 if (StringEqualNoCaseN(path, expected, len)) {
95 if (path[len] == '/') return path + len + 1;
96 if (path[len] == '\0') return path + len;
97 }
98 return nullptr;
99 }
100
SendHttpResponse(InspectorSocket* socket, const std::string& response, int code)101 void SendHttpResponse(InspectorSocket* socket,
102 const std::string& response,
103 int code) {
104 const char HEADERS[] = "HTTP/1.0 %d OK\r\n"
105 "Content-Type: application/json; charset=UTF-8\r\n"
106 "Cache-Control: no-cache\r\n"
107 "Content-Length: %zu\r\n"
108 "\r\n";
109 char header[sizeof(HEADERS) + 20];
110 int header_len = snprintf(header,
111 sizeof(header),
112 HEADERS,
113 code,
114 response.size());
115 socket->Write(header, header_len);
116 socket->Write(response.data(), response.size());
117 }
118
SendVersionResponse(InspectorSocket* socket)119 void SendVersionResponse(InspectorSocket* socket) {
120 std::map<std::string, std::string> response;
121 response["Browser"] = "node.js/" NODE_VERSION;
122 response["Protocol-Version"] = "1.1";
123 SendHttpResponse(socket, MapToString(response), 200);
124 }
125
SendHttpNotFound(InspectorSocket* socket)126 void SendHttpNotFound(InspectorSocket* socket) {
127 SendHttpResponse(socket, "", 404);
128 }
129
SendProtocolJson(InspectorSocket* socket)130 void SendProtocolJson(InspectorSocket* socket) {
131 z_stream strm;
132 strm.zalloc = Z_NULL;
133 strm.zfree = Z_NULL;
134 strm.opaque = Z_NULL;
135 CHECK_EQ(Z_OK, inflateInit(&strm));
136 static const size_t kDecompressedSize =
137 PROTOCOL_JSON[0] * 0x10000u +
138 PROTOCOL_JSON[1] * 0x100u +
139 PROTOCOL_JSON[2];
140 strm.next_in = const_cast<uint8_t*>(PROTOCOL_JSON + 3);
141 strm.avail_in = sizeof(PROTOCOL_JSON) - 3;
142 std::string data(kDecompressedSize, '\0');
143 strm.next_out = reinterpret_cast<Byte*>(data.data());
144 strm.avail_out = data.size();
145 CHECK_EQ(Z_STREAM_END, inflate(&strm, Z_FINISH));
146 CHECK_EQ(0, strm.avail_out);
147 CHECK_EQ(Z_OK, inflateEnd(&strm));
148 SendHttpResponse(socket, data, 200);
149 }
150 } // namespace
151
FormatWsAddress(const std::string& host, int port, const std::string& target_id, bool include_protocol)152 std::string FormatWsAddress(const std::string& host, int port,
153 const std::string& target_id,
154 bool include_protocol) {
155 return FormatAddress(FormatHostPort(host, port), target_id, include_protocol);
156 }
157
158 class SocketSession {
159 public:
160 SocketSession(InspectorSocketServer* server, int id, int server_port);
Close()161 void Close() {
162 ws_socket_.reset();
163 }
164 void Send(const std::string& message);
Own(InspectorSocket::Pointer ws_socket)165 void Own(InspectorSocket::Pointer ws_socket) {
166 ws_socket_ = std::move(ws_socket);
167 }
id() const168 int id() const { return id_; }
server_port()169 int server_port() {
170 return server_port_;
171 }
ws_socket()172 InspectorSocket* ws_socket() {
173 return ws_socket_.get();
174 }
Accept(const std::string& ws_key)175 void Accept(const std::string& ws_key) {
176 ws_socket_->AcceptUpgrade(ws_key);
177 }
Decline()178 void Decline() {
179 ws_socket_->CancelHandshake();
180 }
181
182 class Delegate : public InspectorSocket::Delegate {
183 public:
Delegate(InspectorSocketServer* server, int session_id)184 Delegate(InspectorSocketServer* server, int session_id)
185 : server_(server), session_id_(session_id) { }
186 ~Delegate() override {
187 server_->SessionTerminated(session_id_);
188 }
189 void OnHttpGet(const std::string& host, const std::string& path) override;
190 void OnSocketUpgrade(const std::string& host, const std::string& path,
191 const std::string& ws_key) override;
192 void OnWsFrame(const std::vector<char>& data) override;
193
194 private:
Session()195 SocketSession* Session() {
196 return server_->Session(session_id_);
197 }
198
199 InspectorSocketServer* server_;
200 int session_id_;
201 };
202
203 private:
204 const int id_;
205 InspectorSocket::Pointer ws_socket_;
206 const int server_port_;
207 };
208
209 class ServerSocket {
210 public:
ServerSocket(InspectorSocketServer* server)211 explicit ServerSocket(InspectorSocketServer* server)
212 : tcp_socket_(uv_tcp_t()), server_(server), unix_socket_(uv_pipe_t()) {}
213 int Listen(sockaddr* addr, uv_loop_t* loop, int pid = -1);
Close()214 void Close() {
215 uv_close(reinterpret_cast<uv_handle_t*>(&tcp_socket_), FreeOnCloseCallback);
216 }
CloseUnix()217 void CloseUnix() {
218 if (unix_socket_on) {
219 uv_close(reinterpret_cast<uv_handle_t*>(&unix_socket_), nullptr);
220 unix_socket_on = false;
221 }
222 }
port() const223 int port() const { return port_; }
224
225 private:
226 template <typename UvHandle>
FromTcpSocket(UvHandle* socket)227 static ServerSocket* FromTcpSocket(UvHandle* socket) {
228 return node::ContainerOf(&ServerSocket::tcp_socket_,
229 reinterpret_cast<uv_tcp_t*>(socket));
230 }
231 static void SocketConnectedCallback(uv_stream_t* tcp_socket, int status);
232 static void UnixSocketConnectedCallback(uv_stream_t* unix_socket, int status);
FreeOnCloseCallback(uv_handle_t* tcp_socket_)233 static void FreeOnCloseCallback(uv_handle_t* tcp_socket_) {
234 delete FromTcpSocket(tcp_socket_);
235 }
236 int DetectPort(uv_loop_t *loop, int pid);
237 ~ServerSocket() = default;
238
239 uv_tcp_t tcp_socket_;
240 InspectorSocketServer* server_;
241 uv_pipe_t unix_socket_;
242 int port_ = -1;
243 bool unix_socket_on = false;
244 };
245
PrintDebuggerReadyMessage( const std::string& host, const std::vector<InspectorSocketServer::ServerSocketPtr>& server_sockets, const std::vector<std::string>& ids, const char* verb, bool publish_uid_stderr, FILE* out)246 void PrintDebuggerReadyMessage(
247 const std::string& host,
248 const std::vector<InspectorSocketServer::ServerSocketPtr>& server_sockets,
249 const std::vector<std::string>& ids,
250 const char* verb,
251 bool publish_uid_stderr,
252 FILE* out) {
253 if (!publish_uid_stderr || out == nullptr) {
254 return;
255 }
256 for (const auto& server_socket : server_sockets) {
257 for (const std::string& id : ids) {
258 fprintf(out, "Debugger %s on %s\n",
259 verb,
260 FormatWsAddress(host, server_socket->port(), id, true).c_str());
261 }
262 }
263 fprintf(out, "For help, see: %s\n",
264 "https://nodejs.org/en/docs/inspector");
265 fflush(out);
266 }
267
InspectorSocketServer( std::unique_ptr<SocketServerDelegate> delegate, uv_loop_t* loop, const std::string& host, int port, const InspectPublishUid& inspect_publish_uid, FILE* out, int pid)268 InspectorSocketServer::InspectorSocketServer(
269 std::unique_ptr<SocketServerDelegate> delegate, uv_loop_t* loop,
270 const std::string& host, int port,
271 const InspectPublishUid& inspect_publish_uid, FILE* out, int pid)
272 : loop_(loop),
273 delegate_(std::move(delegate)),
274 host_(host),
275 port_(port),
276 inspect_publish_uid_(inspect_publish_uid),
277 next_session_id_(0),
278 out_(out),
279 pid_(pid) {
280 delegate_->AssignServer(this);
281 state_ = ServerState::kNew;
282 }
283
284 InspectorSocketServer::~InspectorSocketServer() = default;
285
Session(int session_id)286 SocketSession* InspectorSocketServer::Session(int session_id) {
287 auto it = connected_sessions_.find(session_id);
288 return it == connected_sessions_.end() ? nullptr : it->second.second.get();
289 }
290
SessionStarted(int session_id, const std::string& id, const std::string& ws_key)291 void InspectorSocketServer::SessionStarted(int session_id,
292 const std::string& id,
293 const std::string& ws_key) {
294 SocketSession* session = Session(session_id);
295 if (!TargetExists(id)) {
296 session->Decline();
297 return;
298 }
299 connected_sessions_[session_id].first = id;
300 session->Accept(ws_key);
301 delegate_->StartSession(session_id, id);
302 }
303
SessionTerminated(int session_id)304 void InspectorSocketServer::SessionTerminated(int session_id) {
305 if (Session(session_id) == nullptr) {
306 return;
307 }
308 bool was_attached = connected_sessions_[session_id].first != "";
309 if (was_attached) {
310 delegate_->EndSession(session_id);
311 }
312 connected_sessions_.erase(session_id);
313 if (connected_sessions_.empty()) {
314 if (was_attached && state_ == ServerState::kRunning
315 && !server_sockets_.empty()) {
316 PrintDebuggerReadyMessage(host_,
317 server_sockets_,
318 delegate_->GetTargetIds(),
319 "ending",
320 inspect_publish_uid_.console,
321 out_);
322 }
323 if (state_ == ServerState::kStopped) {
324 delegate_.reset();
325 }
326 }
327 }
328
HandleGetRequest(int session_id, const std::string& host, const std::string& path)329 bool InspectorSocketServer::HandleGetRequest(int session_id,
330 const std::string& host,
331 const std::string& path) {
332 SocketSession* session = Session(session_id);
333 InspectorSocket* socket = session->ws_socket();
334 if (!inspect_publish_uid_.http) {
335 SendHttpNotFound(socket);
336 return true;
337 }
338 const char* command = MatchPathSegment(path.c_str(), "/json");
339 if (command == nullptr)
340 return false;
341
342 if (MatchPathSegment(command, "list") || command[0] == '\0') {
343 SendListResponse(socket, host, session);
344 return true;
345 } else if (MatchPathSegment(command, "protocol")) {
346 SendProtocolJson(socket);
347 return true;
348 } else if (MatchPathSegment(command, "version")) {
349 SendVersionResponse(socket);
350 return true;
351 }
352 return false;
353 }
354
SendListResponse(InspectorSocket* socket, const std::string& host, SocketSession* session)355 void InspectorSocketServer::SendListResponse(InspectorSocket* socket,
356 const std::string& host,
357 SocketSession* session) {
358 std::vector<std::map<std::string, std::string>> response;
359 for (const std::string& id : delegate_->GetTargetIds()) {
360 response.push_back(std::map<std::string, std::string>());
361 std::map<std::string, std::string>& target_map = response.back();
362 target_map["description"] = "node.js instance";
363 target_map["faviconUrl"] =
364 "https://nodejs.org/static/images/favicons/favicon.ico";
365 target_map["id"] = id;
366 target_map["title"] = delegate_->GetTargetTitle(id);
367 Escape(&target_map["title"]);
368 target_map["type"] = "node";
369 // This attribute value is a "best effort" URL that is passed as a JSON
370 // string. It is not guaranteed to resolve to a valid resource.
371 target_map["url"] = delegate_->GetTargetUrl(id);
372 Escape(&target_map["url"]);
373
374 std::string detected_host = host;
375 if (detected_host.empty()) {
376 detected_host = FormatHostPort(socket->GetHost(),
377 session->server_port());
378 }
379 std::string formatted_address = FormatAddress(detected_host, id, false);
380 target_map["devtoolsFrontendUrl"] = GetFrontendURL(false,
381 formatted_address);
382 // The compat URL is for Chrome browsers older than 66.0.3345.0
383 target_map["devtoolsFrontendUrlCompat"] = GetFrontendURL(true,
384 formatted_address);
385 target_map["webSocketDebuggerUrl"] = FormatAddress(detected_host, id, true);
386 }
387 SendHttpResponse(socket, MapsToString(response), 200);
388 }
389
GetFrontendURL(bool is_compat, const std::string &formatted_address)390 std::string InspectorSocketServer::GetFrontendURL(bool is_compat,
391 const std::string &formatted_address) {
392 std::ostringstream frontend_url;
393 frontend_url << "devtools://devtools/bundled/";
394 frontend_url << (is_compat ? "inspector" : "js_app");
395 frontend_url << ".html?v8only=true&ws=";
396 frontend_url << formatted_address;
397 return frontend_url.str();
398 }
399
Start()400 bool InspectorSocketServer::Start() {
401 CHECK_NOT_NULL(delegate_);
402 CHECK_EQ(state_, ServerState::kNew);
403 std::unique_ptr<SocketServerDelegate> delegate_holder;
404 // We will return it if startup is successful
405 delegate_.swap(delegate_holder);
406 struct addrinfo hints;
407 memset(&hints, 0, sizeof(hints));
408 hints.ai_flags = AI_NUMERICSERV;
409 hints.ai_socktype = SOCK_STREAM;
410 uv_getaddrinfo_t req;
411 const std::string port_string = std::to_string(port_);
412 int err = uv_getaddrinfo(loop_, &req, nullptr, host_.c_str(),
413 port_string.c_str(), &hints);
414 if (err < 0) {
415 if (out_ != nullptr) {
416 fprintf(out_, "Unable to resolve \"%s\": %s\n", host_.c_str(),
417 uv_strerror(err));
418 }
419 return false;
420 }
421 for (addrinfo* address = req.addrinfo; address != nullptr;
422 address = address->ai_next) {
423 auto server_socket = ServerSocketPtr(new ServerSocket(this));
424 err = server_socket->Listen(address->ai_addr, loop_, pid_);
425 if (err == 0)
426 server_sockets_.push_back(std::move(server_socket));
427 }
428 uv_freeaddrinfo(req.addrinfo);
429
430 // We only show error if we failed to start server on all addresses. We only
431 // show one error, for the last address.
432 if (server_sockets_.empty()) {
433 if (out_ != nullptr) {
434 fprintf(out_, "Starting inspector on %s:%d failed: %s\n",
435 host_.c_str(), port_, uv_strerror(err));
436 fflush(out_);
437 }
438 return false;
439 }
440 delegate_.swap(delegate_holder);
441 state_ = ServerState::kRunning;
442 PrintDebuggerReadyMessage(host_,
443 server_sockets_,
444 delegate_->GetTargetIds(),
445 "listening",
446 inspect_publish_uid_.console,
447 out_);
448 return true;
449 }
450
Stop()451 void InspectorSocketServer::Stop() {
452 if (state_ == ServerState::kStopped)
453 return;
454 CHECK_EQ(state_, ServerState::kRunning);
455 state_ = ServerState::kStopped;
456 server_sockets_.clear();
457 if (done())
458 delegate_.reset();
459 }
460
TerminateConnections()461 void InspectorSocketServer::TerminateConnections() {
462 for (const auto& key_value : connected_sessions_)
463 key_value.second.second->Close();
464 }
465
TargetExists(const std::string& id)466 bool InspectorSocketServer::TargetExists(const std::string& id) {
467 const std::vector<std::string>& target_ids = delegate_->GetTargetIds();
468 const auto& found = std::find(target_ids.begin(), target_ids.end(), id);
469 return found != target_ids.end();
470 }
471
Port() const472 int InspectorSocketServer::Port() const {
473 if (!server_sockets_.empty()) {
474 return server_sockets_[0]->port();
475 }
476 return port_;
477 }
478
Accept(int server_port, uv_stream_t* server_socket)479 void InspectorSocketServer::Accept(int server_port,
480 uv_stream_t* server_socket) {
481 std::unique_ptr<SocketSession> session(
482 new SocketSession(this, next_session_id_++, server_port));
483
484 InspectorSocket::DelegatePointer delegate =
485 InspectorSocket::DelegatePointer(
486 new SocketSession::Delegate(this, session->id()));
487
488 InspectorSocket::Pointer inspector =
489 InspectorSocket::Accept(server_socket, std::move(delegate));
490 if (inspector) {
491 session->Own(std::move(inspector));
492 connected_sessions_[session->id()].second = std::move(session);
493 }
494 }
495
Send(int session_id, const std::string& message)496 void InspectorSocketServer::Send(int session_id, const std::string& message) {
497 SocketSession* session = Session(session_id);
498 if (session != nullptr) {
499 session->Send(message);
500 }
501 }
502
CloseServerSocket(ServerSocket* server)503 void InspectorSocketServer::CloseServerSocket(ServerSocket* server) {
504 server->Close();
505 server->CloseUnix();
506 }
507
508 // InspectorSession tracking
SocketSession(InspectorSocketServer* server, int id, int server_port)509 SocketSession::SocketSession(InspectorSocketServer* server, int id,
510 int server_port)
511 : id_(id), server_port_(server_port) {}
512
Send(const std::string& message)513 void SocketSession::Send(const std::string& message) {
514 ws_socket_->Write(message.data(), message.length());
515 }
516
OnHttpGet(const std::string& host, const std::string& path)517 void SocketSession::Delegate::OnHttpGet(const std::string& host,
518 const std::string& path) {
519 if (!server_->HandleGetRequest(session_id_, host, path))
520 Session()->ws_socket()->CancelHandshake();
521 }
522
OnSocketUpgrade(const std::string& host, const std::string& path, const std::string& ws_key)523 void SocketSession::Delegate::OnSocketUpgrade(const std::string& host,
524 const std::string& path,
525 const std::string& ws_key) {
526 std::string id = path.empty() ? path : path.substr(1);
527 server_->SessionStarted(session_id_, id, ws_key);
528 }
529
OnWsFrame(const std::vector<char>& data)530 void SocketSession::Delegate::OnWsFrame(const std::vector<char>& data) {
531 server_->MessageReceived(session_id_,
532 std::string(data.data(), data.size()));
533 }
534
535 // ServerSocket implementation
DetectPort(uv_loop_t *loop, int pid)536 int ServerSocket::DetectPort(uv_loop_t *loop, int pid) {
537 sockaddr_storage addr;
538 int len = sizeof(addr);
539 int err = uv_tcp_getsockname(&tcp_socket_,
540 reinterpret_cast<struct sockaddr*>(&addr), &len);
541 if (err != 0)
542 return err;
543 int port;
544 if (addr.ss_family == AF_INET6)
545 port = reinterpret_cast<const sockaddr_in6*>(&addr)->sin6_port;
546 else
547 port = reinterpret_cast<const sockaddr_in*>(&addr)->sin_port;
548 port_ = ntohs(port);
549 if (!unix_socket_on && pid != -1) {
550 auto unixDomainSocketPath = "jsvm_devtools_remote_" +
551 std::to_string(port_) + "_" + std::to_string(pid);
552 auto *abstract = new char[unixDomainSocketPath.length() + 2];
553 abstract[0] = '\0';
554 strcpy(abstract + 1, unixDomainSocketPath.c_str());
555 auto status = uv_pipe_init(loop, &unix_socket_, 0);
556 if (status == 0) {
557 status = uv_pipe_bind(&unix_socket_, abstract);
558 }
559 if (status == 0) {
560 constexpr int unixBacklog = 128;
561 status = uv_listen(reinterpret_cast<uv_stream_t*>(&unix_socket_), unixBacklog,
562 ServerSocket::UnixSocketConnectedCallback);
563 }
564 unix_socket_on = status == 0;
565 delete abstract;
566 }
567 return err;
568 }
569
Listen(sockaddr* addr, uv_loop_t* loop, int pid)570 int ServerSocket::Listen(sockaddr* addr, uv_loop_t* loop, int pid) {
571 uv_tcp_t* server = &tcp_socket_;
572 CHECK_EQ(0, uv_tcp_init(loop, server));
573 int err = uv_tcp_bind(server, addr, 0);
574 if (err == 0) {
575 // 511 is the value used by a 'net' module by default
576 err = uv_listen(reinterpret_cast<uv_stream_t*>(server), 511,
577 ServerSocket::SocketConnectedCallback);
578 }
579 if (err == 0) {
580 err = DetectPort(loop, pid);
581 }
582 return err;
583 }
584
585 // static
SocketConnectedCallback(uv_stream_t* tcp_socket, int status)586 void ServerSocket::SocketConnectedCallback(uv_stream_t* tcp_socket,
587 int status) {
588 if (status == 0) {
589 ServerSocket* server_socket = ServerSocket::FromTcpSocket(tcp_socket);
590 // Memory is freed when the socket closes.
591 server_socket->server_->Accept(server_socket->port_, tcp_socket);
592 }
593 }
594
UnixSocketConnectedCallback(uv_stream_t* unix_socket, int status)595 void ServerSocket::UnixSocketConnectedCallback(uv_stream_t* unix_socket,
596 int status) {
597 if (status == 0) {
598 (void)unix_socket;
599 }
600 }
601 } // namespace inspector
602 } // namespace node
603