Serenity Operating System
at master 128 lines 3.4 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <AK/IPv4Address.h> 9#include <AK/Types.h> 10#include <LibCore/Notifier.h> 11#include <LibCore/Socket.h> 12#include <LibCore/System.h> 13#include <LibCore/TCPServer.h> 14 15namespace Core { 16 17ErrorOr<NonnullRefPtr<TCPServer>> TCPServer::try_create(Object* parent) 18{ 19#ifdef SOCK_NONBLOCK 20 int fd = TRY(Core::System::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); 21#else 22 int fd = TRY(Core::System::socket(AF_INET, SOCK_STREAM, 0)); 23 int option = 1; 24 TRY(Core::System::ioctl(fd, FIONBIO, &option)); 25 TRY(Core::System::fcntl(fd, F_SETFD, FD_CLOEXEC)); 26#endif 27 28 return adopt_nonnull_ref_or_enomem(new (nothrow) TCPServer(fd, parent)); 29} 30 31TCPServer::TCPServer(int fd, Object* parent) 32 : Object(parent) 33 , m_fd(fd) 34{ 35 VERIFY(m_fd >= 0); 36} 37 38TCPServer::~TCPServer() 39{ 40 MUST(Core::System::close(m_fd)); 41} 42 43ErrorOr<void> TCPServer::listen(IPv4Address const& address, u16 port, AllowAddressReuse allow_address_reuse) 44{ 45 if (m_listening) 46 return Error::from_errno(EADDRINUSE); 47 48 auto socket_address = SocketAddress(address, port); 49 auto in = socket_address.to_sockaddr_in(); 50 51 if (allow_address_reuse == AllowAddressReuse::Yes) { 52 int option = 1; 53 TRY(Core::System::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option))); 54 } 55 56 TRY(Core::System::bind(m_fd, (sockaddr const*)&in, sizeof(in))); 57 TRY(Core::System::listen(m_fd, 5)); 58 m_listening = true; 59 60 m_notifier = Notifier::construct(m_fd, Notifier::Event::Read, this); 61 m_notifier->on_ready_to_read = [this] { 62 if (on_ready_to_accept) 63 on_ready_to_accept(); 64 }; 65 return {}; 66} 67 68ErrorOr<void> TCPServer::set_blocking(bool blocking) 69{ 70 int flags = TRY(Core::System::fcntl(m_fd, F_GETFL, 0)); 71 if (blocking) 72 TRY(Core::System::fcntl(m_fd, F_SETFL, flags & ~O_NONBLOCK)); 73 else 74 TRY(Core::System::fcntl(m_fd, F_SETFL, flags | O_NONBLOCK)); 75 return {}; 76} 77 78ErrorOr<NonnullOwnPtr<TCPSocket>> TCPServer::accept() 79{ 80 VERIFY(m_listening); 81 sockaddr_in in; 82 socklen_t in_size = sizeof(in); 83#ifndef AK_OS_MACOS 84 int accepted_fd = TRY(Core::System::accept4(m_fd, (sockaddr*)&in, &in_size, SOCK_NONBLOCK | SOCK_CLOEXEC)); 85#else 86 int accepted_fd = TRY(Core::System::accept(m_fd, (sockaddr*)&in, &in_size)); 87#endif 88 89 auto socket = TRY(TCPSocket::adopt_fd(accepted_fd)); 90 91#ifdef AK_OS_MACOS 92 // FIXME: Ideally, we should let the caller decide whether it wants the 93 // socket to be nonblocking or not, but there are currently places 94 // which depend on this. 95 TRY(socket->set_blocking(false)); 96 TRY(socket->set_close_on_exec(true)); 97#endif 98 99 return socket; 100} 101 102Optional<IPv4Address> TCPServer::local_address() const 103{ 104 if (m_fd == -1) 105 return {}; 106 107 sockaddr_in address; 108 socklen_t len = sizeof(address); 109 if (getsockname(m_fd, (sockaddr*)&address, &len) != 0) 110 return {}; 111 112 return IPv4Address(address.sin_addr.s_addr); 113} 114 115Optional<u16> TCPServer::local_port() const 116{ 117 if (m_fd == -1) 118 return {}; 119 120 sockaddr_in address; 121 socklen_t len = sizeof(address); 122 if (getsockname(m_fd, (sockaddr*)&address, &len) != 0) 123 return {}; 124 125 return ntohs(address.sin_port); 126} 127 128}