1/*
2 * Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7/*
8 * Post-message based test for simple rpc based access to name services.
9 *
10 * Converted from srpc_nameservice_test (deprecated), i.e., C -> C++,
11 * srpc -> post message.
12 */
13#include <string>
14
15#include <assert.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <inttypes.h>
19#include <sys/fcntl.h>
20#include <string.h>
21#include <unistd.h>
22
23#include "native_client/src/include/nacl_scoped_ptr.h"
24#include "native_client/src/public/imc_syscalls.h"
25#include "native_client/src/public/name_service.h"
26#include "native_client/src/shared/srpc/nacl_srpc.h"
27
28#include "ppapi/cpp/instance.h"
29#include "ppapi/cpp/module.h"
30#include "ppapi/cpp/var.h"
31
32#include "ppapi/native_client/src/untrusted/nacl_ppapi_util/nacl_ppapi_util.h"
33#include "ppapi/native_client/src/untrusted/nacl_ppapi_util/string_buffer.h"
34
35#define RNG_OUTPUT_BYTES  1024
36
37#define BYTES_PER_LINE    32
38#define BYTE_SPACING      4
39
40bool            g_ns_channel_initialized = false;
41NaClSrpcChannel g_ns_channel;
42
43void dump_output(nacl::StringBuffer *sb, int d, size_t nbytes) {
44  nacl::scoped_array<uint8_t> bytes;
45  size_t                      got;
46  int                         copied;
47
48  bytes.reset(new uint8_t[nbytes]);
49  if (NULL == bytes.get()) {
50    perror("dump_output");
51    fprintf(stderr, "No memory\n");
52    return;
53  }
54  // Read the RNG output.
55  for (got = 0; got < nbytes; got += copied) {
56    copied = read(d, bytes.get() + got, nbytes - got);
57    if (-1 == copied) {
58      perror("dump_output:read");
59      fprintf(stderr, "read failure\n");
60      break;
61    }
62    printf("read(%d, ..., %u) -> %d\n", d, nbytes - got, copied);
63  }
64  // Hex dump it so we can eyeball it for randomness.  Ideally we
65  // would have a chi-square test here to test randomness.
66  for (size_t ix = 0; ix < got; ++ix) {
67    if (0 == (ix & (BYTES_PER_LINE - 1))) {
68      sb->Printf("\n%04x:", ix);
69    } else if (0 == (ix & (BYTE_SPACING - 1))) {
70      sb->Printf(" ");
71    }
72    sb->Printf("%02x", bytes[ix]);
73  }
74  sb->Printf("\n");
75}
76
77void Initialize(const pp::Var& message_data, nacl::StringBuffer* sb) {
78  if (g_ns_channel_initialized) {
79    return;
80  }
81  int ns = -1;
82  nacl_nameservice(&ns);
83  printf("ns = %d\n", ns);
84  assert(-1 != ns);
85  int connected_socket = imc_connect(ns);
86  assert(-1 != connected_socket);
87  if (!NaClSrpcClientCtor(&g_ns_channel, connected_socket)) {
88    sb->Printf("Srpc client channel ctor failed\n");
89    close(ns);
90  }
91  sb->Printf("NaClSrpcClientCtor succeeded\n");
92  close(ns);
93  g_ns_channel_initialized = 1;
94}
95
96//
97// Dump RNG output into a string.
98//
99void RngDump(const pp::Var& message_data, nacl::StringBuffer* sb) {
100  NaClSrpcError rpc_result;
101  int status;
102  int rng;
103
104  Initialize(message_data, sb);
105
106  rpc_result = NaClSrpcInvokeBySignature(&g_ns_channel,
107                                         NACL_NAME_SERVICE_LOOKUP,
108                                         "SecureRandom", O_RDONLY,
109                                         &status, &rng);
110  assert(NACL_SRPC_RESULT_OK == rpc_result);
111  printf("rpc status %d\n", status);
112  assert(NACL_NAME_SERVICE_SUCCESS == status);
113  printf("rng descriptor %d\n", rng);
114
115  dump_output(sb, rng, RNG_OUTPUT_BYTES);
116  close(rng);
117}
118
119void ManifestTest(const pp::Var& message_data, nacl::StringBuffer* sb) {
120  int status = -1;
121  int manifest;
122
123  Initialize(message_data, sb);
124
125  // Make the name service lookup for the manifest service descriptor.
126  if (NACL_SRPC_RESULT_OK !=
127      NaClSrpcInvokeBySignature(&g_ns_channel, NACL_NAME_SERVICE_LOOKUP,
128                                "ManifestNameService", O_RDWR,
129                                &status, &manifest) ||
130      NACL_NAME_SERVICE_SUCCESS != status) {
131    fprintf(stderr, "nameservice lookup failed, status %d\n", status);
132    return;
133  }
134  sb->Printf("Got manifest descriptor %d\n", manifest);
135  if (-1 == manifest) {
136    return;
137  }
138
139  // Connect to manifest name server.
140  int manifest_conn = imc_connect(manifest);
141  close(manifest);
142  sb->Printf("got manifest connection %d\n", manifest_conn);
143  if (-1 == manifest_conn) {
144    sb->Printf("could not connect\n");
145    return;
146  }
147  sb->DiscardOutput();
148  sb->Printf("ManifestTest: basic connectivity ok\n");
149
150  close(manifest_conn);
151}
152
153struct PostMessageHandlerDesc {
154  char const *request;
155  void (*handler)(const pp::Var& message_data, nacl::StringBuffer* out);
156};
157
158// This object represents one time the page says <embed>.
159class MyInstance : public pp::Instance {
160 public:
161  explicit MyInstance(PP_Instance instance) : pp::Instance(instance) {}
162  virtual ~MyInstance() {}
163  virtual void HandleMessage(const pp::Var& message_data);
164};
165
166// HandleMessage gets invoked when postMessage is called on the DOM
167// element associated with this plugin instance.  In this case, if we
168// are given a string, we'll post a message back to JavaScript with a
169// reply string -- essentially treating this as a string-based RPC.
170void MyInstance::HandleMessage(const pp::Var& message_data) {
171  static struct PostMessageHandlerDesc kMsgHandlers[] = {
172    { "init", Initialize },
173    { "rng", RngDump },
174    { "manifest_test", ManifestTest },
175    { reinterpret_cast<char const *>(NULL),
176      reinterpret_cast<void (*)(const pp::Var&, nacl::StringBuffer*)>(NULL) }
177  };
178  nacl::StringBuffer sb;
179
180  if (message_data.is_string()) {
181    std::string op_name(message_data.AsString());
182    std::string reply;
183    size_t len;
184
185    fprintf(stderr, "Searching for handler for request \"%s\".\n",
186            op_name.c_str());
187
188    for (size_t ix = 0; kMsgHandlers[ix].request != NULL; ++ix) {
189      if (op_name == kMsgHandlers[ix].request) {
190        fprintf(stderr, "found at index %u\n", ix);
191        kMsgHandlers[ix].handler(message_data, &sb);
192        break;
193      }
194    }
195
196    reply = sb.ToString();
197    len = strlen(reply.c_str());
198    fprintf(stderr, "posting reply len %d\n", len);
199    // fprintf(stderr, "posting reply \"%s\".\n", sb.ToString().c_str());
200    fprintf(stderr, "posting reply \"");
201    fflush(stderr);
202    write(2, reply.c_str(), len);
203    fprintf(stderr, "\".\n");
204    fflush(stderr);
205
206    PostMessage(pp::Var(sb.ToString()));
207    fprintf(stderr, "returning\n");
208    fflush(stderr);
209  }
210}
211
212// This object is the global object representing this plugin library as long
213// as it is loaded.
214class MyModule : public pp::Module {
215 public:
216  MyModule() : pp::Module() {}
217  virtual ~MyModule() {}
218
219  // Override CreateInstance to create your customized Instance object.
220  virtual pp::Instance* CreateInstance(PP_Instance instance) {
221    return new MyInstance(instance);
222  }
223};
224
225namespace pp {
226
227// Factory function for your specialization of the Module object.
228Module* CreateModule() {
229  return new MyModule();
230}
231
232}  // namespace pp
233