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 2 : static int TypeCheckArgs(const char* arg_types, NaClSrpcArg** alist) {
30 : const char* p;
31 :
32 2 : for (p = arg_types; '\0' != *p && ':' != *p; ++p, ++alist) {
33 2 : 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 2 : 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 2 : if ((*alist)->tag != (enum NaClSrpcArgType) *p) {
50 0 : return 0;
51 : }
52 2 : 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 2 : }
61 2 : if (NULL != *alist) {
62 : /* Too many arguments */
63 0 : return 0;
64 : }
65 2 : return 1;
66 2 : }
67 :
68 : /*
69 : * Methods for invoking RPCs.
70 : */
71 : NaClSrpcError NaClSrpcInvokeV(NaClSrpcChannel* channel,
72 : uint32_t rpc_number,
73 : NaClSrpcArg* args[],
74 2 : 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 2 : if (NULL == channel) {
83 : NaClSrpcLog(NACL_SRPC_LOG_ERROR,
84 0 : "NaClSrpcInvokeV: channel == NULL\n");
85 0 : return NACL_SRPC_RESULT_INTERNAL;
86 : }
87 : if (NaClSrpcServiceMethodNameAndTypes(channel->client,
88 : rpc_number,
89 : &rpc_name,
90 : &arg_types,
91 2 : &ret_types)) {
92 : /* Check input parameters for type conformance */
93 2 : if (!TypeCheckArgs(arg_types, args)) {
94 : NaClSrpcLog(NACL_SRPC_LOG_ERROR,
95 : "NaClSrpcInvokeV(channel=%p): input arg mismatch\n",
96 0 : (void*) channel);
97 0 : return NACL_SRPC_RESULT_IN_ARG_TYPE_MISMATCH;
98 : }
99 : /* Check return values for type conformance */
100 2 : if (!TypeCheckArgs(ret_types, rets)) {
101 : NaClSrpcLog(NACL_SRPC_LOG_ERROR,
102 : "NaClSrpcInvokeV(channel=%p): output arg mismatch\n",
103 0 : (void*) channel);
104 0 : return NACL_SRPC_RESULT_OUT_ARG_TYPE_MISMATCH;
105 : }
106 2 : } else {
107 : NaClSrpcLog(NACL_SRPC_LOG_ERROR,
108 : "NaClSrpcInvokeV(channel=%p): bad rpc number\n",
109 0 : (void*) channel);
110 0 : return NACL_SRPC_RESULT_BAD_RPC_NUMBER;
111 : }
112 : NaClSrpcLog(1,
113 : "NaClSrpcInvokeV: request(channel=%p, rpc_number=%"NACL_PRIu32
114 : ", rpc_name=\"%s\")\n",
115 : (void*) channel,
116 : rpc_number,
117 2 : rpc_name);
118 :
119 2 : for (i = 0; args[i] != NULL; i++ ) {
120 : char buffer[256];
121 1 : NaClSrpcFormatArg(2, args[i], buffer, NACL_ARRAY_SIZE(buffer));
122 : NaClSrpcLog(2,
123 : "NaClSrpcInvokeV: request(channel=%p, args[%d]=%s)\n",
124 : (void*) channel,
125 : i,
126 1 : buffer);
127 1 : }
128 :
129 : /*
130 : * First we send the request.
131 : * This requires sending args and the types and array sizes from rets.
132 : */
133 2 : rpc.protocol_version = kNaClSrpcProtocolVersion;
134 2 : rpc.rpc_number = rpc_number;
135 2 : rpc.request_id = 0;
136 2 : rpc.result = NACL_SRPC_RESULT_OK;
137 2 : rpc.rets = rets;
138 2 : rpc.ret_types = ret_types;
139 2 : retval = NaClSrpcRequestWrite(channel, &rpc, args, rets);
140 2 : if (!retval) {
141 : NaClSrpcLog(NACL_SRPC_LOG_ERROR,
142 : "NaClSrpcInvokeV(channel=%p): rpc request send failed\n",
143 0 : (void*) channel);
144 0 : return NACL_SRPC_RESULT_INTERNAL;
145 : }
146 :
147 : /* Then we wait for the response. */
148 2 : NaClSrpcRpcWait(channel, &rpc);
149 : 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 2 : NaClSrpcErrorString(rpc.result));
157 :
158 2 : for (i = 0; rets[i] != NULL; i++ ) {
159 : char buffer[256];
160 2 : NaClSrpcFormatArg(2, rets[i], buffer, NACL_ARRAY_SIZE(buffer));
161 : NaClSrpcLog(2,
162 : "NaClSrpcInvokeV: response(channel=%p, rets[%d]=%s)\n",
163 : (void*) channel,
164 : i,
165 2 : buffer);
166 2 : }
167 :
168 2 : return rpc.result;
169 2 : }
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 2 : static void FreeArgs(NaClSrpcArg** vec) {
274 2 : if (NULL == vec) {
275 0 : return;
276 : }
277 2 : free(vec[0]);
278 2 : free(vec);
279 2 : }
280 :
281 2 : static NaClSrpcArg** AllocArgs(size_t vector_length) {
282 : NaClSrpcArg** vec;
283 : size_t i;
284 :
285 : /* Allocate the index vector. */
286 2 : if (NACL_SRPC_MAX_ARGS < vector_length) {
287 0 : return NULL;
288 : }
289 2 : vec = (NaClSrpcArg **) calloc(vector_length + 1, sizeof *vec);
290 2 : if (NULL == vec) {
291 0 : return NULL;
292 : }
293 : /* Allocate and initialize the arguments (if any). */
294 2 : if (0 != vector_length) {
295 2 : vec[0] = (NaClSrpcArg *) malloc(vector_length * sizeof *vec[0]);
296 2 : if (NULL == vec[0]) {
297 0 : FreeArgs(vec);
298 0 : return NULL;
299 : }
300 2 : for (i = 0; i < vector_length; ++i) {
301 2 : NaClSrpcArgCtor(vec[0] + i);
302 2 : }
303 : }
304 : /* Set the index vector to point to the arguments. */
305 2 : for (i = 1; i < vector_length; ++i) {
306 0 : vec[i] = vec[0] + i;
307 0 : }
308 2 : vec[vector_length] = 0;
309 2 : return vec;
310 2 : }
311 :
312 : NaClSrpcError NaClSrpcInvokeVaList(NaClSrpcChannel *channel,
313 : uint32_t rpc_num,
314 : va_list in_va,
315 2 : va_list out_va) {
316 : char const *rpc_name;
317 : char const *arg_types;
318 : char const *ret_types;
319 : size_t num_in;
320 : size_t num_out;
321 : uint32_t i;
322 2 : NaClSrpcArg **inv = NULL;
323 2 : NaClSrpcArg **outv = NULL;
324 : char const *p;
325 : NaClSrpcError rv;
326 :
327 2 : if (NULL == channel) {
328 : NaClSrpcLog(NACL_SRPC_LOG_ERROR,
329 0 : "NaClSrpcInvokeVaList: channel == NULL\n");
330 0 : return NACL_SRPC_RESULT_INTERNAL;
331 : }
332 :
333 : if (!NaClSrpcServiceMethodNameAndTypes(channel->client,
334 : rpc_num,
335 : &rpc_name,
336 : &arg_types,
337 2 : &ret_types)) {
338 : /*
339 : * If rpc_number is out of range, this will return an error before
340 : * communicating with the server.
341 : */
342 0 : return NACL_SRPC_RESULT_BAD_RPC_NUMBER;
343 : }
344 :
345 2 : num_in = strlen(arg_types);
346 2 : num_out = strlen(ret_types);
347 :
348 2 : if (NACL_SRPC_MAX_ARGS < num_in || NACL_SRPC_MAX_ARGS < num_out) {
349 0 : return NACL_SRPC_RESULT_APP_ERROR;
350 : }
351 :
352 2 : rv = NACL_SRPC_RESULT_NO_MEMORY;
353 2 : inv = AllocArgs(num_in);
354 2 : if (NULL == inv) {
355 0 : goto done;
356 : }
357 2 : outv = AllocArgs(num_out);
358 2 : if (NULL == outv) {
359 0 : goto done;
360 : }
361 :
362 2 : for (i = 0, p = arg_types; i < num_in; ++i, ++p) {
363 1 : ARGRET_SWITCH(ARG, in_va, inv[i]);
364 1 : inv[i]->tag = (enum NaClSrpcArgType) *p;
365 1 : }
366 2 : for (i = 0, p = ret_types; i < num_out; ++i, ++p) {
367 1 : ARGRET_SWITCH(RETINIT, in_va, outv[i]);
368 1 : outv[i]->tag = (enum NaClSrpcArgType) *p;
369 1 : }
370 :
371 2 : rv = NaClSrpcInvokeV(channel, rpc_num, inv, outv);
372 :
373 2 : for (i = 0, p = arg_types; i < num_in; ++i, ++p) {
374 1 : ARGRET_SWITCH(SKIP, out_va, inv[i]);
375 1 : }
376 2 : for (i = 0, p = ret_types; i < num_out; ++i, ++p) {
377 1 : ARGRET_SWITCH(RET, out_va, outv[i]);
378 1 : }
379 :
380 : done:
381 2 : FreeArgs(outv);
382 2 : FreeArgs(inv);
383 2 : return rv;
384 2 : }
385 :
386 : NaClSrpcError NaClSrpcInvoke(NaClSrpcChannel *channel,
387 : uint32_t rpc_num,
388 0 : ...) {
389 : va_list in_va;
390 : va_list out_va;
391 : NaClSrpcError rv;
392 :
393 0 : va_start(in_va, rpc_num);
394 0 : va_start(out_va, rpc_num);
395 :
396 0 : rv = NaClSrpcInvokeVaList(channel, rpc_num, in_va, out_va);
397 : /*
398 : * Before the messages are sent to the server, rpc_num will be checked
399 : * for validity.
400 : */
401 :
402 0 : va_end(out_va);
403 0 : va_end(in_va);
404 :
405 0 : return rv;
406 0 : }
407 :
408 : NaClSrpcError NaClSrpcInvokeBySignature(NaClSrpcChannel *channel,
409 : const char *rpc_signature,
410 2 : ...) {
411 : uint32_t rpc_num;
412 : va_list in_va;
413 : va_list out_va;
414 : NaClSrpcError rv;
415 :
416 2 : if (NULL == channel) {
417 : NaClSrpcLog(NACL_SRPC_LOG_ERROR,
418 0 : "NaClSrpcInvokeBySignature: channel == NULL\n");
419 0 : return NACL_SRPC_RESULT_INTERNAL;
420 : }
421 2 : rpc_num = NaClSrpcServiceMethodIndex(channel->client, rpc_signature);
422 2 : if (kNaClSrpcInvalidMethodIndex == rpc_num) {
423 : /*
424 : * kNaClSrpcInvalidMethodIndex is returned when rpc_name does not match
425 : * any method in the client service. Explicitly check and return an error.
426 : */
427 : NaClSrpcLog(NACL_SRPC_LOG_ERROR,
428 : "NaClSrpcInvokeBySignature(channel=%p):"
429 : "missing signature [%s]\n",
430 : (void*) channel,
431 0 : rpc_signature);
432 0 : return NACL_SRPC_RESULT_APP_ERROR;
433 : }
434 :
435 2 : va_start(in_va, rpc_signature);
436 2 : va_start(out_va, rpc_signature);
437 :
438 2 : rv = NaClSrpcInvokeVaList(channel, rpc_num, in_va, out_va);
439 :
440 2 : va_end(out_va);
441 2 : va_end(in_va);
442 :
443 2 : return rv;
444 2 : }
|