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