Serenity Operating System
at hosted 400 lines 12 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/StringBuilder.h> 28#include <Kernel/FileSystem/FileDescription.h> 29#include <Kernel/FileSystem/VirtualFileSystem.h> 30#include <Kernel/Net/LocalSocket.h> 31#include <Kernel/Process.h> 32#include <Kernel/UnixTypes.h> 33#include <LibBareMetal/StdLib.h> 34#include <LibC/errno_numbers.h> 35 36//#define DEBUG_LOCAL_SOCKET 37 38namespace Kernel { 39 40Lockable<InlineLinkedList<LocalSocket>>& LocalSocket::all_sockets() 41{ 42 static Lockable<InlineLinkedList<LocalSocket>>* s_list; 43 if (!s_list) 44 s_list = new Lockable<InlineLinkedList<LocalSocket>>(); 45 return *s_list; 46} 47 48void LocalSocket::for_each(Function<void(LocalSocket&)> callback) 49{ 50 LOCKER(all_sockets().lock()); 51 for (auto& socket : all_sockets().resource()) 52 callback(socket); 53} 54 55KResultOr<NonnullRefPtr<Socket>> LocalSocket::create(int type) 56{ 57 return adopt(*new LocalSocket(type)); 58} 59 60LocalSocket::LocalSocket(int type) 61 : Socket(AF_LOCAL, type, 0) 62{ 63 LOCKER(all_sockets().lock()); 64 all_sockets().resource().append(this); 65 66 m_prebind_uid = Process::current->uid(); 67 m_prebind_gid = Process::current->gid(); 68 m_prebind_mode = 0666; 69 70#ifdef DEBUG_LOCAL_SOCKET 71 dbg() << "LocalSocket{" << this << "} created with type=" << type; 72#endif 73} 74 75LocalSocket::~LocalSocket() 76{ 77 LOCKER(all_sockets().lock()); 78 all_sockets().resource().remove(this); 79} 80 81void LocalSocket::get_local_address(sockaddr* address, socklen_t* address_size) 82{ 83 size_t bytes_to_copy = min(static_cast<size_t>(*address_size), sizeof(sockaddr_un)); 84 memcpy(address, &m_address, bytes_to_copy); 85 *address_size = sizeof(sockaddr_un); 86} 87 88void LocalSocket::get_peer_address(sockaddr* address, socklen_t* address_size) 89{ 90 get_local_address(address, address_size); 91} 92 93KResult LocalSocket::bind(const sockaddr* user_address, socklen_t address_size) 94{ 95 ASSERT(setup_state() == SetupState::Unstarted); 96 if (address_size != sizeof(sockaddr_un)) 97 return KResult(-EINVAL); 98 99 sockaddr_un address; 100 copy_from_user(&address, user_address, sizeof(sockaddr_un)); 101 102 if (address.sun_family != AF_LOCAL) 103 return KResult(-EINVAL); 104 105 auto path = String(address.sun_path, strnlen(address.sun_path, sizeof(address.sun_path))); 106 107#ifdef DEBUG_LOCAL_SOCKET 108 dbg() << "LocalSocket{" << this << "} bind(" << safe_address << ")"; 109#endif 110 111 mode_t mode = S_IFSOCK | (m_prebind_mode & 04777); 112 UidAndGid owner { m_prebind_uid, m_prebind_gid }; 113 auto result = VFS::the().open(path, O_CREAT | O_EXCL | O_NOFOLLOW_NOERROR, mode, Process::current->current_directory(), owner); 114 if (result.is_error()) { 115 if (result.error() == -EEXIST) 116 return KResult(-EADDRINUSE); 117 return result.error(); 118 } 119 120 auto file = move(result.value()); 121 122 ASSERT(file->inode()); 123 if (!file->inode()->bind_socket(*this)) 124 return KResult(-EADDRINUSE); 125 126 m_file = move(file); 127 128 m_address = address; 129 m_bound = true; 130 return KSuccess; 131} 132 133KResult LocalSocket::connect(FileDescription& description, const sockaddr* address, socklen_t address_size, ShouldBlock) 134{ 135 ASSERT(!m_bound); 136 if (address_size != sizeof(sockaddr_un)) 137 return KResult(-EINVAL); 138 if (address->sa_family != AF_LOCAL) 139 return KResult(-EINVAL); 140 if (is_connected()) 141 return KResult(-EISCONN); 142 143 const sockaddr_un& local_address = *reinterpret_cast<const sockaddr_un*>(address); 144 char safe_address[sizeof(local_address.sun_path) + 1] = { 0 }; 145 memcpy(safe_address, local_address.sun_path, sizeof(local_address.sun_path)); 146 147#ifdef DEBUG_LOCAL_SOCKET 148 dbg() << "LocalSocket{" << this << "} connect(" << safe_address << ")"; 149#endif 150 151 auto description_or_error = VFS::the().open(safe_address, O_RDWR, 0, Process::current->current_directory()); 152 if (description_or_error.is_error()) 153 return KResult(-ECONNREFUSED); 154 155 m_file = move(description_or_error.value()); 156 157 ASSERT(m_file->inode()); 158 if (!m_file->inode()->socket()) 159 return KResult(-ECONNREFUSED); 160 161 m_address = local_address; 162 163 ASSERT(m_connect_side_fd == &description); 164 m_connect_side_role = Role::Connecting; 165 166 auto peer = m_file->inode()->socket(); 167 auto result = peer->queue_connection_from(*this); 168 if (result.is_error()) { 169 m_connect_side_role = Role::None; 170 return result; 171 } 172 173 if (is_connected()) { 174 m_connect_side_role = Role::Connected; 175 return KSuccess; 176 } 177 178 if (Thread::current->block<Thread::ConnectBlocker>(description) != Thread::BlockResult::WokeNormally) { 179 m_connect_side_role = Role::None; 180 return KResult(-EINTR); 181 } 182 183#ifdef DEBUG_LOCAL_SOCKET 184 dbg() << "LocalSocket{" << this << "} connect(" << safe_address << ") status is " << to_string(setup_state()); 185#endif 186 187 if (!is_connected()) { 188 m_connect_side_role = Role::None; 189 return KResult(-ECONNREFUSED); 190 } 191 m_connect_side_role = Role::Connected; 192 return KSuccess; 193} 194 195KResult LocalSocket::listen(size_t backlog) 196{ 197 LOCKER(lock()); 198 if (type() != SOCK_STREAM) 199 return KResult(-EOPNOTSUPP); 200 set_backlog(backlog); 201 m_connect_side_role = m_role = Role::Listener; 202#ifdef DEBUG_LOCAL_SOCKET 203 dbg() << "LocalSocket{" << this << "} listening with backlog=" << backlog; 204#endif 205 return KSuccess; 206} 207 208void LocalSocket::attach(FileDescription& description) 209{ 210 ASSERT(!m_accept_side_fd_open); 211 if (m_connect_side_role == Role::None) { 212 ASSERT(m_connect_side_fd == nullptr); 213 m_connect_side_fd = &description; 214 } else { 215 ASSERT(m_connect_side_fd != &description); 216 m_accept_side_fd_open = true; 217 } 218} 219 220void LocalSocket::detach(FileDescription& description) 221{ 222 if (m_connect_side_fd == &description) { 223 m_connect_side_fd = nullptr; 224 } else { 225 ASSERT(m_accept_side_fd_open); 226 m_accept_side_fd_open = false; 227 } 228} 229 230bool LocalSocket::can_read(const FileDescription& description) const 231{ 232 auto role = this->role(description); 233 if (role == Role::Listener) 234 return can_accept(); 235 if (role == Role::Accepted) 236 return !has_attached_peer(description) || !m_for_server.is_empty(); 237 if (role == Role::Connected) 238 return !has_attached_peer(description) || !m_for_client.is_empty(); 239 return false; 240} 241 242bool LocalSocket::has_attached_peer(const FileDescription& description) const 243{ 244 auto role = this->role(description); 245 if (role == Role::Accepted) 246 return m_connect_side_fd != nullptr; 247 if (role == Role::Connected) 248 return m_accept_side_fd_open; 249 ASSERT_NOT_REACHED(); 250} 251 252bool LocalSocket::can_write(const FileDescription& description) const 253{ 254 auto role = this->role(description); 255 if (role == Role::Accepted) 256 return !has_attached_peer(description) || m_for_client.space_for_writing(); 257 if (role == Role::Connected) 258 return !has_attached_peer(description) || m_for_server.space_for_writing(); 259 return false; 260} 261 262ssize_t LocalSocket::sendto(FileDescription& description, const void* data, size_t data_size, int, const sockaddr*, socklen_t) 263{ 264 if (!has_attached_peer(description)) 265 return -EPIPE; 266 ssize_t nwritten = send_buffer_for(description).write((const u8*)data, data_size); 267 if (nwritten > 0) 268 Thread::current->did_unix_socket_write(nwritten); 269 return nwritten; 270} 271 272DoubleBuffer& LocalSocket::receive_buffer_for(FileDescription& description) 273{ 274 auto role = this->role(description); 275 if (role == Role::Accepted) 276 return m_for_server; 277 if (role == Role::Connected) 278 return m_for_client; 279 ASSERT_NOT_REACHED(); 280} 281 282DoubleBuffer& LocalSocket::send_buffer_for(FileDescription& description) 283{ 284 auto role = this->role(description); 285 if (role == Role::Connected) 286 return m_for_server; 287 if (role == Role::Accepted) 288 return m_for_client; 289 ASSERT_NOT_REACHED(); 290} 291 292ssize_t LocalSocket::recvfrom(FileDescription& description, void* buffer, size_t buffer_size, int, sockaddr*, socklen_t*) 293{ 294 auto& buffer_for_me = receive_buffer_for(description); 295 if (!description.is_blocking()) { 296 if (buffer_for_me.is_empty()) { 297 if (!has_attached_peer(description)) 298 return 0; 299 return -EAGAIN; 300 } 301 } else if (!can_read(description)) { 302 auto result = Thread::current->block<Thread::ReadBlocker>(description); 303 if (result != Thread::BlockResult::WokeNormally) 304 return -EINTR; 305 } 306 if (!has_attached_peer(description) && buffer_for_me.is_empty()) 307 return 0; 308 ASSERT(!buffer_for_me.is_empty()); 309 int nread = buffer_for_me.read((u8*)buffer, buffer_size); 310 if (nread > 0) 311 Thread::current->did_unix_socket_read(nread); 312 return nread; 313} 314 315StringView LocalSocket::socket_path() const 316{ 317 size_t len = strnlen(m_address.sun_path, sizeof(m_address.sun_path)); 318 return { m_address.sun_path, len }; 319} 320 321String LocalSocket::absolute_path(const FileDescription& description) const 322{ 323 StringBuilder builder; 324 builder.append("socket:"); 325 builder.append(socket_path()); 326 327 switch (role(description)) { 328 case Role::Listener: 329 builder.append(" (listening)"); 330 break; 331 case Role::Accepted: 332 builder.appendf(" (accepted from pid %d)", origin_pid()); 333 break; 334 case Role::Connected: 335 builder.appendf(" (connected to pid %d)", acceptor_pid()); 336 break; 337 case Role::Connecting: 338 builder.append(" (connecting)"); 339 break; 340 default: 341 break; 342 } 343 344 return builder.to_string(); 345} 346 347KResult LocalSocket::getsockopt(FileDescription& description, int level, int option, void* value, socklen_t* value_size) 348{ 349 if (level != SOL_SOCKET) 350 return Socket::getsockopt(description, level, option, value, value_size); 351 352 switch (option) { 353 case SO_PEERCRED: { 354 if (*value_size < sizeof(ucred)) 355 return KResult(-EINVAL); 356 auto& creds = *(ucred*)value; 357 switch (role(description)) { 358 case Role::Accepted: 359 creds = m_origin; 360 *value_size = sizeof(ucred); 361 return KSuccess; 362 case Role::Connected: 363 creds = m_acceptor; 364 *value_size = sizeof(ucred); 365 return KSuccess; 366 case Role::Connecting: 367 return KResult(-ENOTCONN); 368 default: 369 return KResult(-EINVAL); 370 } 371 break; 372 } 373 default: 374 return Socket::getsockopt(description, level, option, value, value_size); 375 } 376} 377 378KResult LocalSocket::chmod(mode_t mode) 379{ 380 if (m_file) 381 return m_file->chmod(mode); 382 383 m_prebind_mode = mode & 04777; 384 return KSuccess; 385} 386 387KResult LocalSocket::chown(uid_t uid, gid_t gid) 388{ 389 if (m_file) 390 return m_file->chown(uid, gid); 391 392 if (!Process::current->is_superuser() && (Process::current->euid() != uid || !Process::current->in_group(gid))) 393 return KResult(-EPERM); 394 395 m_prebind_uid = uid; 396 m_prebind_gid = gid; 397 return KSuccess; 398} 399 400}