|  | // 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/logging.h" | 
|  | #include "net/base/net_errors.h" | 
|  | #include "net/base/sockaddr_storage.h" | 
|  | #include "net/socket/socket_posix.h" | 
|  | #include "net/socket/unix_domain_client_socket_posix.h" | 
|  |  | 
|  | namespace net { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Intended for use as SetterCallbacks in Accept() helper methods. | 
|  | void SetStreamSocket(std::unique_ptr<StreamSocket>* socket, | 
|  | std::unique_ptr<SocketPosix> accepted_socket) { | 
|  | socket->reset(new UnixDomainClientSocket(std::move(accepted_socket))); | 
|  | } | 
|  |  | 
|  | void SetSocketDescriptor(SocketDescriptor* socket, | 
|  | std::unique_ptr<SocketPosix> accepted_socket) { | 
|  | *socket = accepted_socket->ReleaseConnectedSocket(); | 
|  | } | 
|  |  | 
|  | }  // anonymous namespace | 
|  |  | 
|  | 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() { | 
|  | } | 
|  |  | 
|  | // static | 
|  | bool UnixDomainServerSocket::GetPeerCredentials(SocketDescriptor socket, | 
|  | Credentials* credentials) { | 
|  | #if defined(OS_LINUX) || defined(OS_ANDROID) | 
|  | 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 (!UnixDomainClientSocket::FillAddress(socket_path, | 
|  | use_abstract_namespace_, | 
|  | &address)) { | 
|  | return ERR_ADDRESS_INVALID; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<SocketPosix> socket(new 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, | 
|  | const CompletionCallback& callback) { | 
|  | DCHECK(socket); | 
|  |  | 
|  | SetterCallback setter_callback = base::Bind(&SetStreamSocket, socket); | 
|  | return DoAccept(setter_callback, callback); | 
|  | } | 
|  |  | 
|  | int UnixDomainServerSocket::AcceptSocketDescriptor( | 
|  | SocketDescriptor* socket, | 
|  | const CompletionCallback& callback) { | 
|  | DCHECK(socket); | 
|  |  | 
|  | SetterCallback setter_callback = base::Bind(&SetSocketDescriptor, socket); | 
|  | return DoAccept(setter_callback, callback); | 
|  | } | 
|  |  | 
|  | int UnixDomainServerSocket::DoAccept(const SetterCallback& setter_callback, | 
|  | const CompletionCallback& callback) { | 
|  | DCHECK(!setter_callback.is_null()); | 
|  | DCHECK(!callback.is_null()); | 
|  | DCHECK(listen_socket_); | 
|  | DCHECK(!accept_socket_); | 
|  |  | 
|  | while (true) { | 
|  | int rv = listen_socket_->Accept( | 
|  | &accept_socket_, | 
|  | base::Bind(&UnixDomainServerSocket::AcceptCompleted, | 
|  | base::Unretained(this), | 
|  | setter_callback, | 
|  | callback)); | 
|  | if (rv != OK) | 
|  | return rv; | 
|  | if (AuthenticateAndGetStreamSocket(setter_callback)) | 
|  | return OK; | 
|  | // Accept another socket because authentication error should be transparent | 
|  | // to the caller. | 
|  | } | 
|  | } | 
|  |  | 
|  | void UnixDomainServerSocket::AcceptCompleted( | 
|  | const SetterCallback& setter_callback, | 
|  | const CompletionCallback& callback, | 
|  | int rv) { | 
|  | if (rv != OK) { | 
|  | callback.Run(rv); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (AuthenticateAndGetStreamSocket(setter_callback)) { | 
|  | callback.Run(OK); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Accept another socket because authentication error should be transparent | 
|  | // to the caller. | 
|  | rv = DoAccept(setter_callback, callback); | 
|  | if (rv != ERR_IO_PENDING) | 
|  | callback.Run(rv); | 
|  | } | 
|  |  | 
|  | bool UnixDomainServerSocket::AuthenticateAndGetStreamSocket( | 
|  | const SetterCallback& setter_callback) { | 
|  | DCHECK(accept_socket_); | 
|  |  | 
|  | Credentials credentials; | 
|  | if (!GetPeerCredentials(accept_socket_->socket_fd(), &credentials) || | 
|  | !auth_callback_.Run(credentials)) { | 
|  | accept_socket_.reset(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | setter_callback.Run(std::move(accept_socket_)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace net |