| // 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 "shell/domain_socket/unix_domain_server_socket_posix.h" |
| |
| #include <errno.h> |
| #include <sys/socket.h> |
| #include <sys/un.h> |
| #include <unistd.h> |
| |
| #include "base/logging.h" |
| #include "shell/domain_socket/completion_callback.h" |
| #include "shell/domain_socket/net_errors.h" |
| #include "shell/domain_socket/socket_descriptor.h" |
| #include "shell/domain_socket/socket_libevent.h" |
| #include "shell/domain_socket/unix_domain_client_socket_posix.h" |
| |
| namespace mojo { |
| namespace shell { |
| |
| namespace { |
| |
| // Intended for use as SetterCallbacks in Accept() helper methods. |
| void SetSocketDescriptor(SocketDescriptor* socket, |
| scoped_ptr<SocketLibevent> 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) { |
| 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; |
| } |
| |
| int UnixDomainServerSocket::ListenWithPath(const std::string& unix_domain_path, |
| int backlog) { |
| DCHECK(!listen_socket_); |
| |
| SockaddrStorage address; |
| if (!UnixDomainClientSocket::FillAddress(unix_domain_path, |
| use_abstract_namespace_, &address)) { |
| return net::ERR_ADDRESS_INVALID; |
| } |
| |
| scoped_ptr<SocketLibevent> socket(new SocketLibevent); |
| int rv = socket->Open(AF_UNIX); |
| DCHECK_NE(net::ERR_IO_PENDING, rv); |
| if (rv != net::OK) |
| return rv; |
| |
| rv = socket->Bind(address); |
| DCHECK_NE(net::ERR_IO_PENDING, rv); |
| if (rv != net::OK) { |
| PLOG(ERROR) << "Could not bind unix domain socket to " << unix_domain_path |
| << (use_abstract_namespace_ ? " (with abstract namespace)" |
| : ""); |
| return rv; |
| } |
| |
| rv = socket->Listen(backlog); |
| DCHECK_NE(net::ERR_IO_PENDING, rv); |
| if (rv != net::OK) |
| return rv; |
| |
| listen_socket_.swap(socket); |
| return rv; |
| } |
| |
| int UnixDomainServerSocket::Accept(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 != net::OK) |
| return rv; |
| if (AuthenticateAndGetStreamSocket(setter_callback)) |
| return net::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 != net::OK) { |
| callback.Run(rv); |
| return; |
| } |
| |
| if (AuthenticateAndGetStreamSocket(setter_callback)) { |
| callback.Run(net::OK); |
| return; |
| } |
| |
| // Accept another socket because authentication error should be transparent |
| // to the caller. |
| rv = DoAccept(setter_callback, callback); |
| if (rv != net::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(accept_socket_.Pass()); |
| return true; |
| } |
| |
| } // namespace shell |
| } // namespace mojo |