238 lines · cpp
1//===-- DomainSocket.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-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Host/posix/DomainSocket.h"10#include "lldb/Utility/LLDBLog.h"11#ifdef __linux__12#include <lldb/Host/linux/AbstractSocket.h>13#endif14 15#include "llvm/Support/Errno.h"16#include "llvm/Support/Error.h"17#include "llvm/Support/FileSystem.h"18 19#include <cstddef>20#include <fcntl.h>21#include <memory>22#include <sys/socket.h>23#include <sys/un.h>24 25using namespace lldb;26using namespace lldb_private;27 28static const int kDomain = AF_UNIX;29static const int kType = SOCK_STREAM;30 31static bool SetSockAddr(llvm::StringRef name, const size_t name_offset,32 sockaddr_un *saddr_un, socklen_t &saddr_un_len) {33 if (name.size() + name_offset > sizeof(saddr_un->sun_path))34 return false;35 36 memset(saddr_un, 0, sizeof(*saddr_un));37 saddr_un->sun_family = kDomain;38 39 memcpy(saddr_un->sun_path + name_offset, name.data(), name.size());40 41 // For domain sockets we can use SUN_LEN in order to calculate size of42 // sockaddr_un, but for abstract sockets we have to calculate size manually43 // because of leading null symbol.44 if (name_offset == 0)45 saddr_un_len = SUN_LEN(saddr_un);46 else47 saddr_un_len =48 offsetof(struct sockaddr_un, sun_path) + name_offset + name.size();49 50#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \51 defined(__OpenBSD__)52 saddr_un->sun_len = saddr_un_len;53#endif54 55 return true;56}57 58DomainSocket::DomainSocket(bool should_close)59 : DomainSocket(kInvalidSocketValue, should_close) {}60 61DomainSocket::DomainSocket(NativeSocket socket, bool should_close)62 : Socket(ProtocolUnixDomain, should_close) {63 m_socket = socket;64}65 66DomainSocket::DomainSocket(SocketProtocol protocol)67 : Socket(protocol, /*should_close=*/true) {}68 69DomainSocket::DomainSocket(NativeSocket socket,70 const DomainSocket &listen_socket)71 : Socket(ProtocolUnixDomain, listen_socket.m_should_close_fd) {72 m_socket = socket;73}74 75DomainSocket::DomainSocket(SocketProtocol protocol, NativeSocket socket,76 bool should_close)77 : Socket(protocol, should_close) {78 m_socket = socket;79}80 81llvm::Expected<DomainSocket::Pair> DomainSocket::CreatePair() {82 int sockets[2];83 int type = SOCK_STREAM;84#ifdef SOCK_CLOEXEC85 type |= SOCK_CLOEXEC;86#endif87 if (socketpair(AF_UNIX, type, 0, sockets) == -1)88 return llvm::errorCodeToError(llvm::errnoAsErrorCode());89 90#ifndef SOCK_CLOEXEC91 for (int s : sockets) {92 int r = fcntl(s, F_SETFD, FD_CLOEXEC | fcntl(s, F_GETFD));93 assert(r == 0);94 (void)r;95 }96#endif97 98 return Pair(std::unique_ptr<DomainSocket>(99 new DomainSocket(ProtocolUnixDomain, sockets[0],100 /*should_close=*/true)),101 std::unique_ptr<DomainSocket>(102 new DomainSocket(ProtocolUnixDomain, sockets[1],103 /*should_close=*/true)));104}105 106Status DomainSocket::Connect(llvm::StringRef name) {107 sockaddr_un saddr_un;108 socklen_t saddr_un_len;109 if (!SetSockAddr(name, GetNameOffset(), &saddr_un, saddr_un_len))110 return Status::FromErrorString("Failed to set socket address");111 112 Status error;113 m_socket = CreateSocket(kDomain, kType, 0, error);114 if (error.Fail())115 return error;116 if (llvm::sys::RetryAfterSignal(-1, ::connect, GetNativeSocket(),117 (struct sockaddr *)&saddr_un,118 saddr_un_len) < 0)119 SetLastError(error);120 121 return error;122}123 124Status DomainSocket::Listen(llvm::StringRef name, int backlog) {125 sockaddr_un saddr_un;126 socklen_t saddr_un_len;127 if (!SetSockAddr(name, GetNameOffset(), &saddr_un, saddr_un_len))128 return Status::FromErrorString("Failed to set socket address");129 130 DeleteSocketFile(name);131 132 Status error;133 m_socket = CreateSocket(kDomain, kType, 0, error);134 if (error.Fail())135 return error;136 if (::bind(GetNativeSocket(), (struct sockaddr *)&saddr_un, saddr_un_len) ==137 0)138 if (::listen(GetNativeSocket(), backlog) == 0)139 return error;140 141 SetLastError(error);142 return error;143}144 145llvm::Expected<std::vector<MainLoopBase::ReadHandleUP>> DomainSocket::Accept(146 MainLoopBase &loop,147 std::function<void(std::unique_ptr<Socket> socket)> sock_cb) {148 // TODO: Refactor MainLoop to avoid the shared_ptr requirement.149 auto io_sp = std::make_shared<DomainSocket>(GetNativeSocket(), false);150 auto cb = [this, sock_cb](MainLoopBase &loop) {151 Log *log = GetLog(LLDBLog::Host);152 Status error;153 auto conn_fd = AcceptSocket(GetNativeSocket(), nullptr, nullptr, error);154 if (error.Fail()) {155 LLDB_LOG(log, "AcceptSocket({0}): {1}", GetNativeSocket(), error);156 return;157 }158 std::unique_ptr<DomainSocket> sock_up(new DomainSocket(conn_fd, *this));159 sock_cb(std::move(sock_up));160 };161 162 Status error;163 std::vector<MainLoopBase::ReadHandleUP> handles;164 handles.emplace_back(loop.RegisterReadObject(io_sp, cb, error));165 if (error.Fail())166 return error.ToError();167 return handles;168}169 170size_t DomainSocket::GetNameOffset() const { return 0; }171 172void DomainSocket::DeleteSocketFile(llvm::StringRef name) {173 llvm::sys::fs::remove(name);174}175 176std::string DomainSocket::GetSocketName() const {177 if (m_socket == kInvalidSocketValue)178 return "";179 180 struct sockaddr_un saddr_un;181 saddr_un.sun_family = AF_UNIX;182 socklen_t sock_addr_len = sizeof(struct sockaddr_un);183 if (::getpeername(m_socket, (struct sockaddr *)&saddr_un, &sock_addr_len) !=184 0)185 return "";186 187 if (sock_addr_len <= offsetof(struct sockaddr_un, sun_path))188 return ""; // Unnamed domain socket189 190 llvm::StringRef name(saddr_un.sun_path + GetNameOffset(),191 sock_addr_len - offsetof(struct sockaddr_un, sun_path) -192 GetNameOffset());193 name = name.rtrim('\0');194 195 return name.str();196}197 198std::string DomainSocket::GetRemoteConnectionURI() const {199 std::string name = GetSocketName();200 if (name.empty())201 return name;202 203 return llvm::formatv(204 "{0}://{1}",205 GetNameOffset() == 0 ? "unix-connect" : "unix-abstract-connect", name);206}207 208std::vector<std::string> DomainSocket::GetListeningConnectionURI() const {209 if (m_socket == kInvalidSocketValue)210 return {};211 212 struct sockaddr_un addr;213 memset(&addr, 0, sizeof(struct sockaddr_un));214 addr.sun_family = AF_UNIX;215 socklen_t addr_len = sizeof(struct sockaddr_un);216 if (::getsockname(m_socket, (struct sockaddr *)&addr, &addr_len) != 0)217 return {};218 219 return {llvm::formatv("unix-connect://{0}", addr.sun_path)};220}221 222llvm::Expected<std::unique_ptr<DomainSocket>>223DomainSocket::FromBoundNativeSocket(NativeSocket sockfd, bool should_close) {224 // Check if fd represents domain socket or abstract socket.225 struct sockaddr_un addr;226 socklen_t addr_len = sizeof(addr);227 if (getsockname(sockfd, (struct sockaddr *)&addr, &addr_len) == -1)228 return llvm::createStringError("not a socket or error occurred");229 if (addr.sun_family != AF_UNIX)230 return llvm::createStringError("Bad socket type");231#ifdef __linux__232 if (addr_len > offsetof(struct sockaddr_un, sun_path) &&233 addr.sun_path[0] == '\0')234 return std::make_unique<AbstractSocket>(sockfd, should_close);235#endif236 return std::make_unique<DomainSocket>(sockfd, should_close);237}238