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