// 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 #include #include #include #include #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(); 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* 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 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(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