Serenity Operating System
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}