1 : /*
2 : * Copyright (c) 2011 The Native Client 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 : * NaCl SRPC library. A primitive rpc library.
9 : */
10 :
11 : #include <stdarg.h>
12 : #include <stdlib.h>
13 : #include <string.h>
14 :
15 : #include "native_client/src/include/portability.h"
16 : #include "native_client/src/include/nacl_macros.h"
17 : #ifdef __native_client__
18 : #include <inttypes.h>
19 : #else
20 : #include "native_client/src/trusted/desc/nacl_desc_imc.h"
21 : #endif /* __native_client__ */
22 : #include "native_client/src/shared/srpc/nacl_srpc.h"
23 : #include "native_client/src/shared/srpc/nacl_srpc_internal.h"
24 :
25 :
26 : /*
27 : * Utility method for type checking argument lists.
28 : */
29 194 : static int TypeCheckArgs(const char* arg_types, NaClSrpcArg** alist) {
30 : const char* p;
31 :
32 802 : for (p = arg_types; '\0' != *p && ':' != *p; ++p, ++alist) {
33 207 : if (NULL == *alist) {
34 : /* Too few arguments */
35 0 : return 0;
36 : }
37 : /* This code could be more compact by using a 256 entry table. */
38 207 : switch (*p) {
39 : case NACL_SRPC_ARG_TYPE_BOOL:
40 : case NACL_SRPC_ARG_TYPE_CHAR_ARRAY:
41 : case NACL_SRPC_ARG_TYPE_DOUBLE:
42 : case NACL_SRPC_ARG_TYPE_DOUBLE_ARRAY:
43 : case NACL_SRPC_ARG_TYPE_HANDLE:
44 : case NACL_SRPC_ARG_TYPE_INT:
45 : case NACL_SRPC_ARG_TYPE_INT_ARRAY:
46 : case NACL_SRPC_ARG_TYPE_LONG:
47 : case NACL_SRPC_ARG_TYPE_LONG_ARRAY:
48 : case NACL_SRPC_ARG_TYPE_STRING:
49 207 : if ((*alist)->tag != (enum NaClSrpcArgType) *p) {
50 0 : return 0;
51 : }
52 : break;
53 : /* These cases are added to avoid warnings. */
54 : case NACL_SRPC_ARG_TYPE_OBJECT:
55 : case NACL_SRPC_ARG_TYPE_VARIANT_ARRAY:
56 : case NACL_SRPC_ARG_TYPE_INVALID:
57 : default:
58 0 : return 0;
59 : }
60 : }
61 194 : if (NULL != *alist) {
62 : /* Too many arguments */
63 0 : return 0;
64 : }
65 194 : return 1;
66 : }
67 :
68 : /*
69 : * Methods for invoking RPCs.
70 : */
71 : NaClSrpcError NaClSrpcInvokeV(NaClSrpcChannel* channel,
72 : uint32_t rpc_number,
73 : NaClSrpcArg* args[],
74 97 : NaClSrpcArg* rets[]) {
75 : int i;
76 : NaClSrpcRpc rpc;
77 : NaClSrpcError retval;
78 : const char* rpc_name;
79 : const char* arg_types;
80 : const char* ret_types;
81 :
82 97 : if (NULL == channel) {
83 0 : NaClSrpcLog(NACL_SRPC_LOG_ERROR,
84 : "NaClSrpcInvokeV: channel == NULL\n");
85 0 : return NACL_SRPC_RESULT_INTERNAL;
86 : }
87 97 : if (NaClSrpcServiceMethodNameAndTypes(channel->client,
88 : rpc_number,
89 : &rpc_name,
90 : &arg_types,
91 : &ret_types)) {
92 : /* Check input parameters for type conformance */
93 97 : if (!TypeCheckArgs(arg_types, args)) {
94 0 : NaClSrpcLog(NACL_SRPC_LOG_ERROR,
95 : "NaClSrpcInvokeV(channel=%p): input arg mismatch\n",
96 : (void*) channel);
97 0 : return NACL_SRPC_RESULT_IN_ARG_TYPE_MISMATCH;
98 : }
99 : /* Check return values for type conformance */
100 97 : if (!TypeCheckArgs(ret_types, rets)) {
101 0 : NaClSrpcLog(NACL_SRPC_LOG_ERROR,
102 : "NaClSrpcInvokeV(channel=%p): output arg mismatch\n",
103 : (void*) channel);
104 0 : return NACL_SRPC_RESULT_OUT_ARG_TYPE_MISMATCH;
105 : }
106 : } else {
107 0 : NaClSrpcLog(NACL_SRPC_LOG_ERROR,
108 : "NaClSrpcInvokeV(channel=%p): bad rpc number\n",
109 : (void*) channel);
110 0 : return NACL_SRPC_RESULT_BAD_RPC_NUMBER;
111 : }
112 97 : NaClSrpcLog(1,
113 : "NaClSrpcInvokeV: request(channel=%p, rpc_number=%"NACL_PRIu32
114 : ", rpc_name=\"%s\")\n",
115 : (void*) channel,
116 : rpc_number,
117 : rpc_name);
118 :
119 202 : for (i = 0; args[i] != NULL; i++ ) {
120 : char buffer[256];
121 105 : NaClSrpcFormatArg(2, args[i], buffer, NACL_ARRAY_SIZE(buffer));
122 105 : NaClSrpcLog(2,
123 : "NaClSrpcInvokeV: request(channel=%p, args[%d]=%s)\n",
124 : (void*) channel,
125 : i,
126 : buffer);
127 : }
128 :
129 : /*
130 : * First we send the request.
131 : * This requires sending args and the types and array sizes from rets.
132 : */
133 97 : rpc.protocol_version = kNaClSrpcProtocolVersion;
134 97 : rpc.rpc_number = rpc_number;
135 97 : rpc.request_id = 0;
136 97 : rpc.result = NACL_SRPC_RESULT_OK;
137 97 : rpc.rets = rets;
138 97 : rpc.ret_types = ret_types;
139 97 : retval = NaClSrpcRequestWrite(channel, &rpc, args, rets);
140 97 : if (!retval) {
141 0 : NaClSrpcLog(NACL_SRPC_LOG_ERROR,
142 : "NaClSrpcInvokeV(channel=%p): rpc request send failed\n",
143 : (void*) channel);
144 0 : return NACL_SRPC_RESULT_INTERNAL;
145 : }
146 :
147 : /* Then we wait for the response. */
148 97 : NaClSrpcRpcWait(channel, &rpc);
149 97 : NaClSrpcLog(1,
150 : "NaClSrpcInvokeV: response(channel=%p, rpc_number=%"NACL_PRIu32
151 : ", rpc_name=\"%s\", result=%d, string=\"%s\")\n",
152 : (void*) channel,
153 : rpc_number,
154 : rpc_name,
155 : rpc.result,
156 : NaClSrpcErrorString(rpc.result));
157 :
158 199 : for (i = 0; rets[i] != NULL; i++ ) {
159 : char buffer[256];
160 102 : NaClSrpcFormatArg(2, rets[i], buffer, NACL_ARRAY_SIZE(buffer));
161 102 : NaClSrpcLog(2,
162 : "NaClSrpcInvokeV: response(channel=%p, rets[%d]=%s)\n",
163 : (void*) channel,
164 : i,
165 : buffer);
166 : }
167 :
168 97 : return rpc.result;
169 : }
170 :
171 : /*
172 : * Parameter passing and return involves a significant amount of replication
173 : * that could be handled through templates. What follows is a set of
174 : * macros for that task.
175 : */
176 : /*
177 : * Some steps involve skipping a parameter in a va_arg list.
178 : */
179 : #define SKIP(va, impl_type) \
180 : (void) va_arg(va, impl_type);
181 :
182 : /*
183 : * The first phase is the args[] vector construction.
184 : */
185 : #define SCALAR_ARG(arg, field, va, impl_type) \
186 : (arg)->field = va_arg(va, impl_type)
187 : #define ARRAY_ARG(arg, array_name, va, impl_type) \
188 : (arg)->u.count = va_arg(va, uint32_t); \
189 : (arg)->array_name = va_arg(va, impl_type)
190 : #define BOOL_ARG(arg, field, va, impl_type) \
191 : (arg)->u.bval = (va_arg(va, impl_type) != 0)
192 :
193 : /*
194 : * The second phase is the rets[] vector construction before invocation.
195 : */
196 : #define SCALAR_RETINIT(arg, field, va, impl_type) \
197 : (arg)->field = (impl_type) 0; \
198 : SKIP(va, impl_type *)
199 : #define ARRAY_RETINIT(arg, array_name, va, impl_type) \
200 : (arg)->u.count = *va_arg(va, uint32_t*); \
201 : (arg)->array_name = va_arg(va, impl_type)
202 : #define BOOL_RETINIT(arg, field, va, impl_type) \
203 : SKIP(va, impl_type *)
204 :
205 : /*
206 : * The third phase is skipping the args[] after invocation.
207 : */
208 : #define SCALAR_SKIP(arg, field, va, impl_type) \
209 : SKIP(va, impl_type)
210 : #define ARRAY_SKIP(arg, array_name, va, impl_type) \
211 : SKIP(va, uint32_t) \
212 : SKIP(va, impl_type)
213 : #define BOOL_SKIP(arg, field, va, impl_type) \
214 : SCALAR_SKIP(arg, field, va, impl_type)
215 :
216 : /*
217 : * The fourth phase is copying the rets[] into the va_args after invocation.
218 : */
219 : #define SCALAR_RET(arg, field, va, impl_type) \
220 : *va_arg(va, impl_type *) = (arg)->field
221 : #define ARRAY_RET(arg, array_name, va, impl_type) \
222 : *va_arg(va, uint32_t*) = (arg)->u.count; \
223 : SKIP(va, impl_type)
224 : #define BOOL_RET(arg, field, va, impl_type) \
225 : *va_arg(va, impl_type *) = ((arg)->field != 0)
226 :
227 : /*
228 : * All the phases consist of a loop around a switch enumerating types.
229 : */
230 : #define ARGRET_SWITCH(phase, va, arg) \
231 : switch (*p) { \
232 : case NACL_SRPC_ARG_TYPE_BOOL: \
233 : BOOL_##phase(arg, u.bval, va, int); \
234 : break; \
235 : case NACL_SRPC_ARG_TYPE_CHAR_ARRAY: \
236 : ARRAY_##phase(arg, arrays.carr, va, char*); \
237 : break; \
238 : case NACL_SRPC_ARG_TYPE_DOUBLE: \
239 : SCALAR_##phase(arg, u.dval, va, double); \
240 : break; \
241 : case NACL_SRPC_ARG_TYPE_DOUBLE_ARRAY: \
242 : ARRAY_##phase(arg, arrays.darr, va, double*); \
243 : break; \
244 : case NACL_SRPC_ARG_TYPE_HANDLE: \
245 : SCALAR_##phase(arg, u.hval, va, NaClSrpcImcDescType); \
246 : break; \
247 : case NACL_SRPC_ARG_TYPE_INT: \
248 : SCALAR_##phase(arg, u.ival, va, int32_t); \
249 : break; \
250 : case NACL_SRPC_ARG_TYPE_INT_ARRAY: \
251 : ARRAY_##phase(arg, arrays.iarr, va, int32_t*); \
252 : break; \
253 : case NACL_SRPC_ARG_TYPE_LONG: \
254 : SCALAR_##phase(arg, u.lval, va, int64_t); \
255 : break; \
256 : case NACL_SRPC_ARG_TYPE_LONG_ARRAY: \
257 : ARRAY_##phase(arg, arrays.larr, va, int64_t*); \
258 : break; \
259 : case NACL_SRPC_ARG_TYPE_STRING: \
260 : SCALAR_##phase(arg, arrays.str, va, char*); \
261 : break; \
262 : /* \
263 : * The two cases below are added to avoid warnings, \
264 : * they are only used in the plugin code \
265 : */ \
266 : case NACL_SRPC_ARG_TYPE_OBJECT: \
267 : case NACL_SRPC_ARG_TYPE_VARIANT_ARRAY: \
268 : default: \
269 : rv = NACL_SRPC_RESULT_APP_ERROR; \
270 : goto done; \
271 : }
272 :
273 52 : static void FreeArgs(NaClSrpcArg** vec) {
274 52 : if (NULL == vec) {
275 0 : return;
276 : }
277 52 : free(vec[0]);
278 52 : free(vec);
279 : }
280 :
281 52 : static NaClSrpcArg** AllocArgs(size_t vector_length) {
282 : NaClSrpcArg** vec;
283 : size_t vector_length_in_bytes;
284 : size_t i;
285 :
286 : /* Allocate the index vector. */
287 52 : if (NACL_SRPC_MAX_ARGS < vector_length) {
288 0 : return NULL;
289 : }
290 52 : vector_length_in_bytes = (vector_length + 1) * sizeof *vec;
291 52 : vec = (NaClSrpcArg **) malloc(vector_length_in_bytes);
292 52 : memset(vec, 0, vector_length_in_bytes);
293 52 : if (NULL == vec) {
294 0 : FreeArgs(vec);
295 0 : return NULL;
296 : }
297 : /* Allocate and initialize the arguments (if any). */
298 52 : if (0 != vector_length) {
299 26 : vec[0] = (NaClSrpcArg *) malloc(vector_length * sizeof *vec[0]);
300 26 : if (NULL == vec[0]) {
301 0 : FreeArgs(vec);
302 0 : return NULL;
303 : }
304 63 : for (i = 0; i < vector_length; ++i) {
305 37 : NaClSrpcArgCtor(vec[0] + i);
306 : }
307 : }
308 : /* Set the index vector to point to the arguments. */
309 63 : for (i = 1; i < vector_length; ++i) {
310 11 : vec[i] = vec[0] + i;
311 : }
312 52 : vec[vector_length] = 0;
313 52 : return vec;
314 : }
315 :
316 : NaClSrpcError NaClSrpcInvokeVaList(NaClSrpcChannel *channel,
317 : uint32_t rpc_num,
318 : va_list in_va,
319 26 : va_list out_va) {
320 : char const *rpc_name;
321 : char const *arg_types;
322 : char const *ret_types;
323 : size_t num_in;
324 : size_t num_out;
325 : uint32_t i;
326 26 : NaClSrpcArg **inv = NULL;
327 26 : NaClSrpcArg **outv = NULL;
328 : char const *p;
329 : NaClSrpcError rv;
330 :
331 26 : if (NULL == channel) {
332 0 : NaClSrpcLog(NACL_SRPC_LOG_ERROR,
333 : "NaClSrpcInvokeVaList: channel == NULL\n");
334 0 : return NACL_SRPC_RESULT_INTERNAL;
335 : }
336 :
337 26 : if (!NaClSrpcServiceMethodNameAndTypes(channel->client,
338 : rpc_num,
339 : &rpc_name,
340 : &arg_types,
341 : &ret_types)) {
342 : /*
343 : * If rpc_number is out of range, this will return an error before
344 : * communicating with the server.
345 : */
346 0 : return NACL_SRPC_RESULT_BAD_RPC_NUMBER;
347 : }
348 :
349 26 : num_in = strlen(arg_types);
350 26 : num_out = strlen(ret_types);
351 :
352 26 : if (NACL_SRPC_MAX_ARGS < num_in || NACL_SRPC_MAX_ARGS < num_out) {
353 0 : return NACL_SRPC_RESULT_APP_ERROR;
354 : }
355 :
356 26 : rv = NACL_SRPC_RESULT_NO_MEMORY;
357 26 : inv = AllocArgs(num_in);
358 26 : if (NULL == inv) {
359 0 : goto done;
360 : }
361 26 : outv = AllocArgs(num_out);
362 26 : if (NULL == outv) {
363 0 : goto done;
364 : }
365 :
366 49 : for (i = 0, p = arg_types; i < num_in; ++i, ++p) {
367 23 : ARGRET_SWITCH(ARG, in_va, inv[i]);
368 23 : inv[i]->tag = (enum NaClSrpcArgType) *p;
369 : }
370 40 : for (i = 0, p = ret_types; i < num_out; ++i, ++p) {
371 14 : ARGRET_SWITCH(RETINIT, in_va, outv[i]);
372 14 : outv[i]->tag = (enum NaClSrpcArgType) *p;
373 : }
374 :
375 26 : rv = NaClSrpcInvokeV(channel, rpc_num, inv, outv);
376 :
377 49 : for (i = 0, p = arg_types; i < num_in; ++i, ++p) {
378 23 : ARGRET_SWITCH(SKIP, out_va, inv[i]);
379 : }
380 40 : for (i = 0, p = ret_types; i < num_out; ++i, ++p) {
381 14 : ARGRET_SWITCH(RET, out_va, outv[i]);
382 : }
383 :
384 26 : done:
385 26 : FreeArgs(outv);
386 26 : FreeArgs(inv);
387 26 : return rv;
388 : }
389 :
390 : NaClSrpcError NaClSrpcInvoke(NaClSrpcChannel *channel,
391 : uint32_t rpc_num,
392 0 : ...) {
393 : va_list in_va;
394 : va_list out_va;
395 : NaClSrpcError rv;
396 :
397 0 : va_start(in_va, rpc_num);
398 0 : va_start(out_va, rpc_num);
399 :
400 0 : rv = NaClSrpcInvokeVaList(channel, rpc_num, in_va, out_va);
401 : /*
402 : * Before the messages are sent to the server, rpc_num will be checked
403 : * for validity.
404 : */
405 :
406 0 : va_end(out_va);
407 0 : va_end(in_va);
408 :
409 0 : return rv;
410 : }
411 :
412 : NaClSrpcError NaClSrpcInvokeBySignature(NaClSrpcChannel *channel,
413 : const char *rpc_signature,
414 26 : ...) {
415 : uint32_t rpc_num;
416 : va_list in_va;
417 : va_list out_va;
418 : NaClSrpcError rv;
419 :
420 26 : if (NULL == channel) {
421 0 : NaClSrpcLog(NACL_SRPC_LOG_ERROR,
422 : "NaClSrpcInvokeBySignature: channel == NULL\n");
423 0 : return NACL_SRPC_RESULT_INTERNAL;
424 : }
425 26 : rpc_num = NaClSrpcServiceMethodIndex(channel->client, rpc_signature);
426 26 : if (kNaClSrpcInvalidMethodIndex == rpc_num) {
427 : /*
428 : * kNaClSrpcInvalidMethodIndex is returned when rpc_name does not match
429 : * any method in the client service. Explicitly check and return an error.
430 : */
431 0 : NaClSrpcLog(NACL_SRPC_LOG_ERROR,
432 : "NaClSrpcInvokeBySignature(channel=%p):"
433 : "missing signature [%s]\n",
434 : (void*) channel,
435 : rpc_signature);
436 0 : return NACL_SRPC_RESULT_APP_ERROR;
437 : }
438 :
439 26 : va_start(in_va, rpc_signature);
440 26 : va_start(out_va, rpc_signature);
441 :
442 26 : rv = NaClSrpcInvokeVaList(channel, rpc_num, in_va, out_va);
443 :
444 26 : va_end(out_va);
445 26 : va_end(in_va);
446 :
447 26 : return rv;
448 : }
|