| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/socket/unix_domain_server_socket_posix.h" |
| |
| #include <errno.h> |
| #include <sys/socket.h> |
| #include <sys/un.h> |
| #include <unistd.h> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "build/build_config.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/sockaddr_storage.h" |
| #include "net/base/sockaddr_util_posix.h" |
| #include "net/socket/socket_posix.h" |
| #include "net/socket/unix_domain_client_socket_posix.h" |
| |
| namespace net { |
| |
| UnixDomainServerSocket::UnixDomainServerSocket( |
| const AuthCallback& auth_callback, |
| bool use_abstract_namespace) |
| : auth_callback_(auth_callback), |
| use_abstract_namespace_(use_abstract_namespace) { |
| DCHECK(!auth_callback_.is_null()); |
| } |
| |
| UnixDomainServerSocket::~UnixDomainServerSocket() = default; |
| |
| // static |
| bool UnixDomainServerSocket::GetPeerCredentials(SocketDescriptor socket, |
| Credentials* credentials) { |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \ |
| BUILDFLAG(IS_FUCHSIA) |
| struct ucred user_cred; |
| socklen_t len = sizeof(user_cred); |
| if (getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &user_cred, &len) < 0) |
| return false; |
| credentials->process_id = user_cred.pid; |
| credentials->user_id = user_cred.uid; |
| credentials->group_id = user_cred.gid; |
| return true; |
| #else |
| return getpeereid( |
| socket, &credentials->user_id, &credentials->group_id) == 0; |
| #endif |
| } |
| |
| int UnixDomainServerSocket::Listen(const IPEndPoint& address, int backlog) { |
| NOTIMPLEMENTED(); |
| return ERR_NOT_IMPLEMENTED; |
| } |
| |
| int UnixDomainServerSocket::ListenWithAddressAndPort( |
| const std::string& address_string, |
| uint16_t port, |
| int backlog) { |
| NOTIMPLEMENTED(); |
| return ERR_NOT_IMPLEMENTED; |
| } |
| |
| int UnixDomainServerSocket::BindAndListen(const std::string& socket_path, |
| int backlog) { |
| DCHECK(!listen_socket_); |
| |
| SockaddrStorage address; |
| if (!FillUnixAddress(socket_path, use_abstract_namespace_, &address)) { |
| return ERR_ADDRESS_INVALID; |
| } |
| |
| auto socket = std::make_unique<SocketPosix>(); |
| int rv = socket->Open(AF_UNIX); |
| DCHECK_NE(ERR_IO_PENDING, rv); |
| if (rv != OK) |
| return rv; |
| |
| rv = socket->Bind(address); |
| DCHECK_NE(ERR_IO_PENDING, rv); |
| if (rv != OK) { |
| PLOG(ERROR) |
| << "Could not bind unix domain socket to " << socket_path |
| << (use_abstract_namespace_ ? " (with abstract namespace)" : ""); |
| return rv; |
| } |
| |
| rv = socket->Listen(backlog); |
| DCHECK_NE(ERR_IO_PENDING, rv); |
| if (rv != OK) |
| return rv; |
| |
| listen_socket_.swap(socket); |
| return rv; |
| } |
| |
| int UnixDomainServerSocket::GetLocalAddress(IPEndPoint* address) const { |
| DCHECK(address); |
| |
| // Unix domain sockets have no valid associated addr/port; |
| // return address invalid. |
| return ERR_ADDRESS_INVALID; |
| } |
| |
| int UnixDomainServerSocket::Accept(std::unique_ptr<StreamSocket>* socket, |
| CompletionOnceCallback callback) { |
| DCHECK(socket); |
| DCHECK(callback); |
| DCHECK(!callback_ && !out_socket_.stream && !out_socket_.descriptor); |
| |
| out_socket_ = {socket, nullptr}; |
| int rv = DoAccept(); |
| if (rv == ERR_IO_PENDING) |
| callback_ = std::move(callback); |
| else |
| CancelCallback(); |
| return rv; |
| } |
| |
| int UnixDomainServerSocket::AcceptSocketDescriptor( |
| SocketDescriptor* socket, |
| CompletionOnceCallback callback) { |
| DCHECK(socket); |
| DCHECK(callback); |
| DCHECK(!callback_ && !out_socket_.stream && !out_socket_.descriptor); |
| |
| out_socket_ = {nullptr, socket}; |
| int rv = DoAccept(); |
| if (rv == ERR_IO_PENDING) |
| callback_ = std::move(callback); |
| else |
| CancelCallback(); |
| return rv; |
| } |
| |
| int UnixDomainServerSocket::DoAccept() { |
| DCHECK(listen_socket_); |
| DCHECK(!accept_socket_); |
| |
| while (true) { |
| int rv = listen_socket_->Accept( |
| &accept_socket_, |
| base::BindOnce(&UnixDomainServerSocket::AcceptCompleted, |
| base::Unretained(this))); |
| if (rv != OK) |
| return rv; |
| if (AuthenticateAndGetStreamSocket()) |
| return OK; |
| // Accept another socket because authentication error should be transparent |
| // to the caller. |
| } |
| } |
| |
| void UnixDomainServerSocket::AcceptCompleted(int rv) { |
| DCHECK(!callback_.is_null()); |
| |
| if (rv != OK) { |
| RunCallback(rv); |
| return; |
| } |
| |
| if (AuthenticateAndGetStreamSocket()) { |
| RunCallback(OK); |
| return; |
| } |
| |
| // Accept another socket because authentication error should be transparent |
| // to the caller. |
| rv = DoAccept(); |
| if (rv != ERR_IO_PENDING) |
| RunCallback(rv); |
| } |
| |
| bool UnixDomainServerSocket::AuthenticateAndGetStreamSocket() { |
| DCHECK(accept_socket_); |
| |
| Credentials credentials; |
| if (!GetPeerCredentials(accept_socket_->socket_fd(), &credentials) || |
| !auth_callback_.Run(credentials)) { |
| accept_socket_.reset(); |
| return false; |
| } |
| |
| SetSocketResult(std::move(accept_socket_)); |
| return true; |
| } |
| |
| void UnixDomainServerSocket::SetSocketResult( |
| std::unique_ptr<SocketPosix> accepted_socket) { |
| // Exactly one of the output pointers should be set. |
| DCHECK_NE(!!out_socket_.stream, !!out_socket_.descriptor); |
| |
| // Pass ownership of |accepted_socket|. |
| if (out_socket_.descriptor) { |
| *out_socket_.descriptor = accepted_socket->ReleaseConnectedSocket(); |
| return; |
| } |
| *out_socket_.stream = |
| std::make_unique<UnixDomainClientSocket>(std::move(accepted_socket)); |
| } |
| |
| void UnixDomainServerSocket::RunCallback(int rv) { |
| out_socket_ = SocketDestination(); |
| std::move(callback_).Run(rv); |
| } |
| |
| void UnixDomainServerSocket::CancelCallback() { |
| out_socket_ = SocketDestination(); |
| callback_.Reset(); |
| } |
| |
| } // namespace net |