1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/socket/unix_domain_server_socket_posix.h"
6
7#include <errno.h>
8#include <sys/socket.h>
9#include <sys/un.h>
10#include <unistd.h>
11
12#include "base/logging.h"
13#include "net/base/net_errors.h"
14#include "net/socket/socket_libevent.h"
15#include "net/socket/unix_domain_client_socket_posix.h"
16
17namespace net {
18
19namespace {
20
21// Intended for use as SetterCallbacks in Accept() helper methods.
22void SetStreamSocket(scoped_ptr<StreamSocket>* socket,
23                     scoped_ptr<SocketLibevent> accepted_socket) {
24  socket->reset(new UnixDomainClientSocket(accepted_socket.Pass()));
25}
26
27void SetSocketDescriptor(SocketDescriptor* socket,
28                         scoped_ptr<SocketLibevent> accepted_socket) {
29  *socket = accepted_socket->ReleaseConnectedSocket();
30}
31
32}  // anonymous namespace
33
34UnixDomainServerSocket::UnixDomainServerSocket(
35    const AuthCallback& auth_callback,
36    bool use_abstract_namespace)
37    : auth_callback_(auth_callback),
38      use_abstract_namespace_(use_abstract_namespace) {
39  DCHECK(!auth_callback_.is_null());
40}
41
42UnixDomainServerSocket::~UnixDomainServerSocket() {
43}
44
45// static
46bool UnixDomainServerSocket::GetPeerCredentials(SocketDescriptor socket,
47                                                Credentials* credentials) {
48#if defined(OS_LINUX) || defined(OS_ANDROID)
49  struct ucred user_cred;
50  socklen_t len = sizeof(user_cred);
51  if (getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &user_cred, &len) < 0)
52    return false;
53  credentials->process_id = user_cred.pid;
54  credentials->user_id = user_cred.uid;
55  credentials->group_id = user_cred.gid;
56  return true;
57#else
58  return getpeereid(
59      socket, &credentials->user_id, &credentials->group_id) == 0;
60#endif
61}
62
63int UnixDomainServerSocket::Listen(const IPEndPoint& address, int backlog) {
64  NOTIMPLEMENTED();
65  return ERR_NOT_IMPLEMENTED;
66}
67
68int UnixDomainServerSocket::ListenWithAddressAndPort(
69    const std::string& unix_domain_path,
70    int port_unused,
71    int backlog) {
72  DCHECK(!listen_socket_);
73
74  SockaddrStorage address;
75  if (!UnixDomainClientSocket::FillAddress(unix_domain_path,
76                                           use_abstract_namespace_,
77                                           &address)) {
78    return ERR_ADDRESS_INVALID;
79  }
80
81  scoped_ptr<SocketLibevent> socket(new SocketLibevent);
82  int rv = socket->Open(AF_UNIX);
83  DCHECK_NE(ERR_IO_PENDING, rv);
84  if (rv != OK)
85    return rv;
86
87  rv = socket->Bind(address);
88  DCHECK_NE(ERR_IO_PENDING, rv);
89  if (rv != OK) {
90    PLOG(ERROR)
91        << "Could not bind unix domain socket to " << unix_domain_path
92        << (use_abstract_namespace_ ? " (with abstract namespace)" : "");
93    return rv;
94  }
95
96  rv = socket->Listen(backlog);
97  DCHECK_NE(ERR_IO_PENDING, rv);
98  if (rv != OK)
99    return rv;
100
101  listen_socket_.swap(socket);
102  return rv;
103}
104
105int UnixDomainServerSocket::GetLocalAddress(IPEndPoint* address) const {
106  NOTIMPLEMENTED();
107  return ERR_NOT_IMPLEMENTED;
108}
109
110int UnixDomainServerSocket::Accept(scoped_ptr<StreamSocket>* socket,
111                                   const CompletionCallback& callback) {
112  DCHECK(socket);
113
114  SetterCallback setter_callback = base::Bind(&SetStreamSocket, socket);
115  return DoAccept(setter_callback, callback);
116}
117
118int UnixDomainServerSocket::AcceptSocketDescriptor(
119    SocketDescriptor* socket,
120    const CompletionCallback& callback) {
121  DCHECK(socket);
122
123  SetterCallback setter_callback = base::Bind(&SetSocketDescriptor, socket);
124  return DoAccept(setter_callback, callback);
125}
126
127int UnixDomainServerSocket::DoAccept(const SetterCallback& setter_callback,
128                                     const CompletionCallback& callback) {
129  DCHECK(!setter_callback.is_null());
130  DCHECK(!callback.is_null());
131  DCHECK(listen_socket_);
132  DCHECK(!accept_socket_);
133
134  while (true) {
135    int rv = listen_socket_->Accept(
136        &accept_socket_,
137        base::Bind(&UnixDomainServerSocket::AcceptCompleted,
138                   base::Unretained(this),
139                   setter_callback,
140                   callback));
141    if (rv != OK)
142      return rv;
143    if (AuthenticateAndGetStreamSocket(setter_callback))
144      return OK;
145    // Accept another socket because authentication error should be transparent
146    // to the caller.
147  }
148}
149
150void UnixDomainServerSocket::AcceptCompleted(
151    const SetterCallback& setter_callback,
152    const CompletionCallback& callback,
153    int rv) {
154  if (rv != OK) {
155    callback.Run(rv);
156    return;
157  }
158
159  if (AuthenticateAndGetStreamSocket(setter_callback)) {
160    callback.Run(OK);
161    return;
162  }
163
164  // Accept another socket because authentication error should be transparent
165  // to the caller.
166  rv = DoAccept(setter_callback, callback);
167  if (rv != ERR_IO_PENDING)
168    callback.Run(rv);
169}
170
171bool UnixDomainServerSocket::AuthenticateAndGetStreamSocket(
172    const SetterCallback& setter_callback) {
173  DCHECK(accept_socket_);
174
175  Credentials credentials;
176  if (!GetPeerCredentials(accept_socket_->socket_fd(), &credentials) ||
177      !auth_callback_.Run(credentials)) {
178    accept_socket_.reset();
179    return false;
180  }
181
182  setter_callback.Run(accept_socket_.Pass());
183  return true;
184}
185
186}  // namespace net
187