Serenity Operating System
at master 422 lines 12 kB view raw
1/* 2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <LibCore/Socket.h> 9#include <LibCore/System.h> 10 11namespace Core { 12 13ErrorOr<int> Socket::create_fd(SocketDomain domain, SocketType type) 14{ 15 int socket_domain; 16 switch (domain) { 17 case SocketDomain::Inet: 18 socket_domain = AF_INET; 19 break; 20 case SocketDomain::Local: 21 socket_domain = AF_LOCAL; 22 break; 23 default: 24 VERIFY_NOT_REACHED(); 25 } 26 27 int socket_type; 28 switch (type) { 29 case SocketType::Stream: 30 socket_type = SOCK_STREAM; 31 break; 32 case SocketType::Datagram: 33 socket_type = SOCK_DGRAM; 34 break; 35 default: 36 VERIFY_NOT_REACHED(); 37 } 38 39 // Let's have a safe default of CLOEXEC. :^) 40#ifdef SOCK_CLOEXEC 41 return System::socket(socket_domain, socket_type | SOCK_CLOEXEC, 0); 42#else 43 auto fd = TRY(System::socket(socket_domain, socket_type, 0)); 44 TRY(System::fcntl(fd, F_SETFD, FD_CLOEXEC)); 45 return fd; 46#endif 47} 48 49ErrorOr<IPv4Address> Socket::resolve_host(DeprecatedString const& host, SocketType type) 50{ 51 int socket_type; 52 switch (type) { 53 case SocketType::Stream: 54 socket_type = SOCK_STREAM; 55 break; 56 case SocketType::Datagram: 57 socket_type = SOCK_DGRAM; 58 break; 59 default: 60 VERIFY_NOT_REACHED(); 61 } 62 63 struct addrinfo hints = {}; 64 hints.ai_family = AF_UNSPEC; 65 hints.ai_socktype = socket_type; 66 hints.ai_flags = 0; 67 hints.ai_protocol = 0; 68 69 auto const results = TRY(Core::System::getaddrinfo(host.characters(), nullptr, hints)); 70 71 for (auto const& result : results.addresses()) { 72 if (result.ai_family == AF_INET) { 73 auto* socket_address = bit_cast<struct sockaddr_in*>(result.ai_addr); 74 NetworkOrdered<u32> const network_ordered_address { socket_address->sin_addr.s_addr }; 75 return IPv4Address { network_ordered_address }; 76 } 77 } 78 79 return Error::from_string_literal("Could not resolve to IPv4 address"); 80} 81 82ErrorOr<void> Socket::connect_local(int fd, DeprecatedString const& path) 83{ 84 auto address = SocketAddress::local(path); 85 auto maybe_sockaddr = address.to_sockaddr_un(); 86 if (!maybe_sockaddr.has_value()) { 87 dbgln("Core::Socket::connect_local: Could not obtain a sockaddr_un"); 88 return Error::from_errno(EINVAL); 89 } 90 91 auto addr = maybe_sockaddr.release_value(); 92 return System::connect(fd, bit_cast<struct sockaddr*>(&addr), sizeof(addr)); 93} 94 95ErrorOr<void> Socket::connect_inet(int fd, SocketAddress const& address) 96{ 97 auto addr = address.to_sockaddr_in(); 98 return System::connect(fd, bit_cast<struct sockaddr*>(&addr), sizeof(addr)); 99} 100 101ErrorOr<Bytes> PosixSocketHelper::read(Bytes buffer, int flags) 102{ 103 if (!is_open()) { 104 return Error::from_errno(ENOTCONN); 105 } 106 107 ssize_t nread = TRY(System::recv(m_fd, buffer.data(), buffer.size(), flags)); 108 m_last_read_was_eof = nread == 0; 109 110 // If a socket read is EOF, then no more data can be read from it because 111 // the protocol has disconnected. In this case, we can just disable the 112 // notifier if we have one. 113 if (m_last_read_was_eof && m_notifier) 114 m_notifier->set_enabled(false); 115 116 return buffer.trim(nread); 117} 118 119ErrorOr<size_t> PosixSocketHelper::write(ReadonlyBytes buffer, int flags) 120{ 121 if (!is_open()) { 122 return Error::from_errno(ENOTCONN); 123 } 124 125 return TRY(System::send(m_fd, buffer.data(), buffer.size(), flags)); 126} 127 128void PosixSocketHelper::close() 129{ 130 if (!is_open()) { 131 return; 132 } 133 134 if (m_notifier) 135 m_notifier->set_enabled(false); 136 137 ErrorOr<void> result; 138 do { 139 result = System::close(m_fd); 140 } while (result.is_error() && result.error().code() == EINTR); 141 142 VERIFY(!result.is_error()); 143 m_fd = -1; 144} 145 146ErrorOr<bool> PosixSocketHelper::can_read_without_blocking(int timeout) const 147{ 148 struct pollfd the_fd = { .fd = m_fd, .events = POLLIN, .revents = 0 }; 149 150 ErrorOr<int> result { 0 }; 151 do { 152 result = Core::System::poll({ &the_fd, 1 }, timeout); 153 } while (result.is_error() && result.error().code() == EINTR); 154 155 if (result.is_error()) 156 return result.release_error(); 157 158 return (the_fd.revents & POLLIN) > 0; 159} 160 161ErrorOr<void> PosixSocketHelper::set_blocking(bool enabled) 162{ 163 int value = enabled ? 0 : 1; 164 return System::ioctl(m_fd, FIONBIO, &value); 165} 166 167ErrorOr<void> PosixSocketHelper::set_close_on_exec(bool enabled) 168{ 169 int flags = TRY(System::fcntl(m_fd, F_GETFD)); 170 171 if (enabled) 172 flags |= FD_CLOEXEC; 173 else 174 flags &= ~FD_CLOEXEC; 175 176 TRY(System::fcntl(m_fd, F_SETFD, flags)); 177 return {}; 178} 179 180ErrorOr<void> PosixSocketHelper::set_receive_timeout(Time timeout) 181{ 182 auto timeout_spec = timeout.to_timespec(); 183 return System::setsockopt(m_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout_spec, sizeof(timeout_spec)); 184} 185 186void PosixSocketHelper::setup_notifier() 187{ 188 if (!m_notifier) 189 m_notifier = Core::Notifier::construct(m_fd, Core::Notifier::Read); 190} 191 192ErrorOr<NonnullOwnPtr<TCPSocket>> TCPSocket::connect(DeprecatedString const& host, u16 port) 193{ 194 auto ip_address = TRY(resolve_host(host, SocketType::Stream)); 195 return connect(SocketAddress { ip_address, port }); 196} 197 198ErrorOr<NonnullOwnPtr<TCPSocket>> TCPSocket::connect(SocketAddress const& address) 199{ 200 auto socket = TRY(adopt_nonnull_own_or_enomem(new (nothrow) TCPSocket())); 201 202 auto fd = TRY(create_fd(SocketDomain::Inet, SocketType::Stream)); 203 socket->m_helper.set_fd(fd); 204 205 TRY(connect_inet(fd, address)); 206 207 socket->setup_notifier(); 208 return socket; 209} 210 211ErrorOr<NonnullOwnPtr<TCPSocket>> TCPSocket::adopt_fd(int fd) 212{ 213 if (fd < 0) { 214 return Error::from_errno(EBADF); 215 } 216 217 auto socket = TRY(adopt_nonnull_own_or_enomem(new (nothrow) TCPSocket())); 218 socket->m_helper.set_fd(fd); 219 socket->setup_notifier(); 220 return socket; 221} 222 223ErrorOr<size_t> PosixSocketHelper::pending_bytes() const 224{ 225 if (!is_open()) { 226 return Error::from_errno(ENOTCONN); 227 } 228 229 int value; 230 TRY(System::ioctl(m_fd, FIONREAD, &value)); 231 return static_cast<size_t>(value); 232} 233 234ErrorOr<NonnullOwnPtr<UDPSocket>> UDPSocket::connect(DeprecatedString const& host, u16 port, Optional<Time> timeout) 235{ 236 auto ip_address = TRY(resolve_host(host, SocketType::Datagram)); 237 return connect(SocketAddress { ip_address, port }, timeout); 238} 239 240ErrorOr<NonnullOwnPtr<UDPSocket>> UDPSocket::connect(SocketAddress const& address, Optional<Time> timeout) 241{ 242 auto socket = TRY(adopt_nonnull_own_or_enomem(new (nothrow) UDPSocket())); 243 244 auto fd = TRY(create_fd(SocketDomain::Inet, SocketType::Datagram)); 245 socket->m_helper.set_fd(fd); 246 if (timeout.has_value()) { 247 TRY(socket->m_helper.set_receive_timeout(timeout.value())); 248 } 249 250 TRY(connect_inet(fd, address)); 251 252 socket->setup_notifier(); 253 return socket; 254} 255 256ErrorOr<NonnullOwnPtr<LocalSocket>> LocalSocket::connect(DeprecatedString const& path, PreventSIGPIPE prevent_sigpipe) 257{ 258 auto socket = TRY(adopt_nonnull_own_or_enomem(new (nothrow) LocalSocket(prevent_sigpipe))); 259 260 auto fd = TRY(create_fd(SocketDomain::Local, SocketType::Stream)); 261 socket->m_helper.set_fd(fd); 262 263 TRY(connect_local(fd, path)); 264 265 socket->setup_notifier(); 266 return socket; 267} 268 269ErrorOr<NonnullOwnPtr<LocalSocket>> LocalSocket::adopt_fd(int fd, PreventSIGPIPE prevent_sigpipe) 270{ 271 if (fd < 0) { 272 return Error::from_errno(EBADF); 273 } 274 275 auto socket = TRY(adopt_nonnull_own_or_enomem(new (nothrow) LocalSocket(prevent_sigpipe))); 276 socket->m_helper.set_fd(fd); 277 socket->setup_notifier(); 278 return socket; 279} 280 281ErrorOr<int> LocalSocket::receive_fd(int flags) 282{ 283#if defined(AK_OS_SERENITY) 284 return Core::System::recvfd(m_helper.fd(), flags); 285#elif defined(AK_OS_LINUX) || defined(AK_OS_BSD_GENERIC) 286 union { 287 struct cmsghdr cmsghdr; 288 char control[CMSG_SPACE(sizeof(int))]; 289 } cmsgu {}; 290 char c = 0; 291 struct iovec iov { 292 .iov_base = &c, 293 .iov_len = 1, 294 }; 295 struct msghdr msg = {}; 296 msg.msg_iov = &iov; 297 msg.msg_iovlen = 1; 298 msg.msg_control = cmsgu.control; 299 msg.msg_controllen = sizeof(cmsgu.control); 300 TRY(Core::System::recvmsg(m_helper.fd(), &msg, 0)); 301 302 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); 303 if (!cmsg || cmsg->cmsg_len != CMSG_LEN(sizeof(int))) 304 return Error::from_string_literal("Malformed message when receiving file descriptor"); 305 306 VERIFY(cmsg->cmsg_level == SOL_SOCKET); 307 VERIFY(cmsg->cmsg_type == SCM_RIGHTS); 308 int fd = *((int*)CMSG_DATA(cmsg)); 309 310 if (flags & O_CLOEXEC) { 311 auto fd_flags = TRY(Core::System::fcntl(fd, F_GETFD)); 312 TRY(Core::System::fcntl(fd, F_SETFD, fd_flags | FD_CLOEXEC)); 313 } 314 315 return fd; 316#else 317 (void)flags; 318 return Error::from_string_literal("File descriptor passing not supported on this platform"); 319#endif 320} 321 322ErrorOr<void> LocalSocket::send_fd(int fd) 323{ 324#if defined(AK_OS_SERENITY) 325 return Core::System::sendfd(m_helper.fd(), fd); 326#elif defined(AK_OS_LINUX) || defined(AK_OS_BSD_GENERIC) 327 char c = 'F'; 328 struct iovec iov { 329 .iov_base = &c, 330 .iov_len = sizeof(c) 331 }; 332 333 union { 334 struct cmsghdr cmsghdr; 335 char control[CMSG_SPACE(sizeof(int))]; 336 } cmsgu {}; 337 338 struct msghdr msg = {}; 339 msg.msg_iov = &iov; 340 msg.msg_iovlen = 1; 341 msg.msg_control = cmsgu.control; 342 msg.msg_controllen = sizeof(cmsgu.control); 343 344 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); 345 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 346 cmsg->cmsg_level = SOL_SOCKET; 347 cmsg->cmsg_type = SCM_RIGHTS; 348 349 *((int*)CMSG_DATA(cmsg)) = fd; 350 351 TRY(Core::System::sendmsg(m_helper.fd(), &msg, 0)); 352 return {}; 353#else 354 (void)fd; 355 return Error::from_string_literal("File descriptor passing not supported on this platform"); 356#endif 357} 358 359ErrorOr<pid_t> LocalSocket::peer_pid() const 360{ 361#ifdef AK_OS_MACOS 362 pid_t pid; 363 socklen_t pid_size = sizeof(pid); 364#elif defined(AK_OS_FREEBSD) 365 struct xucred creds = {}; 366 socklen_t creds_size = sizeof(creds); 367#elif defined(AK_OS_OPENBSD) 368 struct sockpeercred creds = {}; 369 socklen_t creds_size = sizeof(creds); 370#elif defined(AK_OS_NETBSD) 371 struct sockcred creds = {}; 372 socklen_t creds_size = sizeof(creds); 373#elif defined(AK_OS_SOLARIS) 374 ucred_t* creds = NULL; 375 socklen_t creds_size = sizeof(creds); 376#else 377 struct ucred creds = {}; 378 socklen_t creds_size = sizeof(creds); 379#endif 380 381#ifdef AK_OS_MACOS 382 TRY(System::getsockopt(m_helper.fd(), SOL_LOCAL, LOCAL_PEERPID, &pid, &pid_size)); 383 return pid; 384#elif defined(AK_OS_FREEBSD) 385 TRY(System::getsockopt(m_helper.fd(), SOL_LOCAL, LOCAL_PEERCRED, &creds, &creds_size)); 386 return creds.cr_pid; 387#elif defined(AK_OS_NETBSD) 388 TRY(System::getsockopt(m_helper.fd(), SOL_SOCKET, SCM_CREDS, &creds, &creds_size)); 389 return creds.sc_pid; 390#elif defined(AK_OS_SOLARIS) 391 TRY(System::getsockopt(m_helper.fd(), SOL_SOCKET, SO_RECVUCRED, &creds, &creds_size)); 392 return ucred_getpid(creds); 393#else 394 TRY(System::getsockopt(m_helper.fd(), SOL_SOCKET, SO_PEERCRED, &creds, &creds_size)); 395 return creds.pid; 396#endif 397} 398 399ErrorOr<Bytes> LocalSocket::read_without_waiting(Bytes buffer) 400{ 401 return m_helper.read(buffer, MSG_DONTWAIT); 402} 403 404Optional<int> LocalSocket::fd() const 405{ 406 if (!is_open()) 407 return {}; 408 return m_helper.fd(); 409} 410 411ErrorOr<int> LocalSocket::release_fd() 412{ 413 if (!is_open()) { 414 return Error::from_errno(ENOTCONN); 415 } 416 417 auto fd = m_helper.fd(); 418 m_helper.set_fd(-1); 419 return fd; 420} 421 422}