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 
31 #ifdef OS_WIN
32 #include <windows.h>
33 #include <ws2tcpip.h>
34 #pragma comment(lib, "ws2_32.lib")
35 using ssize_t = SSIZE_T;
36 #else
37 #include <fcntl.h>
38 #include <netdb.h>
39 #include <netinet/in.h>
40 #include <sys/socket.h>
41 #include <unistd.h>
42 #endif  // OS_WIN
43 
44 #include <cstddef>
45 #include <cstring>
46 #include <memory>
47 #include <string>
48 #include <vector>
49 
50 #include "base/flags.h"
51 #include "base/init_mozc.h"
52 #include "base/singleton.h"
53 #include "base/system_util.h"
54 #include "engine/engine_factory.h"
55 #include "protocol/commands.pb.h"
56 #include "session/random_keyevents_generator.h"
57 #include "session/session_handler.h"
58 #include "session/session_usage_observer.h"
59 
60 DEFINE_string(host, "localhost", "server host name");
61 DEFINE_bool(server, true, "server mode");
62 DEFINE_bool(client, false, "client mode");
63 DEFINE_int32(client_test_size, 100, "client test size");
64 DEFINE_int32(port, 8000, "port of RPC server");
65 DEFINE_int32(rpc_timeout, 60000, "timeout");
66 DEFINE_string(user_profile_directory, "", "user profile directory");
67 
68 namespace mozc {
69 
70 namespace {
71 
72 const size_t kMaxRequestSize = 32 * 32 * 8192;
73 const size_t kMaxOutputSize  = 32 * 32 * 8192;
74 const int    kInvalidSocket  = -1;
75 
76 // TODO(taku): timeout should be handled.
Recv(int socket,char * buf,size_t buf_size,int timeout)77 bool Recv(int socket, char *buf,
78           size_t buf_size, int timeout) {
79   ssize_t buf_left = buf_size;
80   while (buf_left > 0) {
81     const ssize_t read_size = ::recv(socket, buf, buf_left, 0);
82     if (read_size < 0) {
83       LOG(ERROR) << "an error occurred during recv()";
84       return false;
85     }
86     buf += read_size;
87     buf_left -= read_size;
88   }
89   return buf_left == 0;
90 }
91 
92 // TODO(taku): timeout should be handled.
Send(int socket,const char * buf,size_t buf_size,int timeout)93 bool Send(int socket, const char *buf,
94           size_t buf_size, int timeout) {
95   ssize_t buf_left = buf_size;
96   while (buf_left > 0) {
97 #if defined(OS_WIN)
98     const int kFlag = 0;
99 #elif defined(OS_MACOSX)
100     const int kFlag = SO_NOSIGPIPE;
101 #else
102     const int kFlag = MSG_NOSIGNAL;
103 #endif
104     const ssize_t read_size = ::send(socket, buf, buf_left, kFlag);
105     if (read_size < 0) {
106       LOG(ERROR) << "an error occurred during sending";
107       return false;
108     }
109     buf += read_size;
110     buf_left -= read_size;
111   }
112   return buf_left == 0;
113 }
114 
CloseSocket(int client_socket)115 void CloseSocket(int client_socket) {
116 #ifdef OS_WIN
117   ::closesocket(client_socket);
118   ::shutdown(client_socket, SD_BOTH);
119 #else
120   ::close(client_socket);
121   ::shutdown(client_socket, SHUT_RDWR);
122 #endif
123 }
124 
125 // Standalone RPCServer.
126 // TODO(taku): Make a RPC class inherited from IPCInterface.
127 // This allows us to reuse client::Session library and SessionServer.
128 class RPCServer {
129  public:
RPCServer()130   RPCServer() : server_socket_(kInvalidSocket),
131                 handler_(new SessionHandler(
132                     std::unique_ptr<Engine>(EngineFactory::Create()))) {
133     struct sockaddr_in sin;
134 
135     server_socket_ = ::socket(AF_INET, SOCK_STREAM, 0);
136 
137     CHECK_NE(server_socket_, kInvalidSocket) << "socket failed";
138 
139 #ifndef OS_WIN
140     int flags = ::fcntl(server_socket_, F_GETFD, 0);
141     CHECK_GE(flags, 0) << "fcntl(F_GETFD) failed";
142     flags |= FD_CLOEXEC;
143     CHECK_EQ(::fcntl(server_socket_, F_SETFD, flags), 0)
144         << "fctl(F_SETFD) failed";
145 #endif
146 
147     ::memset(&sin, 0, sizeof(sin));
148     sin.sin_port = htons(FLAGS_port);
149     sin.sin_family = AF_INET;
150     sin.sin_addr.s_addr = htonl(INADDR_ANY);
151 
152     int on = 1;
153     ::setsockopt(server_socket_,
154                  SOL_SOCKET,
155                  SO_REUSEADDR, reinterpret_cast<char * >(&on),
156                  sizeof(on));
157 
158     CHECK_GE(::bind(server_socket_,
159                     reinterpret_cast<struct sockaddr *>(&sin),
160                     sizeof(sin)), 0) << "bind failed";
161 
162     CHECK_GE(::listen(server_socket_, SOMAXCONN), 0) << "listen failed";
163     CHECK_NE(server_socket_, 0);
164 
165     handler_->AddObserver(Singleton<session::SessionUsageObserver>::get());
166   }
167 
~RPCServer()168   ~RPCServer() {
169     CloseSocket(server_socket_);
170     server_socket_ = kInvalidSocket;
171   }
172 
Loop()173   void Loop() {
174     LOG(INFO) << "Start Mozc RPCServer";
175 
176     while (true) {
177       const int client_socket = ::accept(server_socket_, NULL, NULL);
178 
179       if (client_socket == kInvalidSocket) {
180         LOG(ERROR) << "accept failed";
181         continue;
182       }
183 
184       uint32 request_size = 0;
185       // Receive the size of data.
186       if (!Recv(client_socket, reinterpret_cast<char *>(&request_size),
187                 sizeof(request_size), FLAGS_rpc_timeout)) {
188         LOG(ERROR) << "Cannot receive request_size header.";
189         CloseSocket(client_socket);
190         continue;
191       }
192       request_size = ntohl(request_size);
193       CHECK_GT(request_size, 0);
194       CHECK_LT(request_size, kMaxRequestSize);
195 
196       // Receive the body of serialized protobuf.
197       std::unique_ptr<char[]> request_str(new char[request_size]);
198       if (!Recv(client_socket,
199                 request_str.get(), request_size, FLAGS_rpc_timeout)) {
200         LOG(ERROR) << "cannot receive body of request.";
201         CloseSocket(client_socket);
202         continue;
203       }
204 
205       commands::Command command;
206       if (!command.mutable_input()->ParseFromArray(request_str.get(),
207                                                    request_size)) {
208         LOG(ERROR) << "ParseFromArray failed";
209         CloseSocket(client_socket);
210         continue;
211       }
212 
213       CHECK(handler_->EvalCommand(&command));
214 
215       string output_str;
216       // Return the result.
217       CHECK(command.output().SerializeToString(&output_str));
218 
219       uint32 output_size = output_str.size();
220       CHECK_GT(output_size, 0);
221       CHECK_LT(output_size, kMaxOutputSize);
222       output_size = htonl(output_size);
223 
224       if (!Send(client_socket, reinterpret_cast<char *>(&output_size),
225                 sizeof(output_size), FLAGS_rpc_timeout) ||
226           !Send(client_socket, output_str.data(), output_str.size(),
227                 FLAGS_rpc_timeout)) {
228         LOG(ERROR) << "Cannot send reply.";
229       }
230 
231       CloseSocket(client_socket);
232     }
233   }
234 
235  private:
236   int server_socket_;
237   std::unique_ptr<SessionHandler> handler_;
238 };
239 
240 // Standalone RPCClient.
241 // TODO(taku): Make a RPC class inherited from IPCInterface.
242 // This allows us to reuse client::Session library and SessionServer.
243 class RPCClient {
244  public:
RPCClient()245   RPCClient() : id_(0) {}
246 
CreateSession()247   bool CreateSession() {
248     id_ = 0;
249     commands::Input input;
250     commands::Output output;
251     input.set_type(commands::Input::CREATE_SESSION);
252     if (!Call(input, &output) ||
253         output.error_code() != commands::Output::SESSION_SUCCESS) {
254       return false;
255     }
256     id_ = output.id();
257     return true;
258   }
259 
DeleteSession()260   bool DeleteSession() {
261     commands::Input input;
262     commands::Output output;
263     id_ = 0;
264     input.set_type(commands::Input::DELETE_SESSION);
265     return (Call(input, &output) &&
266             output.error_code() == commands::Output::SESSION_SUCCESS);
267   }
268 
SendKey(const mozc::commands::KeyEvent & key,mozc::commands::Output * output) const269   bool SendKey(const mozc::commands::KeyEvent &key,
270                mozc::commands::Output *output) const {
271     if (id_ == 0) {
272       return false;
273     }
274     commands::Input input;
275     input.set_type(commands::Input::SEND_KEY);
276     input.set_id(id_);
277     input.mutable_key()->CopyFrom(key);
278     return (Call(input, output) &&
279             output->error_code() == commands::Output::SESSION_SUCCESS);
280   }
281 
282  private:
Call(const commands::Input & input,commands::Output * output) const283   bool Call(const commands::Input &input,
284             commands::Output *output) const {
285     struct addrinfo hints, *res;
286     ::memset(&hints, 0, sizeof(hints));
287     hints.ai_socktype = SOCK_STREAM;
288     hints.ai_family = AF_INET;
289 
290     const string port_str = std::to_string(FLAGS_port);
291     CHECK_EQ(::getaddrinfo(FLAGS_host.c_str(), port_str.c_str(),
292                            &hints, &res), 0)
293         << "getaddrinfo failed";
294 
295     const int client_socket = ::socket(res->ai_family,
296                                        res->ai_socktype,
297                                        res->ai_protocol);
298     CHECK_NE(client_socket, kInvalidSocket) << "socket failed";
299     CHECK_GE(::connect(client_socket,
300                        res->ai_addr, res->ai_addrlen), 0)
301         << "connect failed";
302 
303     string request_str;
304     CHECK(input.SerializeToString(&request_str));
305     uint32 request_size = request_str.size();
306     CHECK_GT(request_size, 0);
307     CHECK_LT(request_size, kMaxRequestSize);
308     request_size = htonl(request_size);
309 
310     CHECK(Send(client_socket, reinterpret_cast<char *>(&request_size),
311                sizeof(request_size), FLAGS_rpc_timeout));
312     CHECK(Send(client_socket, request_str.data(), request_str.size(),
313                FLAGS_rpc_timeout));
314 
315     uint32 output_size = 0;
316     CHECK(Recv(client_socket, reinterpret_cast<char *>(&output_size),
317                sizeof(output_size), FLAGS_rpc_timeout));
318     output_size = ntohl(output_size);
319     CHECK_GT(output_size, 0);
320     CHECK_LT(output_size, kMaxOutputSize);
321 
322     std::unique_ptr<char[]> output_str(new char[output_size]);
323     CHECK(Recv(client_socket,
324                output_str.get(), output_size, FLAGS_rpc_timeout));
325 
326     CHECK(output->ParseFromArray(output_str.get(), output_size));
327 
328     ::freeaddrinfo(res);
329 
330     CloseSocket(client_socket);
331 
332     return true;
333   }
334 
335   uint64 id_;
336 };
337 
338 // Wrapper class for WSAStartup on Windows.
339 class ScopedWSAData {
340  public:
ScopedWSAData()341   ScopedWSAData() {
342 #ifdef OS_WIN
343     WSADATA wsaData;
344     CHECK_EQ(::WSAStartup(MAKEWORD(2, 1), &wsaData), 0)
345         << "WSAStartup failed";
346 #endif
347   }
~ScopedWSAData()348   ~ScopedWSAData() {
349 #ifdef OS_WIN
350     ::WSACleanup();
351 #endif
352   }
353 };
354 }  // namespace
355 
356 }  // namespace mozc
357 
main(int argc,char * argv[])358 int main(int argc, char *argv[]) {
359   mozc::InitMozc(argv[0], &argc, &argv, false);
360 
361   mozc::ScopedWSAData wsadata;
362 
363   if (!FLAGS_user_profile_directory.empty()) {
364     LOG(INFO) << "Setting user profile directory to "
365               << FLAGS_user_profile_directory;
366     mozc::SystemUtil::SetUserProfileDirectory(FLAGS_user_profile_directory);
367   }
368 
369   if (FLAGS_client) {
370     mozc::RPCClient client;
371     CHECK(client.CreateSession());
372     for (int n = 0; n < FLAGS_client_test_size; ++n) {
373       std::vector<mozc::commands::KeyEvent> keys;
374       mozc::session::RandomKeyEventsGenerator::GenerateSequence(&keys);
375       for (size_t i = 0; i < keys.size(); ++i) {
376         LOG(INFO) << "Sending to Server: " << keys[i].Utf8DebugString();
377         mozc::commands::Output output;
378         CHECK(client.SendKey(keys[i], &output));
379         LOG(INFO) << "Output of SendKey: " << output.Utf8DebugString();
380       }
381     }
382     CHECK(client.DeleteSession());
383     return 0;
384   } else if (FLAGS_server) {
385     mozc::RPCServer server;
386     server.Loop();
387   } else {
388     LOG(ERROR) << "use --server or --client option";
389     return -1;
390   }
391 
392   return 0;
393 }
394