1 : /*
2 : * Copyright (c) 2012 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 : * Service descriptions.
9 : */
10 :
11 : #include <stdio.h>
12 : #include <stdlib.h>
13 : #include <string.h>
14 :
15 : #include "native_client/src/shared/srpc/nacl_srpc.h"
16 : #include "native_client/src/shared/srpc/nacl_srpc_internal.h"
17 :
18 : #ifndef SIZE_T_MAX
19 : # define SIZE_T_MAX (~((size_t) 0))
20 : #endif
21 :
22 :
23 : /*
24 : * The struct used to describe methods within services.
25 : */
26 : struct NaClSrpcMethodDesc {
27 : char const *name;
28 : char const *input_types;
29 : char const *output_types;
30 : /**
31 : * function pointer used to process calls to the named method
32 : */
33 : NaClSrpcMethod handler;
34 : };
35 : typedef struct NaClSrpcMethodDesc NaClSrpcMethodDesc;
36 :
37 : /*
38 : * Forward declarations for static "built-in" methods.
39 : */
40 : static void ServiceDiscovery(NaClSrpcRpc* rpc,
41 : NaClSrpcArg** in_args,
42 : NaClSrpcArg** out_args,
43 : NaClSrpcClosure* done);
44 :
45 : /*
46 : * Get the next text element (name, input types, or output types). These
47 : * elements are delimited by ':', '\n', or '\0'.
48 : */
49 1302 : static char* GetOneElement(const char* string) {
50 1302 : size_t len = 0;
51 1302 : char* buf;
52 :
53 22468 : while (':' != string[len] && '\n' != string[len] && '\0' != string[len]) {
54 6408 : len++;
55 6408 : }
56 1302 : buf = malloc(len + 1);
57 1302 : if (NULL == buf) {
58 0 : return NULL;
59 : }
60 3906 : strncpy(buf, string, len);
61 1302 : buf[len] = '\0';
62 1302 : return buf;
63 1302 : }
64 :
65 : /* * Get the components of one method description. Returns a pointer to
66 : * the delimiter if successful or NULL if not.
67 : */
68 434 : static const char* ParseOneEntry(const char* entry_fmt,
69 434 : char** name,
70 434 : char** input_types,
71 434 : char** output_types) {
72 434 : char* namebuf = NULL;
73 434 : char* insbuf = NULL;
74 434 : char* outsbuf = NULL;
75 :
76 434 : *name = NULL;
77 434 : *input_types = NULL;
78 434 : *output_types = NULL;
79 :
80 : /* Don't try to parse NULL strings. */
81 434 : if (NULL == entry_fmt) {
82 0 : goto cleanup;
83 : }
84 : /* Get the name into a buffer, and ensure it is followed by ':'. */
85 434 : namebuf = GetOneElement(entry_fmt);
86 434 : if (NULL == namebuf) {
87 0 : goto cleanup;
88 : }
89 434 : entry_fmt += strlen(namebuf);
90 434 : if (':' != *entry_fmt) {
91 0 : goto cleanup;
92 : }
93 434 : entry_fmt++;
94 : /* Get the input types into a buffer, and ensure it is followed by ':'. */
95 434 : insbuf = GetOneElement(entry_fmt);
96 434 : if (NULL == insbuf) {
97 0 : goto cleanup;
98 : }
99 434 : entry_fmt += strlen(insbuf);
100 434 : if (':' != *entry_fmt) {
101 0 : goto cleanup;
102 : }
103 434 : entry_fmt++;
104 : /* Get the output types into a buffer. */
105 434 : outsbuf = GetOneElement(entry_fmt);
106 434 : if (NULL == outsbuf) {
107 0 : goto cleanup;
108 : }
109 434 : entry_fmt += strlen(outsbuf);
110 : /* Copy the result strings out. */
111 434 : *name = namebuf;
112 434 : *input_types = insbuf;
113 434 : *output_types = outsbuf;
114 : /* Return a pointer to the next character after the elements. */
115 434 : return entry_fmt;
116 :
117 : cleanup:
118 0 : free(insbuf);
119 0 : free(namebuf);
120 0 : return NULL;
121 434 : }
122 :
123 104 : static void FreeMethods(NaClSrpcMethodDesc* methods, uint32_t method_count) {
124 104 : uint32_t i;
125 :
126 104 : if (NULL == methods) {
127 0 : return;
128 : }
129 854 : for (i = 0; i < method_count; ++i) {
130 323 : if (NULL == methods[i].name) {
131 : /* We have reached the end of the portion set by ParseOneEntry calls. */
132 0 : break;
133 : }
134 323 : free((char*) methods[i].name);
135 323 : free((char*) methods[i].input_types);
136 323 : free((char*) methods[i].output_types);
137 323 : }
138 104 : free(methods);
139 208 : }
140 :
141 : /*
142 : * The method tables passed to construction do not contain "intrinsic" methods
143 : * such as service discovery and shutdown. Build a complete table including
144 : * those from a given input.
145 : */
146 : static NaClSrpcMethodDesc* BuildMethods(
147 74 : const struct NaClSrpcHandlerDesc* methods,
148 74 : uint32_t* method_count) {
149 : static const char* kSDDescString = "service_discovery::C";
150 74 : NaClSrpcMethodDesc* complete_methods = NULL;
151 74 : uint32_t i;
152 74 : const char* nul_loc;
153 :
154 : /* Compute the number of methods to export. */
155 74 : *method_count = 0;
156 280 : while (NULL != methods[*method_count].entry_fmt)
157 132 : ++*method_count;
158 : /* Add one extra method for service discovery. */
159 74 : ++*method_count;
160 : /* Allocate the method descriptors. One extra for NULL termination. */
161 : complete_methods = (NaClSrpcMethodDesc*)
162 74 : calloc(*method_count + 1, sizeof(*complete_methods));
163 74 : if (NULL == complete_methods) {
164 0 : return NULL;
165 : }
166 : /* Copy the methods passed in, adding service discovery as element zero. */
167 74 : nul_loc = ParseOneEntry(kSDDescString,
168 : (char**) &complete_methods[0].name,
169 : (char**) &complete_methods[0].input_types,
170 : (char**) &complete_methods[0].output_types);
171 74 : if (nul_loc == NULL) {
172 0 : goto cleanup;
173 : }
174 74 : complete_methods[0].handler = ServiceDiscovery;
175 412 : for (i = 0; i < *method_count - 1; ++i) {
176 132 : nul_loc = ParseOneEntry(methods[i].entry_fmt,
177 : (char**) &complete_methods[i + 1].name,
178 : (char**) &complete_methods[i + 1].input_types,
179 : (char**) &complete_methods[i + 1].output_types);
180 132 : if (nul_loc == NULL) {
181 0 : goto cleanup;
182 : }
183 132 : complete_methods[i + 1].handler = methods[i].handler;
184 132 : }
185 : /* Add the NULL terminator */
186 74 : complete_methods[*method_count].name = NULL;
187 74 : complete_methods[*method_count].input_types = NULL;
188 74 : complete_methods[*method_count].output_types = NULL;
189 74 : complete_methods[*method_count].handler = NULL;
190 : /* Return the array */
191 74 : return complete_methods;
192 :
193 : cleanup:
194 0 : FreeMethods(complete_methods, *method_count);
195 0 : return NULL;
196 74 : }
197 :
198 : /*
199 : * Builds a service discovery string from an array of method descriptions.
200 : */
201 74 : static char* BuildSDString(const NaClSrpcMethodDesc* methods,
202 74 : uint32_t method_count,
203 74 : nacl_abi_size_t *length) {
204 74 : uint32_t i;
205 74 : char* p;
206 74 : char* str;
207 :
208 : /* Find the total length of the method description strings. */
209 74 : *length = 1;
210 560 : for (i = 0; i < method_count; ++i) {
211 206 : *length += nacl_abi_size_t_saturate(
212 206 : strlen(methods[i].name) + 1 +
213 206 : strlen(methods[i].input_types) + 1 +
214 206 : strlen(methods[i].output_types) + 1);
215 206 : }
216 : /* Allocate the string. */
217 74 : str = (char*) malloc(*length + 1);
218 74 : if (NULL == str) {
219 0 : return NULL;
220 : }
221 : /* Concatenate the method description strings into the string. */
222 74 : p = str;
223 560 : for (i = 0; i < method_count; ++i) {
224 206 : size_t buf_limit = str + *length - p;
225 : /* TODO(robertm): explore use portability_io.h */
226 : #if NACL_WINDOWS
227 : p += _snprintf(p, buf_limit, "%s:%s:%s\n",
228 : methods[i].name,
229 : methods[i].input_types,
230 : methods[i].output_types);
231 : #else
232 206 : p += snprintf(p, buf_limit, "%s:%s:%s\n",
233 : methods[i].name,
234 : methods[i].input_types,
235 : methods[i].output_types);
236 : #endif
237 206 : }
238 74 : *p = '\0';
239 : /* Return the resulting string. */
240 74 : return str;
241 74 : }
242 :
243 : /*
244 : * Create a service descriptor from an array of methods.
245 : */
246 74 : int NaClSrpcServiceHandlerCtor(NaClSrpcService* service,
247 74 : const NaClSrpcHandlerDesc* handler_desc) {
248 74 : char* service_str;
249 74 : nacl_abi_size_t str_length;
250 74 : NaClSrpcMethodDesc* methods = NULL;
251 74 : uint32_t method_count = 0;
252 :
253 : /* Initialize the struct, so that failures can be cleaned up properly. */
254 74 : service->service_string = NULL;
255 74 : service->service_string_length = 0;
256 74 : service->rpc_descr = NULL;
257 74 : service->rpc_count = 0;
258 : /* Add the service_discovery method to the table. */
259 74 : methods = BuildMethods(handler_desc, &method_count);
260 74 : if (NULL == methods) {
261 0 : goto cleanup;
262 : }
263 74 : service_str = BuildSDString(methods, method_count, &str_length);
264 74 : if (NULL == service_str) {
265 0 : goto cleanup;
266 : }
267 74 : service->service_string = service_str;
268 74 : service->service_string_length = str_length;
269 74 : service->rpc_descr = methods;
270 74 : service->rpc_count = method_count;
271 74 : return 1;
272 : cleanup:
273 0 : FreeMethods(methods, method_count);
274 0 : return 0;
275 74 : }
276 :
277 48 : int NaClSrpcServiceStringCtor(NaClSrpcService* service, const char* str) {
278 48 : NaClSrpcMethodDesc* methods = NULL;
279 48 : const char* p;
280 48 : uint32_t i;
281 48 : uint32_t rpc_count;
282 48 : size_t rpc_count_size_t;
283 :
284 : /* Initialize the struct, so that failures can be cleaned up properly. */
285 48 : service->service_string = NULL;
286 48 : service->service_string_length = 0;
287 48 : service->rpc_descr = NULL;
288 48 : service->rpc_count = 0;
289 : /* Count the number of rpc methods */
290 48 : rpc_count = 0;
291 324 : for (p = str; *p != '\0'; ) {
292 228 : char* next_p = strchr(p, '\n');
293 228 : if (NULL == next_p) {
294 : /* malformed input -- no remaining \n character was found */
295 0 : goto cleanup;
296 : }
297 228 : p = next_p + 1;
298 228 : ++rpc_count;
299 228 : if (0 == rpc_count) {
300 : /* uint32_t overflow detected. */
301 0 : goto cleanup;
302 : }
303 228 : }
304 : /*
305 : * The front end knows the next comparison is useless due to the range of
306 : * uint32_t. And furthermore at least one version of gcc knows that a
307 : * cast doesn't really change that fact. Hence, assign to a new variable
308 : * of size_t type.
309 : */
310 48 : rpc_count_size_t = (size_t) rpc_count;
311 : /* Allocate and clear the descriptor array */
312 48 : if (rpc_count_size_t >= SIZE_T_MAX / sizeof(*methods)) {
313 0 : goto cleanup;
314 : }
315 48 : methods = (NaClSrpcMethodDesc*) malloc(rpc_count_size_t * sizeof(*methods));
316 48 : if (NULL == methods) {
317 0 : goto cleanup;
318 : }
319 144 : memset(methods, 0, rpc_count_size_t * sizeof(*methods));
320 : /* Parse the list of method descriptions */
321 48 : p = str;
322 552 : for (i = 0; i < rpc_count; ++i) {
323 228 : const char* newline_loc;
324 :
325 228 : newline_loc = ParseOneEntry(p,
326 : (char**) &methods[i].name,
327 : (char**) &methods[i].input_types,
328 : (char**) &methods[i].output_types);
329 456 : if (NULL == newline_loc || '\n' != *newline_loc) {
330 0 : goto cleanup;
331 : }
332 228 : p = newline_loc + 1;
333 : /* The handler is not set from service_discovery strings */
334 228 : methods[i].handler = NULL;
335 228 : }
336 48 : service->service_string = strdup(str);
337 48 : service->service_string_length = nacl_abi_size_t_saturate(strlen(str));
338 48 : service->rpc_descr = methods;
339 48 : service->rpc_count = rpc_count;
340 48 : return 1;
341 :
342 : cleanup:
343 0 : FreeMethods(methods, rpc_count);
344 0 : return 0;
345 48 : }
346 :
347 : /*
348 : * Destroy a service descriptor.
349 : */
350 214 : void NaClSrpcServiceDtor(NaClSrpcService* service) {
351 214 : if (NULL == service) {
352 110 : return;
353 : }
354 : /* Free the method descriptors. */
355 104 : FreeMethods((NaClSrpcMethodDesc*) service->rpc_descr, service->rpc_count);
356 : /* Free the service discovery string. */
357 104 : free((char*) service->service_string);
358 318 : }
359 :
360 0 : void NaClSrpcServicePrint(const NaClSrpcService *service) {
361 0 : uint32_t i;
362 :
363 0 : if (NULL == service) {
364 0 : printf("NULL service\n");
365 0 : return;
366 : }
367 0 : printf("RPC %-20s %-10s %-10s\n", "Name", "Input args", "Output args");
368 0 : for (i = 0; i < service->rpc_count; ++i) {
369 0 : printf("%3u %-20s %-10s %-10s\n",
370 : (unsigned int) i,
371 : service->rpc_descr[i].name,
372 : service->rpc_descr[i].input_types,
373 : service->rpc_descr[i].output_types);
374 0 : }
375 0 : }
376 :
377 0 : uint32_t NaClSrpcServiceMethodCount(const NaClSrpcService *service) {
378 0 : if (NULL == service) {
379 0 : return 0;
380 : }
381 0 : return service->rpc_count;
382 0 : }
383 :
384 463 : static int SignatureMatches(NaClSrpcMethodDesc* method_desc,
385 463 : char const* signature) {
386 : struct {
387 : char const* field;
388 : char terminator;
389 463 : } matcher[3];
390 463 : size_t ix;
391 :
392 463 : matcher[0].field = method_desc->name;
393 463 : matcher[0].terminator = ':';
394 463 : matcher[1].field = method_desc->input_types;
395 463 : matcher[1].terminator = ':';
396 463 : matcher[2].field = method_desc->output_types;
397 463 : matcher[2].terminator = '\0';
398 :
399 1664 : for (ix = 0; ix < sizeof(matcher) / sizeof(matcher[0]); ++ix) {
400 709 : size_t length = strlen(matcher[ix].field);
401 1085 : if (strncmp(matcher[ix].field, signature, length) ||
402 : matcher[ix].terminator != signature[length]) {
403 340 : return 0;
404 : }
405 369 : signature += length + 1;
406 369 : }
407 123 : return 1;
408 463 : }
409 :
410 123 : uint32_t NaClSrpcServiceMethodIndex(const NaClSrpcService* service,
411 123 : char const* signature) {
412 123 : uint32_t i;
413 :
414 123 : if (NULL == service) {
415 0 : return kNaClSrpcInvalidMethodIndex;
416 : }
417 926 : for (i = 0; i < service->rpc_count; ++i) {
418 463 : if (SignatureMatches(&(service->rpc_descr[i]), signature)) {
419 123 : return i;
420 : }
421 340 : }
422 0 : return kNaClSrpcInvalidMethodIndex;
423 123 : }
424 :
425 443 : int NaClSrpcServiceMethodNameAndTypes(const NaClSrpcService* service,
426 443 : uint32_t rpc_number,
427 443 : const char** name,
428 443 : const char** input_types,
429 443 : const char** output_types) {
430 443 : if (rpc_number >= service->rpc_count) {
431 : /* This ensures that the method is in the user-defined set. */
432 0 : return 0;
433 : } else {
434 443 : *name = service->rpc_descr[rpc_number].name;
435 443 : *input_types = service->rpc_descr[rpc_number].input_types;
436 443 : *output_types = service->rpc_descr[rpc_number].output_types;
437 : }
438 443 : return 1;
439 443 : }
440 :
441 65 : NaClSrpcMethod NaClSrpcServiceMethod(const NaClSrpcService* service,
442 65 : uint32_t rpc_number) {
443 130 : if (NULL == service || rpc_number >= service->rpc_count) {
444 0 : return NULL;
445 : } else {
446 65 : return service->rpc_descr[rpc_number].handler;
447 : }
448 65 : }
449 :
450 25 : static void ServiceDiscovery(NaClSrpcRpc* rpc,
451 25 : NaClSrpcArg** in_args,
452 25 : NaClSrpcArg** out_args,
453 25 : NaClSrpcClosure* done) {
454 50 : UNREFERENCED_PARAMETER(in_args);
455 25 : rpc->result = NACL_SRPC_RESULT_APP_ERROR;
456 50 : if (NULL == rpc->channel || NULL == rpc->channel->server) {
457 0 : done->Run(done);
458 0 : return;
459 : }
460 25 : if (out_args[0]->u.count >= rpc->channel->server->service_string_length) {
461 75 : strncpy(out_args[0]->arrays.carr,
462 : rpc->channel->server->service_string,
463 : rpc->channel->server->service_string_length);
464 : /* Set the length of the string actually returned. */
465 25 : out_args[0]->u.count = rpc->channel->server->service_string_length;
466 25 : rpc->result = NACL_SRPC_RESULT_OK;
467 25 : }
468 25 : done->Run(done);
469 50 : }
|