1 // Copyright (c) 2011 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.
5 #include "base/posix/unix_domain_socket_linux.h"
8 #include <sys/socket.h>
13 #include "base/files/scoped_file.h"
14 #include "base/logging.h"
15 #include "base/memory/scoped_vector.h"
16 #include "base/pickle.h"
17 #include "base/posix/eintr_wrapper.h"
18 #include "base/stl_util.h"
20 #if !defined(__native_client_nonsfi__)
24 const size_t UnixDomainSocket::kMaxFileDescriptors
= 16;
26 #if !defined(__native_client_nonsfi__)
27 // Creates a connected pair of UNIX-domain SOCK_SEQPACKET sockets, and passes
28 // ownership of the newly allocated file descriptors to |one| and |two|.
29 // Returns true on success.
30 static bool CreateSocketPair(base::ScopedFD
* one
, base::ScopedFD
* two
) {
32 if (socketpair(AF_UNIX
, SOCK_SEQPACKET
, 0, raw_socks
) == -1)
34 one
->reset(raw_socks
[0]);
35 two
->reset(raw_socks
[1]);
40 bool UnixDomainSocket::EnableReceiveProcessId(int fd
) {
42 return setsockopt(fd
, SOL_SOCKET
, SO_PASSCRED
, &enable
, sizeof(enable
)) == 0;
44 #endif // !defined(__native_client_nonsfi__)
47 bool UnixDomainSocket::SendMsg(int fd
,
50 const std::vector
<int>& fds
) {
51 struct msghdr msg
= {};
52 struct iovec iov
= { const_cast<void*>(buf
), length
};
56 char* control_buffer
= NULL
;
58 const unsigned control_len
= CMSG_SPACE(sizeof(int) * fds
.size());
59 control_buffer
= new char[control_len
];
62 msg
.msg_control
= control_buffer
;
63 msg
.msg_controllen
= control_len
;
64 cmsg
= CMSG_FIRSTHDR(&msg
);
65 cmsg
->cmsg_level
= SOL_SOCKET
;
66 cmsg
->cmsg_type
= SCM_RIGHTS
;
67 cmsg
->cmsg_len
= CMSG_LEN(sizeof(int) * fds
.size());
68 memcpy(CMSG_DATA(cmsg
), &fds
[0], sizeof(int) * fds
.size());
69 msg
.msg_controllen
= cmsg
->cmsg_len
;
72 // Avoid a SIGPIPE if the other end breaks the connection.
73 // Due to a bug in the Linux kernel (net/unix/af_unix.c) MSG_NOSIGNAL isn't
74 // regarded for SOCK_SEQPACKET in the AF_UNIX domain, but it is mandated by
76 const int flags
= MSG_NOSIGNAL
;
77 const ssize_t r
= HANDLE_EINTR(sendmsg(fd
, &msg
, flags
));
78 const bool ret
= static_cast<ssize_t
>(length
) == r
;
79 delete[] control_buffer
;
84 ssize_t
UnixDomainSocket::RecvMsg(int fd
,
87 ScopedVector
<base::ScopedFD
>* fds
) {
88 return UnixDomainSocket::RecvMsgWithPid(fd
, buf
, length
, fds
, NULL
);
92 ssize_t
UnixDomainSocket::RecvMsgWithPid(int fd
,
95 ScopedVector
<base::ScopedFD
>* fds
,
96 base::ProcessId
* pid
) {
97 return UnixDomainSocket::RecvMsgWithFlags(fd
, buf
, length
, 0, fds
, pid
);
101 ssize_t
UnixDomainSocket::RecvMsgWithFlags(int fd
,
105 ScopedVector
<base::ScopedFD
>* fds
,
106 base::ProcessId
* out_pid
) {
109 struct msghdr msg
= {};
110 struct iovec iov
= { buf
, length
};
114 const size_t kControlBufferSize
=
115 CMSG_SPACE(sizeof(int) * kMaxFileDescriptors
)
116 #if !defined(__native_client_nonsfi__)
117 // The PNaCl toolchain for Non-SFI binary build does not support ucred.
118 + CMSG_SPACE(sizeof(struct ucred
))
121 char control_buffer
[kControlBufferSize
];
122 msg
.msg_control
= control_buffer
;
123 msg
.msg_controllen
= sizeof(control_buffer
);
125 const ssize_t r
= HANDLE_EINTR(recvmsg(fd
, &msg
, flags
));
129 int* wire_fds
= NULL
;
130 unsigned wire_fds_len
= 0;
131 base::ProcessId pid
= -1;
133 if (msg
.msg_controllen
> 0) {
134 struct cmsghdr
* cmsg
;
135 for (cmsg
= CMSG_FIRSTHDR(&msg
); cmsg
; cmsg
= CMSG_NXTHDR(&msg
, cmsg
)) {
136 const unsigned payload_len
= cmsg
->cmsg_len
- CMSG_LEN(0);
137 if (cmsg
->cmsg_level
== SOL_SOCKET
&&
138 cmsg
->cmsg_type
== SCM_RIGHTS
) {
139 DCHECK(payload_len
% sizeof(int) == 0);
140 DCHECK(wire_fds
== NULL
);
141 wire_fds
= reinterpret_cast<int*>(CMSG_DATA(cmsg
));
142 wire_fds_len
= payload_len
/ sizeof(int);
144 #if !defined(__native_client_nonsfi__)
145 // The PNaCl toolchain for Non-SFI binary build does not support
147 if (cmsg
->cmsg_level
== SOL_SOCKET
&&
148 cmsg
->cmsg_type
== SCM_CREDENTIALS
) {
149 DCHECK(payload_len
== sizeof(struct ucred
));
151 pid
= reinterpret_cast<struct ucred
*>(CMSG_DATA(cmsg
))->pid
;
157 #if !defined(__native_client_nonsfi__)
158 // The PNaCl toolchain for Non-SFI binary build does not support
159 // MSG_TRUNC or MSG_CTRUNC.
160 if (msg
.msg_flags
& MSG_TRUNC
|| msg
.msg_flags
& MSG_CTRUNC
) {
161 for (unsigned i
= 0; i
< wire_fds_len
; ++i
)
169 for (unsigned i
= 0; i
< wire_fds_len
; ++i
)
170 fds
->push_back(new base::ScopedFD(wire_fds
[i
]));
174 // |pid| will legitimately be -1 if we read EOF, so only DCHECK if we
175 // actually received a message. Unfortunately, Linux allows sending zero
176 // length messages, which are indistinguishable from EOF, so this check
177 // has false negatives.
178 if (r
> 0 || msg
.msg_controllen
> 0)
187 #if !defined(__native_client_nonsfi__)
189 ssize_t
UnixDomainSocket::SendRecvMsg(int fd
,
191 unsigned max_reply_len
,
193 const Pickle
& request
) {
194 return UnixDomainSocket::SendRecvMsgWithFlags(fd
, reply
, max_reply_len
,
195 0, /* recvmsg_flags */
200 ssize_t
UnixDomainSocket::SendRecvMsgWithFlags(int fd
,
202 unsigned max_reply_len
,
205 const Pickle
& request
) {
206 // This socketpair is only used for the IPC and is cleaned up before
208 base::ScopedFD recv_sock
, send_sock
;
209 if (!CreateSocketPair(&recv_sock
, &send_sock
))
213 std::vector
<int> send_fds
;
214 send_fds
.push_back(send_sock
.get());
215 if (!SendMsg(fd
, request
.data(), request
.size(), send_fds
))
219 // Close the sending end of the socket right away so that if our peer closes
220 // it before sending a response (e.g., from exiting), RecvMsgWithFlags() will
221 // return EOF instead of hanging.
224 ScopedVector
<base::ScopedFD
> recv_fds
;
225 // When porting to OSX keep in mind it doesn't support MSG_NOSIGNAL, so the
226 // sender might get a SIGPIPE.
227 const ssize_t reply_len
= RecvMsgWithFlags(
228 recv_sock
.get(), reply
, max_reply_len
, recvmsg_flags
, &recv_fds
, NULL
);
233 // If we received more file descriptors than caller expected, then we treat
235 if (recv_fds
.size() > (result_fd
!= NULL
? 1 : 0)) {
241 *result_fd
= recv_fds
.empty() ? -1 : recv_fds
[0]->release();
245 #endif // !defined(__native_client_nonsfi__)