xref: /llvm-project/lldb/source/Host/common/UDPSocket.cpp (revision c1dff7152592f1beee9059ee8e2cb3cc68baea4d)
1 //===-- UDPSocket.cpp -----------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Host/common/UDPSocket.h"
10 
11 #include "lldb/Host/Config.h"
12 #include "lldb/Utility/LLDBLog.h"
13 #include "lldb/Utility/Log.h"
14 
15 #if LLDB_ENABLE_POSIX
16 #include <arpa/inet.h>
17 #include <sys/socket.h>
18 #endif
19 
20 #include <memory>
21 
22 using namespace lldb;
23 using namespace lldb_private;
24 
25 static const int kDomain = AF_INET;
26 static const int kType = SOCK_DGRAM;
27 
28 static const char *g_not_supported_error = "Not supported";
29 
30 UDPSocket::UDPSocket(NativeSocket socket)
31     : Socket(ProtocolUdp, /*should_close=*/true) {
32   m_socket = socket;
33 }
34 
35 UDPSocket::UDPSocket(bool should_close) : Socket(ProtocolUdp, should_close) {}
36 
37 size_t UDPSocket::Send(const void *buf, const size_t num_bytes) {
38   return ::sendto(m_socket, static_cast<const char *>(buf), num_bytes, 0,
39                   m_sockaddr, m_sockaddr.GetLength());
40 }
41 
42 Status UDPSocket::Connect(llvm::StringRef name) {
43   return Status::FromErrorStringWithFormat("%s", g_not_supported_error);
44 }
45 
46 Status UDPSocket::Listen(llvm::StringRef name, int backlog) {
47   return Status::FromErrorStringWithFormat("%s", g_not_supported_error);
48 }
49 
50 llvm::Expected<std::unique_ptr<UDPSocket>>
51 UDPSocket::CreateConnected(llvm::StringRef name) {
52   std::unique_ptr<UDPSocket> socket;
53 
54   Log *log = GetLog(LLDBLog::Connection);
55   LLDB_LOG(log, "host/port = {0}", name);
56 
57   Status error;
58   llvm::Expected<HostAndPort> host_port = DecodeHostAndPort(name);
59   if (!host_port)
60     return host_port.takeError();
61 
62   // At this point we have setup the receive port, now we need to setup the UDP
63   // send socket
64 
65   struct addrinfo hints;
66   struct addrinfo *service_info_list = nullptr;
67 
68   ::memset(&hints, 0, sizeof(hints));
69   hints.ai_family = kDomain;
70   hints.ai_socktype = kType;
71   int err = ::getaddrinfo(host_port->hostname.c_str(), std::to_string(host_port->port).c_str(), &hints,
72                           &service_info_list);
73   if (err != 0) {
74     error = Status::FromErrorStringWithFormat(
75 #if defined(_WIN32) && defined(UNICODE)
76         "getaddrinfo(%s, %d, &hints, &info) returned error %i (%S)",
77 #else
78         "getaddrinfo(%s, %d, &hints, &info) returned error %i (%s)",
79 #endif
80         host_port->hostname.c_str(), host_port->port, err, gai_strerror(err));
81     return error.ToError();
82   }
83 
84   for (struct addrinfo *service_info_ptr = service_info_list;
85        service_info_ptr != nullptr;
86        service_info_ptr = service_info_ptr->ai_next) {
87     auto send_fd =
88         CreateSocket(service_info_ptr->ai_family, service_info_ptr->ai_socktype,
89                      service_info_ptr->ai_protocol, error);
90     if (error.Success()) {
91       socket.reset(new UDPSocket(send_fd));
92       socket->m_sockaddr = service_info_ptr;
93       break;
94     } else
95       continue;
96   }
97 
98   ::freeaddrinfo(service_info_list);
99 
100   if (!socket)
101     return error.ToError();
102 
103   SocketAddress bind_addr;
104 
105   // Only bind to the loopback address if we are expecting a connection from
106   // localhost to avoid any firewall issues.
107   const bool bind_addr_success = (host_port->hostname == "127.0.0.1" || host_port->hostname == "localhost")
108                                      ? bind_addr.SetToLocalhost(kDomain, host_port->port)
109                                      : bind_addr.SetToAnyAddress(kDomain, host_port->port);
110 
111   if (!bind_addr_success) {
112     error = Status::FromErrorString("Failed to get hostspec to bind for");
113     return error.ToError();
114   }
115 
116   bind_addr.SetPort(0); // Let the source port # be determined dynamically
117 
118   err = ::bind(socket->GetNativeSocket(), bind_addr, bind_addr.GetLength());
119 
120   struct sockaddr_in source_info;
121   socklen_t address_len = sizeof (struct sockaddr_in);
122   err = ::getsockname(socket->GetNativeSocket(),
123                       (struct sockaddr *)&source_info, &address_len);
124 
125   return std::move(socket);
126 }
127 
128 std::string UDPSocket::GetRemoteConnectionURI() const {
129   if (m_socket != kInvalidSocketValue) {
130     return std::string(llvm::formatv(
131         "udp://[{0}]:{1}", m_sockaddr.GetIPAddress(), m_sockaddr.GetPort()));
132   }
133   return "";
134 }
135