Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Singleton.h>
8#include <AK/StringBuilder.h>
9#include <Kernel/API/Ioctl.h>
10#include <Kernel/API/POSIX/errno.h>
11#include <Kernel/Debug.h>
12#include <Kernel/FileSystem/OpenFileDescription.h>
13#include <Kernel/FileSystem/VirtualFileSystem.h>
14#include <Kernel/Locking/Mutex.h>
15#include <Kernel/Locking/MutexProtected.h>
16#include <Kernel/Net/LocalSocket.h>
17#include <Kernel/Process.h>
18#include <Kernel/StdLib.h>
19#include <Kernel/UnixTypes.h>
20
21namespace Kernel {
22
23static Singleton<MutexProtected<LocalSocket::List>> s_list;
24
25static MutexProtected<LocalSocket::List>& all_sockets()
26{
27 return *s_list;
28}
29
30void LocalSocket::for_each(Function<void(LocalSocket const&)> callback)
31{
32 all_sockets().for_each_shared([&](auto const& socket) {
33 callback(socket);
34 });
35}
36
37ErrorOr<void> LocalSocket::try_for_each(Function<ErrorOr<void>(LocalSocket const&)> callback)
38{
39 return all_sockets().with_shared([&](auto const& sockets) -> ErrorOr<void> {
40 for (auto& socket : sockets)
41 TRY(callback(socket));
42 return {};
43 });
44}
45
46ErrorOr<NonnullRefPtr<LocalSocket>> LocalSocket::try_create(int type)
47{
48 auto client_buffer = TRY(DoubleBuffer::try_create("LocalSocket: Client buffer"sv));
49 auto server_buffer = TRY(DoubleBuffer::try_create("LocalSocket: Server buffer"sv));
50 return adopt_nonnull_ref_or_enomem(new (nothrow) LocalSocket(type, move(client_buffer), move(server_buffer)));
51}
52
53ErrorOr<SocketPair> LocalSocket::try_create_connected_pair(int type)
54{
55 auto socket = TRY(LocalSocket::try_create(type));
56 auto description1 = TRY(OpenFileDescription::try_create(*socket));
57
58 TRY(socket->try_set_path("[socketpair]"sv));
59
60 socket->set_acceptor(Process::current());
61 socket->set_connected(true);
62 socket->set_connect_side_role(Role::Connected);
63 socket->set_role(Role::Accepted);
64
65 auto description2 = TRY(OpenFileDescription::try_create(*socket));
66
67 return SocketPair { move(description1), move(description2) };
68}
69
70LocalSocket::LocalSocket(int type, NonnullOwnPtr<DoubleBuffer> client_buffer, NonnullOwnPtr<DoubleBuffer> server_buffer)
71 : Socket(AF_LOCAL, type, 0)
72 , m_for_client(move(client_buffer))
73 , m_for_server(move(server_buffer))
74{
75 auto& current_process = Process::current();
76 auto current_process_credentials = current_process.credentials();
77 m_prebind_uid = current_process_credentials->euid();
78 m_prebind_gid = current_process_credentials->egid();
79 m_prebind_mode = 0666;
80
81 m_for_client->set_unblock_callback([this]() {
82 evaluate_block_conditions();
83 });
84 m_for_server->set_unblock_callback([this]() {
85 evaluate_block_conditions();
86 });
87
88 all_sockets().with_exclusive([&](auto& list) {
89 list.append(*this);
90 });
91
92 dbgln_if(LOCAL_SOCKET_DEBUG, "LocalSocket({}) created with type={}", this, type);
93}
94
95LocalSocket::~LocalSocket()
96{
97 all_sockets().with_exclusive([&](auto& list) {
98 list.remove(*this);
99 });
100}
101
102void LocalSocket::get_local_address(sockaddr* address, socklen_t* address_size)
103{
104 auto& address_un = *reinterpret_cast<sockaddr_un*>(address);
105 address_un = {
106 .sun_family = AF_UNIX,
107 .sun_path = {},
108 };
109
110 if (!m_path || m_path->is_empty()) {
111 *address_size = sizeof(address_un.sun_family);
112 return;
113 }
114
115 size_t bytes_to_copy = min(m_path->length() + 1, min(static_cast<size_t>(*address_size), sizeof(address_un.sun_path)));
116 memcpy(address_un.sun_path, m_path->characters(), bytes_to_copy);
117 *address_size = sizeof(address_un.sun_family) + bytes_to_copy;
118}
119
120void LocalSocket::get_peer_address(sockaddr* address, socklen_t* address_size)
121{
122 get_local_address(address, address_size);
123}
124
125ErrorOr<void> LocalSocket::bind(Credentials const& credentials, Userspace<sockaddr const*> user_address, socklen_t address_size)
126{
127 VERIFY(setup_state() == SetupState::Unstarted);
128 if (address_size > sizeof(sockaddr_un))
129 return set_so_error(EINVAL);
130
131 sockaddr_un address = {};
132 SOCKET_TRY(copy_from_user(&address, user_address, address_size));
133
134 if (address.sun_family != AF_LOCAL)
135 return set_so_error(EINVAL);
136
137 auto path = SOCKET_TRY(KString::try_create(StringView { address.sun_path, strnlen(address.sun_path, sizeof(address.sun_path)) }));
138 dbgln_if(LOCAL_SOCKET_DEBUG, "LocalSocket({}) bind({})", this, path);
139
140 mode_t mode = S_IFSOCK | (m_prebind_mode & 0777);
141 UidAndGid owner { m_prebind_uid, m_prebind_gid };
142 auto result = VirtualFileSystem::the().open(credentials, path->view(), O_CREAT | O_EXCL | O_NOFOLLOW_NOERROR, mode, Process::current().current_directory(), owner);
143 if (result.is_error()) {
144 if (result.error().code() == EEXIST)
145 return set_so_error(EADDRINUSE);
146 return result.release_error();
147 }
148
149 auto file = move(result.value());
150 auto inode = file->inode();
151
152 VERIFY(inode);
153 if (!inode->bind_socket(*this))
154 return set_so_error(EADDRINUSE);
155
156 m_inode = inode;
157
158 m_path = move(path);
159 m_bound = true;
160 return {};
161}
162
163ErrorOr<void> LocalSocket::connect(Credentials const& credentials, OpenFileDescription& description, Userspace<sockaddr const*> user_address, socklen_t address_size)
164{
165 VERIFY(!m_bound);
166
167 if (address_size > sizeof(sockaddr_un))
168 return set_so_error(EINVAL);
169
170 sockaddr_un address = {};
171 SOCKET_TRY(copy_from_user(&address, user_address, address_size));
172
173 if (address.sun_family != AF_LOCAL)
174 return set_so_error(EINVAL);
175
176 if (is_connected())
177 return set_so_error(EISCONN);
178
179 auto path = SOCKET_TRY(KString::try_create(StringView { address.sun_path, strnlen(address.sun_path, sizeof(address.sun_path)) }));
180 dbgln_if(LOCAL_SOCKET_DEBUG, "LocalSocket({}) connect({})", this, *path);
181
182 auto file = SOCKET_TRY(VirtualFileSystem::the().open(credentials, path->view(), O_RDWR, 0, Process::current().current_directory()));
183 auto inode = file->inode();
184 m_inode = inode;
185
186 VERIFY(inode);
187
188 auto peer = inode->bound_socket();
189 if (!peer)
190 return set_so_error(ECONNREFUSED);
191
192 m_path = move(path);
193
194 VERIFY(m_connect_side_fd == &description);
195 set_connect_side_role(Role::Connecting);
196
197 auto result = peer->queue_connection_from(*this);
198 if (result.is_error()) {
199 set_connect_side_role(Role::None);
200 return result;
201 }
202
203 if (is_connected()) {
204 set_connect_side_role(Role::Connected);
205 return {};
206 }
207
208 auto unblock_flags = Thread::OpenFileDescriptionBlocker::BlockFlags::None;
209 if (Thread::current()->block<Thread::ConnectBlocker>({}, description, unblock_flags).was_interrupted()) {
210 set_connect_side_role(Role::None);
211 return set_so_error(EINTR);
212 }
213
214 dbgln_if(LOCAL_SOCKET_DEBUG, "LocalSocket({}) connect({}) status is {}", this, *m_path, to_string(setup_state()));
215
216 if (!has_flag(unblock_flags, Thread::OpenFileDescriptionBlocker::BlockFlags::Connect)) {
217 set_connect_side_role(Role::None);
218 return set_so_error(ECONNREFUSED);
219 }
220 set_connect_side_role(Role::Connected);
221 return {};
222}
223
224ErrorOr<void> LocalSocket::listen(size_t backlog)
225{
226 MutexLocker locker(mutex());
227 if (type() != SOCK_STREAM)
228 return set_so_error(EOPNOTSUPP);
229 set_backlog(backlog);
230 auto previous_role = m_role;
231 set_role(Role::Listener);
232 set_connect_side_role(Role::Listener, previous_role != m_role);
233
234 dbgln_if(LOCAL_SOCKET_DEBUG, "LocalSocket({}) listening with backlog={}", this, backlog);
235
236 return {};
237}
238
239ErrorOr<void> LocalSocket::attach(OpenFileDescription& description)
240{
241 VERIFY(!m_accept_side_fd_open);
242 if (m_connect_side_role == Role::None) {
243 VERIFY(m_connect_side_fd == nullptr);
244 m_connect_side_fd = &description;
245 } else {
246 VERIFY(m_connect_side_fd != &description);
247 m_accept_side_fd_open = true;
248 }
249
250 evaluate_block_conditions();
251 return {};
252}
253
254void LocalSocket::detach(OpenFileDescription& description)
255{
256 if (m_connect_side_fd == &description) {
257 m_connect_side_fd = nullptr;
258 } else {
259 VERIFY(m_accept_side_fd_open);
260 m_accept_side_fd_open = false;
261
262 if (m_bound) {
263 if (m_inode)
264 m_inode->unbind_socket();
265 }
266 }
267
268 evaluate_block_conditions();
269}
270
271bool LocalSocket::can_read(OpenFileDescription const& description, u64) const
272{
273 auto role = this->role(description);
274 if (role == Role::Listener)
275 return can_accept();
276 if (role == Role::Accepted)
277 return !has_attached_peer(description) || !m_for_server->is_empty();
278 if (role == Role::Connected)
279 return !has_attached_peer(description) || !m_for_client->is_empty();
280 return false;
281}
282
283bool LocalSocket::has_attached_peer(OpenFileDescription const& description) const
284{
285 auto role = this->role(description);
286 if (role == Role::Accepted)
287 return m_connect_side_fd != nullptr;
288 if (role == Role::Connected)
289 return m_accept_side_fd_open;
290 return false;
291}
292
293bool LocalSocket::can_write(OpenFileDescription const& description, u64) const
294{
295 auto role = this->role(description);
296 if (role == Role::Accepted)
297 return !has_attached_peer(description) || m_for_client->space_for_writing();
298 if (role == Role::Connected)
299 return !has_attached_peer(description) || m_for_server->space_for_writing();
300 return false;
301}
302
303ErrorOr<size_t> LocalSocket::sendto(OpenFileDescription& description, UserOrKernelBuffer const& data, size_t data_size, int, Userspace<sockaddr const*>, socklen_t)
304{
305 if (!has_attached_peer(description))
306 return set_so_error(EPIPE);
307 auto* socket_buffer = send_buffer_for(description);
308 if (!socket_buffer)
309 return set_so_error(EINVAL);
310 auto nwritten_or_error = socket_buffer->write(data, data_size);
311 if (!nwritten_or_error.is_error() && nwritten_or_error.value() > 0)
312 Thread::current()->did_unix_socket_write(nwritten_or_error.value());
313 return nwritten_or_error;
314}
315
316DoubleBuffer* LocalSocket::receive_buffer_for(OpenFileDescription& description)
317{
318 auto role = this->role(description);
319 if (role == Role::Accepted)
320 return m_for_server.ptr();
321 if (role == Role::Connected)
322 return m_for_client.ptr();
323 return nullptr;
324}
325
326DoubleBuffer* LocalSocket::send_buffer_for(OpenFileDescription& description)
327{
328 auto role = this->role(description);
329 if (role == Role::Connected)
330 return m_for_server.ptr();
331 if (role == Role::Accepted)
332 return m_for_client.ptr();
333 return nullptr;
334}
335
336ErrorOr<size_t> LocalSocket::recvfrom(OpenFileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_size, int, Userspace<sockaddr*>, Userspace<socklen_t*>, Time&, bool blocking)
337{
338 auto* socket_buffer = receive_buffer_for(description);
339 if (!socket_buffer)
340 return set_so_error(EINVAL);
341 if (!blocking) {
342 if (socket_buffer->is_empty()) {
343 if (!has_attached_peer(description))
344 return 0;
345 return set_so_error(EAGAIN);
346 }
347 } else if (!can_read(description, 0)) {
348 auto unblock_flags = Thread::OpenFileDescriptionBlocker::BlockFlags::None;
349 if (Thread::current()->block<Thread::ReadBlocker>({}, description, unblock_flags).was_interrupted())
350 return set_so_error(EINTR);
351 }
352 if (!has_attached_peer(description) && socket_buffer->is_empty())
353 return 0;
354 VERIFY(!socket_buffer->is_empty());
355 auto nread_or_error = socket_buffer->read(buffer, buffer_size);
356 if (!nread_or_error.is_error() && nread_or_error.value() > 0)
357 Thread::current()->did_unix_socket_read(nread_or_error.value());
358 return nread_or_error;
359}
360
361StringView LocalSocket::socket_path() const
362{
363 if (!m_path)
364 return {};
365 return m_path->view();
366}
367
368ErrorOr<NonnullOwnPtr<KString>> LocalSocket::pseudo_path(OpenFileDescription const& description) const
369{
370 StringBuilder builder;
371 TRY(builder.try_append("socket:"sv));
372 TRY(builder.try_append(socket_path()));
373
374 switch (role(description)) {
375 case Role::Listener:
376 TRY(builder.try_append(" (listening)"sv));
377 break;
378 case Role::Accepted:
379 TRY(builder.try_appendff(" (accepted from pid {})", origin_pid()));
380 break;
381 case Role::Connected:
382 TRY(builder.try_appendff(" (connected to pid {})", acceptor_pid()));
383 break;
384 case Role::Connecting:
385 TRY(builder.try_append(" (connecting)"sv));
386 break;
387 default:
388 break;
389 }
390
391 return KString::try_create(builder.string_view());
392}
393
394ErrorOr<void> LocalSocket::getsockopt(OpenFileDescription& description, int level, int option, Userspace<void*> value, Userspace<socklen_t*> value_size)
395{
396 if (level != SOL_SOCKET)
397 return Socket::getsockopt(description, level, option, value, value_size);
398
399 MutexLocker locker(mutex());
400
401 socklen_t size;
402 TRY(copy_from_user(&size, value_size.unsafe_userspace_ptr()));
403
404 switch (option) {
405 case SO_SNDBUF:
406 return ENOTSUP;
407 case SO_RCVBUF:
408 return ENOTSUP;
409 case SO_PEERCRED: {
410 if (size < sizeof(ucred))
411 return EINVAL;
412 switch (role(description)) {
413 case Role::Accepted:
414 TRY(copy_to_user(static_ptr_cast<ucred*>(value), &m_origin));
415 size = sizeof(ucred);
416 TRY(copy_to_user(value_size, &size));
417 return {};
418 case Role::Connected:
419 TRY(copy_to_user(static_ptr_cast<ucred*>(value), &m_acceptor));
420 size = sizeof(ucred);
421 TRY(copy_to_user(value_size, &size));
422 return {};
423 case Role::Connecting:
424 return ENOTCONN;
425 default:
426 return EINVAL;
427 }
428 VERIFY_NOT_REACHED();
429 }
430 default:
431 return Socket::getsockopt(description, level, option, value, value_size);
432 }
433}
434
435ErrorOr<void> LocalSocket::ioctl(OpenFileDescription& description, unsigned request, Userspace<void*> arg)
436{
437 switch (request) {
438 case FIONREAD: {
439 int readable = receive_buffer_for(description)->immediately_readable();
440 return copy_to_user(static_ptr_cast<int*>(arg), &readable);
441 }
442 }
443
444 return EINVAL;
445}
446
447ErrorOr<void> LocalSocket::chmod(Credentials const& credentials, OpenFileDescription& description, mode_t mode)
448{
449 if (m_inode) {
450 if (auto custody = description.custody())
451 return VirtualFileSystem::the().chmod(credentials, *custody, mode);
452 VERIFY_NOT_REACHED();
453 }
454
455 m_prebind_mode = mode & 0777;
456 return {};
457}
458
459ErrorOr<void> LocalSocket::chown(Credentials const& credentials, OpenFileDescription& description, UserID uid, GroupID gid)
460{
461 if (m_inode) {
462 if (auto custody = description.custody())
463 return VirtualFileSystem::the().chown(credentials, *custody, uid, gid);
464 VERIFY_NOT_REACHED();
465 }
466
467 if (!credentials.is_superuser() && (credentials.euid() != uid || !credentials.in_group(gid)))
468 return set_so_error(EPERM);
469
470 m_prebind_uid = uid;
471 m_prebind_gid = gid;
472 return {};
473}
474
475Vector<NonnullRefPtr<OpenFileDescription>>& LocalSocket::recvfd_queue_for(OpenFileDescription const& description)
476{
477 VERIFY(mutex().is_exclusively_locked_by_current_thread());
478 auto role = this->role(description);
479 if (role == Role::Connected)
480 return m_fds_for_client;
481 if (role == Role::Accepted)
482 return m_fds_for_server;
483 VERIFY_NOT_REACHED();
484}
485
486Vector<NonnullRefPtr<OpenFileDescription>>& LocalSocket::sendfd_queue_for(OpenFileDescription const& description)
487{
488 VERIFY(mutex().is_exclusively_locked_by_current_thread());
489 auto role = this->role(description);
490 if (role == Role::Connected)
491 return m_fds_for_server;
492 if (role == Role::Accepted)
493 return m_fds_for_client;
494 VERIFY_NOT_REACHED();
495}
496
497ErrorOr<void> LocalSocket::sendfd(OpenFileDescription const& socket_description, NonnullRefPtr<OpenFileDescription> passing_description)
498{
499 MutexLocker locker(mutex());
500 auto role = this->role(socket_description);
501 if (role != Role::Connected && role != Role::Accepted)
502 return set_so_error(EINVAL);
503 auto& queue = sendfd_queue_for(socket_description);
504 // FIXME: Figure out how we should limit this properly.
505 if (queue.size() > 128)
506 return set_so_error(EBUSY);
507 SOCKET_TRY(queue.try_append(move(passing_description)));
508 return {};
509}
510
511ErrorOr<NonnullRefPtr<OpenFileDescription>> LocalSocket::recvfd(OpenFileDescription const& socket_description)
512{
513 MutexLocker locker(mutex());
514 auto role = this->role(socket_description);
515 if (role != Role::Connected && role != Role::Accepted)
516 return set_so_error(EINVAL);
517 auto& queue = recvfd_queue_for(socket_description);
518 if (queue.is_empty()) {
519 // FIXME: Figure out the perfect error code for this.
520 return set_so_error(EAGAIN);
521 }
522 return queue.take_first();
523}
524
525ErrorOr<Vector<NonnullRefPtr<OpenFileDescription>>> LocalSocket::recvfds(OpenFileDescription const& socket_description, int n)
526{
527 MutexLocker locker(mutex());
528 Vector<NonnullRefPtr<OpenFileDescription>> fds;
529
530 auto role = this->role(socket_description);
531 if (role != Role::Connected && role != Role::Accepted)
532 return set_so_error(EINVAL);
533 auto& queue = recvfd_queue_for(socket_description);
534
535 for (int i = 0; i < n; ++i) {
536 if (queue.is_empty())
537 break;
538
539 fds.append(queue.take_first());
540 }
541
542 return fds;
543}
544
545ErrorOr<void> LocalSocket::try_set_path(StringView path)
546{
547 m_path = TRY(KString::try_create(path));
548 return {};
549}
550
551}