1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 // OS_LINUX only. Note that OS_ANDROID/OS_NACL don't reach here.
31 #if defined(OS_LINUX)
32
33 #include "ipc/ipc.h"
34
35 #include <arpa/inet.h>
36 #include <fcntl.h>
37 #include <libgen.h>
38 #include <netinet/in.h>
39 #include <sys/socket.h>
40 #include <sys/stat.h>
41 #include <sys/time.h>
42 #include <sys/types.h>
43 #include <sys/un.h>
44 #if defined(OS_MACOSX) || defined(OS_DRAGONFLY)
45 #include <sys/ucred.h>
46 #endif
47 #include <sys/wait.h>
48 #include <unistd.h>
49
50 #include <cerrno>
51 #include <cstring>
52 #include <cstdlib>
53
54 #include "base/file_util.h"
55 #include "base/logging.h"
56 #include "base/thread.h"
57 #include "ipc/ipc_path_manager.h"
58
59 #ifndef UNIX_PATH_MAX
60 #define UNIX_PATH_MAX 108
61 #endif // UNIX_PATH_MAX
62
63 namespace mozc {
64
65 namespace {
66
67 const int kInvalidSocket = -1;
68
mkdir_p(const string & dirname)69 void mkdir_p(const string &dirname) {
70 const string parent_dir = FileUtil::Dirname(dirname);
71 struct stat st;
72 if (!parent_dir.empty() &&
73 ::stat(parent_dir.c_str(), &st) < 0) {
74 mkdir_p(parent_dir);
75 }
76 FileUtil::CreateDirectory(dirname);
77 }
78
IsReadTimeout(int socket,int timeout)79 bool IsReadTimeout(int socket, int timeout) {
80 if (timeout < 0) {
81 return false;
82 }
83 fd_set fds;
84 struct timeval tv;
85 FD_ZERO(&fds);
86 FD_SET(socket, &fds);
87 tv.tv_sec = timeout / 1000;
88 tv.tv_usec = 1000 * (timeout % 1000);
89 if (select(socket + 1, &fds, NULL, NULL, &tv) < 0) {
90 // Mac OS X and glibc implementations of strerror() return a pointer to a
91 // string literal whenever errno is in a valid range, and thus thread-safe.
92 // Probably we don't have to use the cumbersome strerror_r() function.
93 LOG(WARNING) << "select() failed: " << strerror(errno);
94 return true;
95 }
96 if (FD_ISSET(socket, &fds)) {
97 return false;
98 }
99
100 LOG(ERROR) << "FD_ISSET failed";
101 return true;
102 }
103
IsWriteTimeout(int socket,int timeout)104 bool IsWriteTimeout(int socket, int timeout) {
105 if (timeout < 0) {
106 return false;
107 }
108 fd_set fds;
109 struct timeval tv;
110 FD_ZERO(&fds);
111 FD_SET(socket, &fds);
112 tv.tv_sec = timeout / 1000;
113 tv.tv_usec = 1000 * (timeout % 1000);
114 if (select(socket + 1, NULL, &fds, NULL, &tv) < 0) {
115 LOG(WARNING) << "select() failed: " << strerror(errno);
116 return true;
117 }
118 if (FD_ISSET(socket, &fds)) {
119 return false;
120 }
121
122 LOG(ERROR) << "FD_ISSET failed";
123 return true;
124 }
125
IsPeerValid(int socket,pid_t * pid)126 bool IsPeerValid(int socket, pid_t *pid) {
127 *pid = 0;
128
129 #if defined(OS_MACOSX) || defined(OS_DRAGONFLY)
130 // If the OS is MAC, we should validate the peer by using LOCAL_PEERCRED.
131 struct xucred peer_cred;
132 socklen_t peer_cred_len = sizeof(struct xucred);
133 if (::getsockopt(socket, 0, LOCAL_PEERCRED,
134 &peer_cred, &peer_cred_len) < 0) {
135 LOG(ERROR) << "cannot get peer credential. NOT a Unix socket?";
136 return false;
137 }
138 if (peer_cred.cr_version != XUCRED_VERSION) {
139 LOG(WARNING) << "credential version mismatch.";
140 return false;
141 }
142 if (peer_cred.cr_uid != ::geteuid()) {
143 LOG(WARNING) << "uid mismatch." << peer_cred.cr_uid << "!=" << ::geteuid();
144 return false;
145 }
146
147 // MacOS doesn't have cr_pid;
148 *pid = 0;
149 #endif
150
151 #if defined(OS_LINUX) && !defined(OS_DRAGONFLY)
152 // On ARM Linux, we do nothing and just return true since the platform
153 // sometimes doesn't support the getsockopt(sock, SOL_SOCKET, SO_PEERCRED)
154 // system call.
155 // TODO(yusukes): Add implementation for ARM Linux.
156 #ifndef __arm__
157 struct ucred peer_cred;
158 int peer_cred_len = sizeof(peer_cred);
159 if (getsockopt(socket, SOL_SOCKET, SO_PEERCRED,
160 reinterpret_cast<void *>(&peer_cred),
161 reinterpret_cast<socklen_t *>(&peer_cred_len)) < 0) {
162 LOG(ERROR) << "cannot get peer credential. Not a Unix socket?";
163 return false;
164 }
165
166 if (peer_cred.uid != ::geteuid()) {
167 LOG(WARNING) << "uid mismatch." << peer_cred.uid << "!=" << ::geteuid();
168 return false;
169 }
170
171 *pid = peer_cred.pid;
172 #endif // __arm__
173 #endif
174
175 return true;
176 }
177
SendMessage(int socket,const char * buf,size_t buf_length,int timeout,IPCErrorType * last_ipc_error)178 bool SendMessage(int socket,
179 const char *buf,
180 size_t buf_length, int timeout,
181 IPCErrorType *last_ipc_error) {
182 size_t buf_length_left = buf_length;
183 while (buf_length_left > 0) {
184 if (IsWriteTimeout(socket, timeout)) {
185 LOG(WARNING) << "Write timeout " << timeout;
186 *last_ipc_error = IPC_TIMEOUT_ERROR;
187 return false;
188 }
189 const ssize_t l = ::send(socket, buf, buf_length_left, MSG_NOSIGNAL);
190 if (l < 0) {
191 // An error occurs.
192 LOG(ERROR) << "an error occurred during sending \""
193 << string(buf, buf_length_left) << "\": " << strerror(errno);
194 *last_ipc_error = IPC_WRITE_ERROR;
195 return false;
196 }
197 buf += l;
198 buf_length_left -= l;
199 }
200 VLOG(1) << buf_length << " bytes sent";
201 return true;
202 }
203
RecvMessage(int socket,char * buf,size_t * buf_length,int timeout,IPCErrorType * last_ipc_error)204 bool RecvMessage(int socket,
205 char *buf,
206 size_t *buf_length,
207 int timeout,
208 IPCErrorType *last_ipc_error) {
209 if (*buf_length == 0) {
210 LOG(WARNING) << "buf_length is 0";
211 *last_ipc_error = IPC_UNKNOWN_ERROR;
212 return false;
213 }
214 ssize_t buf_left = *buf_length;
215 ssize_t read_length = 0;
216 *buf_length = 0;
217 do {
218 if (IsReadTimeout(socket, timeout)) {
219 LOG(WARNING) << "Read timeout " << timeout;
220 *last_ipc_error = IPC_TIMEOUT_ERROR;
221 return false;
222 }
223 read_length = ::recv(socket, buf, buf_left, 0);
224 if (read_length < 0) {
225 LOG(ERROR) << "an error occurred during recv(): " << strerror(errno);
226 *buf_length = 0;
227 *last_ipc_error = IPC_READ_ERROR;
228 return false;
229 }
230 *buf_length += read_length;
231 buf += read_length;
232 buf_left -= read_length;
233 } while (read_length != 0 && buf_left > 0);
234 VLOG(1) << *buf_length << " bytes received";
235 return true;
236 }
237
SetCloseOnExecFlag(int fd)238 void SetCloseOnExecFlag(int fd) {
239 int flags = ::fcntl(fd, F_GETFD, 0);
240 if (flags < 0) {
241 LOG(WARNING) << "fcntl(F_GETFD) for fd " << fd << " failed: "
242 << strerror(errno);
243 return;
244 }
245 flags |= FD_CLOEXEC;
246 if (::fcntl(fd, F_SETFD, flags) != 0) {
247 LOG(WARNING) << "fcntl(F_SETFD) for fd " << fd << " failed: "
248 << strerror(errno);
249 }
250 }
251
252 // Returns true if address is in abstract namespace. See unix(7) on Linux for
253 // details.
IsAbstractSocket(const string & address)254 bool IsAbstractSocket(const string& address) {
255 return (!address.empty()) && (address[0] == '\0');
256 }
257 } // namespace
258
259 // Client
IPCClient(const string & name)260 IPCClient::IPCClient(const string &name)
261 : socket_(kInvalidSocket), connected_(false),
262 ipc_path_manager_(NULL),
263 last_ipc_error_(IPC_NO_ERROR) {
264 Init(name, "");
265 }
266
IPCClient(const string & name,const string & server_path)267 IPCClient::IPCClient(const string &name, const string &server_path)
268 : socket_(kInvalidSocket), connected_(false),
269 ipc_path_manager_(NULL),
270 last_ipc_error_(IPC_NO_ERROR) {
271 Init(name, server_path);
272 }
273
Init(const string & name,const string & server_path)274 void IPCClient::Init(const string &name, const string &server_path) {
275 last_ipc_error_ = IPC_NO_CONNECTION;
276
277 // Try twice, because key may be changed.
278 IPCPathManager *manager = IPCPathManager::GetIPCPathManager(name);
279 if (manager == NULL) {
280 LOG(ERROR) << "IPCPathManager::GetIPCPathManager failed";
281 return;
282 }
283
284 ipc_path_manager_ = manager;
285
286 for (size_t trial = 0; trial < 2; ++trial) {
287 string server_address;
288 if (!manager->LoadPathName() || !manager->GetPathName(&server_address)) {
289 continue;
290 }
291 sockaddr_un address;
292 ::memset(&address, 0, sizeof(address));
293 const size_t server_address_length =
294 (server_address.size() >= UNIX_PATH_MAX) ?
295 UNIX_PATH_MAX - 1 : server_address.size();
296 if (server_address.size() >= UNIX_PATH_MAX) {
297 LOG(WARNING) << "too long path: " << server_address;
298 }
299 socket_ = socket(PF_UNIX, SOCK_STREAM, 0);
300 if (socket_ < 0) {
301 LOG(WARNING) << "socket failed: " << strerror(errno);
302 continue;
303 }
304 SetCloseOnExecFlag(socket_);
305 address.sun_family = AF_UNIX;
306 ::memcpy(address.sun_path, server_address.data(), server_address_length);
307 address.sun_path[server_address_length] = '\0';
308 #if defined(OS_MACOSX) || defined(OS_DRAGONFLY)
309 address.sun_len = SUN_LEN(&address);
310 const size_t sun_len = sizeof(address);
311 #else
312 const size_t sun_len = sizeof(address.sun_family) + server_address_length;
313 #endif
314 pid_t pid = 0;
315 if (::connect(socket_,
316 reinterpret_cast<const sockaddr*>(&address),
317 sun_len) != 0 ||
318 !IsPeerValid(socket_, &pid)) {
319 if ((errno == ENOTSOCK || errno == ECONNREFUSED) &&
320 !IsAbstractSocket(server_address)) {
321 // If abstract namepace is not enabled, recreate server_addresss path.
322 ::unlink(server_address.c_str());
323 }
324 LOG(WARNING) << "connect failed: " << strerror(errno);
325 connected_ = false;
326 manager->Clear();
327 continue;
328 } else {
329 if (!manager->IsValidServer(static_cast<uint32>(pid),
330 server_path)) {
331 LOG(ERROR) << "Connecting to invalid server";
332 last_ipc_error_ = IPC_INVALID_SERVER;
333 break;
334 }
335 last_ipc_error_ = IPC_NO_ERROR;
336 connected_ = true;
337 break;
338 }
339 }
340 }
341
~IPCClient()342 IPCClient::~IPCClient() {
343 if (socket_ != kInvalidSocket) {
344 if (::close(socket_) < 0) {
345 LOG(WARNING) << "close failed: " << strerror(errno);
346 }
347 socket_ = kInvalidSocket;
348 }
349 connected_ = false;
350 VLOG(1) << "connection closed (IPCClient destructed)";
351 }
352
353 // RPC call
Call(const char * request_,size_t input_length,char * response_,size_t * response_size,int32 timeout)354 bool IPCClient::Call(const char *request_,
355 size_t input_length,
356 char *response_,
357 size_t *response_size,
358 int32 timeout) {
359 last_ipc_error_ = IPC_NO_ERROR;
360 if (!SendMessage(socket_, request_, input_length, timeout,
361 &last_ipc_error_)) {
362 LOG(ERROR) << "SendMessage failed";
363 return false;
364 }
365
366 // Half-close the socket so that mozc_server could know the length of the
367 // request data. Without this, RecvMessage() in mozc_server would fail with
368 // timeout.
369 // TODO(yusukes): It would be also possible to modify Send and RecvMessage()
370 // so that they send/recv the payload size explicitly together with the actual
371 // data. Will revisit later.
372 ::shutdown(socket_, SHUT_WR);
373
374 if (!RecvMessage(socket_, response_, response_size, timeout,
375 &last_ipc_error_)) {
376 LOG(ERROR) << "RecvMessage failed";
377 return false;
378 }
379 VLOG(1) << "Call succeeded";
380 return true;
381 }
382
Connected() const383 bool IPCClient::Connected() const {
384 return connected_;
385 }
386
387 // Server
IPCServer(const string & name,int32 num_connections,int32 timeout)388 IPCServer::IPCServer(const string &name,
389 int32 num_connections,
390 int32 timeout)
391 : connected_(false), socket_(kInvalidSocket), timeout_(timeout) {
392 IPCPathManager *manager = IPCPathManager::GetIPCPathManager(name);
393 if (!manager->CreateNewPathName() && !manager->LoadPathName()) {
394 LOG(ERROR) << "Cannot prepare IPC path name";
395 return;
396 }
397
398 if (!manager->GetPathName(&server_address_)) {
399 LOG(ERROR) << "Cannot make IPC path name";
400 return;
401 }
402 DCHECK(!server_address_.empty());
403
404 const string dirname = FileUtil::Dirname(server_address_);
405 mkdir_p(dirname);
406
407 sockaddr_un addr;
408 ::memset(&addr, 0, sizeof(addr));
409 socket_ = ::socket(PF_UNIX, SOCK_STREAM, 0);
410 if (socket_ < 0) {
411 LOG(WARNING) << "socket failed: " << strerror(errno);
412 return;
413 }
414 SetCloseOnExecFlag(socket_);
415
416 if (server_address_.size() >= UNIX_PATH_MAX) {
417 LOG(WARNING) << "server address is too long";
418 return;
419 }
420
421 addr.sun_family = AF_UNIX;
422 ::memcpy(addr.sun_path,
423 server_address_.data(),
424 server_address_.size());
425 addr.sun_path[server_address_.size()] = '\0';
426
427 int on = 1;
428 ::setsockopt(socket_,
429 SOL_SOCKET,
430 SO_REUSEADDR,
431 reinterpret_cast<char *>(&on),
432 sizeof(on));
433 #if defined(OS_MACOSX) || defined(OS_DRAGONFLY)
434 addr.sun_len = SUN_LEN(&addr);
435 const size_t sun_len = sizeof(addr);
436 #else
437 const size_t sun_len = sizeof(addr.sun_family) + server_address_.size();
438 #endif
439 if (::bind(socket_, reinterpret_cast<sockaddr *>(&addr), sun_len) != 0) {
440 // The UNIX domain socket file (server_address_) already exists?
441 LOG(FATAL) << "bind() failed: " << strerror(errno);
442 return;
443 }
444 if (!IsAbstractSocket(server_address_)) {
445 // Linux does not use files for IPC.
446 ::chmod(server_address_.c_str(), 0600);
447 }
448
449 if (::listen(socket_, num_connections) < 0) {
450 LOG(FATAL) << "listen() failed: " << strerror(errno);
451 return;
452 }
453
454 if (!manager->SavePathName()) {
455 LOG(ERROR) << "Cannot save IPC path name";
456 return;
457 }
458
459 connected_ = true;
460 VLOG(1) << "IPCServer ready";
461 }
462
~IPCServer()463 IPCServer::~IPCServer() {
464 if (server_thread_.get() != NULL) {
465 server_thread_->Terminate();
466 }
467 ::shutdown(socket_, SHUT_RDWR);
468 ::close(socket_);
469 if (!IsAbstractSocket(server_address_)) {
470 // When abstract namespace is used, unlink() is not necessary.
471 ::unlink(server_address_.c_str());
472 }
473 connected_ = false;
474 socket_ = kInvalidSocket;
475 VLOG(1) << "IPCServer destructed";
476 }
477
Connected() const478 bool IPCServer::Connected() const {
479 return connected_;
480 }
481
Loop()482 void IPCServer::Loop() {
483 // The most portable and straightforward single-thread server
484 bool error = false;
485 IPCErrorType last_ipc_error = IPC_NO_ERROR;
486 pid_t pid = 0;
487 while (!error) {
488 const int new_sock = ::accept(socket_, NULL, NULL);
489 if (new_sock < 0) {
490 LOG(FATAL) << "accept() failed: " << strerror(errno);
491 return;
492 }
493 if (!IsPeerValid(new_sock, &pid)) {
494 continue;
495 }
496 size_t request_size = sizeof(request_);
497 size_t response_size = sizeof(response_);
498 if (RecvMessage(new_sock,
499 &request_[0],
500 &request_size, timeout_, &last_ipc_error)) {
501 if (!Process(&request_[0], request_size,
502 &response_[0], &response_size)) {
503 LOG(WARNING) << "Process() failed";
504 error = true;
505 }
506 if (response_size > 0) {
507 SendMessage(new_sock,
508 &response_[0],
509 response_size, timeout_, &last_ipc_error);
510 }
511 }
512
513 ::close(new_sock);
514 }
515
516 ::shutdown(socket_, SHUT_RDWR);
517 ::close(socket_);
518 if (!IsAbstractSocket(server_address_)) {
519 // When abstract namespace is used, unlink() is not necessary.
520 ::unlink(server_address_.c_str());
521 }
522 connected_ = false;
523 socket_ = kInvalidSocket;
524 }
525
Terminate()526 void IPCServer::Terminate() {
527 server_thread_->Terminate();
528 }
529
530 } // namespace mozc
531
532 #endif // OS_LINUX
533