• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 namespace net {
18 
19 namespace {
20 
21 // Intended for use as SetterCallbacks in Accept() helper methods.
SetStreamSocket(scoped_ptr<StreamSocket> * socket,scoped_ptr<SocketLibevent> accepted_socket)22 void SetStreamSocket(scoped_ptr<StreamSocket>* socket,
23                      scoped_ptr<SocketLibevent> accepted_socket) {
24   socket->reset(new UnixDomainClientSocket(accepted_socket.Pass()));
25 }
26 
SetSocketDescriptor(SocketDescriptor * socket,scoped_ptr<SocketLibevent> accepted_socket)27 void SetSocketDescriptor(SocketDescriptor* socket,
28                          scoped_ptr<SocketLibevent> accepted_socket) {
29   *socket = accepted_socket->ReleaseConnectedSocket();
30 }
31 
32 }  // anonymous namespace
33 
UnixDomainServerSocket(const AuthCallback & auth_callback,bool use_abstract_namespace)34 UnixDomainServerSocket::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 
~UnixDomainServerSocket()42 UnixDomainServerSocket::~UnixDomainServerSocket() {
43 }
44 
45 // static
GetPeerCredentials(SocketDescriptor socket,Credentials * credentials)46 bool 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 
Listen(const IPEndPoint & address,int backlog)63 int UnixDomainServerSocket::Listen(const IPEndPoint& address, int backlog) {
64   NOTIMPLEMENTED();
65   return ERR_NOT_IMPLEMENTED;
66 }
67 
ListenWithAddressAndPort(const std::string & unix_domain_path,int port_unused,int backlog)68 int 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 
GetLocalAddress(IPEndPoint * address) const105 int UnixDomainServerSocket::GetLocalAddress(IPEndPoint* address) const {
106   NOTIMPLEMENTED();
107   return ERR_NOT_IMPLEMENTED;
108 }
109 
Accept(scoped_ptr<StreamSocket> * socket,const CompletionCallback & callback)110 int 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 
AcceptSocketDescriptor(SocketDescriptor * socket,const CompletionCallback & callback)118 int 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 
DoAccept(const SetterCallback & setter_callback,const CompletionCallback & callback)127 int 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 
AcceptCompleted(const SetterCallback & setter_callback,const CompletionCallback & callback,int rv)150 void 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 
AuthenticateAndGetStreamSocket(const SetterCallback & setter_callback)171 bool 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