1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include <string.h>
21 #include <stdlib.h>
22 #include <limits.h>
23 #include <stdarg.h>
24 #include <errno.h>
25 #include <assert.h>
26 #include "host/ble_hs.h"
27 #include "host/ble_uuid.h"
28 #include "host/ble_eddystone.h"
29 #include "cmd.h"
30 #include "btshell.h"
31
32 #include "nimble/npl_shell.h"
33
34 #define CMD_MAX_ARGS 16
35
36 static char *cmd_args[CMD_MAX_ARGS][2];
37 static int cmd_num_args;
38
39 int
parse_arg_find_idx(const char * key)40 parse_arg_find_idx(const char *key)
41 {
42 int i;
43
44 for (i = 0; i < cmd_num_args; i++) {
45 if (strcmp(cmd_args[i][0], key) == 0) {
46 return i;
47 }
48 }
49
50 return -1;
51 }
52
53 char *
parse_arg_peek(const char * key)54 parse_arg_peek(const char *key)
55 {
56 int i;
57
58 for (i = 0; i < cmd_num_args; i++) {
59 if (strcmp(cmd_args[i][0], key) == 0) {
60 return cmd_args[i][1];
61 }
62 }
63
64 return NULL;
65 }
66
67 char *
parse_arg_extract(const char * key)68 parse_arg_extract(const char *key)
69 {
70 int i;
71
72 for (i = 0; i < cmd_num_args; i++) {
73 if (strcmp(cmd_args[i][0], key) == 0) {
74 /* Erase parameter. */
75 cmd_args[i][0][0] = '\0';
76
77 return cmd_args[i][1];
78 }
79 }
80
81 return NULL;
82 }
83
84 /**
85 * Determines which number base to use when parsing the specified numeric
86 * string. This just avoids base '0' so that numbers don't get interpreted as
87 * octal.
88 */
89 static int
parse_arg_long_base(char * sval)90 parse_arg_long_base(char *sval)
91 {
92 if (sval[0] == '0' && sval[1] == 'x') {
93 return 0;
94 } else {
95 return 10;
96 }
97 }
98
99 long
parse_long_bounds(char * sval,long min,long max,int * out_status)100 parse_long_bounds(char *sval, long min, long max, int *out_status)
101 {
102 char *endptr;
103 long lval;
104
105 lval = strtol(sval, &endptr, parse_arg_long_base(sval));
106 if (sval[0] != '\0' && *endptr == '\0' &&
107 lval >= min && lval <= max) {
108
109 *out_status = 0;
110 return lval;
111 }
112
113 *out_status = EINVAL;
114 return 0;
115 }
116
117 long
parse_arg_long_bounds_peek(char * name,long min,long max,int * out_status)118 parse_arg_long_bounds_peek(char *name, long min, long max, int *out_status)
119 {
120 char *sval;
121
122 sval = parse_arg_peek(name);
123 if (sval == NULL) {
124 *out_status = ENOENT;
125 return 0;
126 }
127 return parse_long_bounds(sval, min, max, out_status);
128 }
129
130 long
parse_arg_long_bounds(char * name,long min,long max,int * out_status)131 parse_arg_long_bounds(char *name, long min, long max, int *out_status)
132 {
133 char *sval;
134
135 sval = parse_arg_extract(name);
136 if (sval == NULL) {
137 *out_status = ENOENT;
138 return 0;
139 }
140 return parse_long_bounds(sval, min, max, out_status);
141 }
142
143 long
parse_arg_long_bounds_dflt(char * name,long min,long max,long dflt,int * out_status)144 parse_arg_long_bounds_dflt(char *name, long min, long max,
145 long dflt, int *out_status)
146 {
147 long val;
148 int rc;
149
150 val = parse_arg_long_bounds(name, min, max, &rc);
151 if (rc == ENOENT) {
152 rc = 0;
153 val = dflt;
154 }
155
156 *out_status = rc;
157
158 return val;
159 }
160
161 uint64_t
parse_arg_uint64_bounds(char * name,uint64_t min,uint64_t max,int * out_status)162 parse_arg_uint64_bounds(char *name, uint64_t min, uint64_t max, int *out_status)
163 {
164 char *endptr;
165 char *sval;
166 uint64_t lval;
167
168 sval = parse_arg_extract(name);
169 if (sval == NULL) {
170 *out_status = ENOENT;
171 return 0;
172 }
173
174 lval = strtoull(sval, &endptr, parse_arg_long_base(sval));
175 if (sval[0] != '\0' && *endptr == '\0' &&
176 lval >= min && lval <= max) {
177
178 *out_status = 0;
179 return lval;
180 }
181
182 *out_status = EINVAL;
183 return 0;
184 }
185
186 long
parse_arg_long(char * name,int * out_status)187 parse_arg_long(char *name, int *out_status)
188 {
189 return parse_arg_long_bounds(name, LONG_MIN, LONG_MAX, out_status);
190 }
191
192 uint8_t
parse_arg_bool(char * name,int * out_status)193 parse_arg_bool(char *name, int *out_status)
194 {
195 return parse_arg_long_bounds(name, 0, 1, out_status);
196 }
197
198 uint8_t
parse_arg_bool_dflt(char * name,uint8_t dflt,int * out_status)199 parse_arg_bool_dflt(char *name, uint8_t dflt, int *out_status)
200 {
201 return parse_arg_long_bounds_dflt(name, 0, 1, dflt, out_status);
202 }
203
204 uint8_t
parse_arg_uint8(char * name,int * out_status)205 parse_arg_uint8(char *name, int *out_status)
206 {
207 return parse_arg_long_bounds(name, 0, UINT8_MAX, out_status);
208 }
209
210 uint16_t
parse_arg_uint16(char * name,int * out_status)211 parse_arg_uint16(char *name, int *out_status)
212 {
213 return parse_arg_long_bounds(name, 0, UINT16_MAX, out_status);
214 }
215
216 uint16_t
parse_arg_uint16_peek(char * name,int * out_status)217 parse_arg_uint16_peek(char *name, int *out_status)
218 {
219 return parse_arg_long_bounds_peek(name, 0, UINT16_MAX, out_status);
220 }
221
222 uint32_t
parse_arg_uint32(char * name,int * out_status)223 parse_arg_uint32(char *name, int *out_status)
224 {
225 return parse_arg_uint64_bounds(name, 0, UINT32_MAX, out_status);
226 }
227
228 uint64_t
parse_arg_uint64(char * name,int * out_status)229 parse_arg_uint64(char *name, int *out_status)
230 {
231 return parse_arg_uint64_bounds(name, 0, UINT64_MAX, out_status);
232 }
233
234 uint8_t
parse_arg_uint8_dflt(char * name,uint8_t dflt,int * out_status)235 parse_arg_uint8_dflt(char *name, uint8_t dflt, int *out_status)
236 {
237 uint8_t val;
238 int rc;
239
240 val = parse_arg_uint8(name, &rc);
241 if (rc == ENOENT) {
242 val = dflt;
243 rc = 0;
244 }
245
246 *out_status = rc;
247 return val;
248 }
249
250 uint16_t
parse_arg_uint16_dflt(char * name,uint16_t dflt,int * out_status)251 parse_arg_uint16_dflt(char *name, uint16_t dflt, int *out_status)
252 {
253 uint16_t val;
254 int rc;
255
256 val = parse_arg_uint16(name, &rc);
257 if (rc == ENOENT) {
258 val = dflt;
259 rc = 0;
260 }
261
262 *out_status = rc;
263 return val;
264 }
265
266 uint32_t
parse_arg_uint32_dflt(char * name,uint32_t dflt,int * out_status)267 parse_arg_uint32_dflt(char *name, uint32_t dflt, int *out_status)
268 {
269 uint32_t val;
270 int rc;
271
272 val = parse_arg_uint32(name, &rc);
273 if (rc == ENOENT) {
274 val = dflt;
275 rc = 0;
276 }
277
278 *out_status = rc;
279 return val;
280 }
281
282 const struct kv_pair *
parse_kv_find(const struct kv_pair * kvs,char * name)283 parse_kv_find(const struct kv_pair *kvs, char *name)
284 {
285 const struct kv_pair *kv;
286 int i;
287
288 for (i = 0; kvs[i].key != NULL; i++) {
289 kv = kvs + i;
290 if (strcmp(name, kv->key) == 0) {
291 return kv;
292 }
293 }
294
295 return NULL;
296 }
297
298 int
parse_arg_kv(char * name,const struct kv_pair * kvs,int * out_status)299 parse_arg_kv(char *name, const struct kv_pair *kvs, int *out_status)
300 {
301 const struct kv_pair *kv;
302 char *sval;
303
304 sval = parse_arg_extract(name);
305 if (sval == NULL) {
306 *out_status = ENOENT;
307 return -1;
308 }
309
310 kv = parse_kv_find(kvs, sval);
311 if (kv == NULL) {
312 *out_status = EINVAL;
313 return -1;
314 }
315
316 *out_status = 0;
317 return kv->val;
318 }
319
320 int
parse_arg_kv_dflt(char * name,const struct kv_pair * kvs,int def_val,int * out_status)321 parse_arg_kv_dflt(char *name, const struct kv_pair *kvs, int def_val,
322 int *out_status)
323 {
324 int val;
325 int rc;
326
327 val = parse_arg_kv(name, kvs, &rc);
328 if (rc == ENOENT) {
329 rc = 0;
330 val = def_val;
331 }
332
333 *out_status = rc;
334
335 return val;
336 }
337
338
339 static int
parse_arg_byte_stream_delim(char * sval,char * delims,int max_len,uint8_t * dst,int * out_len)340 parse_arg_byte_stream_delim(char *sval, char *delims, int max_len,
341 uint8_t *dst, int *out_len)
342 {
343 unsigned long ul;
344 char *endptr;
345 char *token;
346 int i;
347
348 i = 0;
349 for (token = strtok(sval, delims);
350 token != NULL;
351 token = strtok(NULL, delims)) {
352
353 if (i >= max_len) {
354 return EINVAL;
355 }
356
357 ul = strtoul(token, &endptr, 16);
358 if (sval[0] == '\0' || *endptr != '\0' || ul > UINT8_MAX) {
359 return -1;
360 }
361
362 dst[i] = ul;
363 i++;
364 }
365
366 *out_len = i;
367
368 return 0;
369 }
370
371 int
parse_arg_byte_stream(char * name,int max_len,uint8_t * dst,int * out_len)372 parse_arg_byte_stream(char *name, int max_len, uint8_t *dst, int *out_len)
373 {
374 char *sval;
375
376 sval = parse_arg_extract(name);
377 if (sval == NULL) {
378 return ENOENT;
379 }
380
381 return parse_arg_byte_stream_delim(sval, ":-", max_len, dst, out_len);
382 }
383
384 int
parse_arg_byte_stream_exact_length(char * name,uint8_t * dst,int len)385 parse_arg_byte_stream_exact_length(char *name, uint8_t *dst, int len)
386 {
387 int actual_len;
388 int rc;
389
390 rc = parse_arg_byte_stream(name, len, dst, &actual_len);
391 if (rc != 0) {
392 return rc;
393 }
394
395 if (actual_len != len) {
396 return EINVAL;
397 }
398
399 return 0;
400 }
401
402 static void
parse_reverse_bytes(uint8_t * bytes,int len)403 parse_reverse_bytes(uint8_t *bytes, int len)
404 {
405 uint8_t tmp;
406 int i;
407
408 for (i = 0; i < len / 2; i++) {
409 tmp = bytes[i];
410 bytes[i] = bytes[len - i - 1];
411 bytes[len - i - 1] = tmp;
412 }
413 }
414
415 int
parse_arg_mac(char * name,uint8_t * dst)416 parse_arg_mac(char *name, uint8_t *dst)
417 {
418 int rc;
419
420 rc = parse_arg_byte_stream_exact_length(name, dst, 6);
421 if (rc != 0) {
422 return rc;
423 }
424
425 parse_reverse_bytes(dst, 6);
426
427 return 0;
428 }
429
430 int
parse_arg_uuid(char * str,ble_uuid_any_t * uuid)431 parse_arg_uuid(char *str, ble_uuid_any_t *uuid)
432 {
433 uint16_t uuid16;
434 uint8_t val[16];
435 int len;
436 int rc;
437
438 uuid16 = parse_arg_uint16_peek(str, &rc);
439 switch (rc) {
440 case ENOENT:
441 parse_arg_extract(str);
442 return ENOENT;
443
444 case 0:
445 len = 2;
446 val[0] = uuid16;
447 val[1] = uuid16 >> 8;
448 parse_arg_extract(str);
449 break;
450
451 default:
452 len = 16;
453 rc = parse_arg_byte_stream_exact_length(str, val, 16);
454 if (rc != 0) {
455 return EINVAL;
456 }
457 parse_reverse_bytes(val, 16);
458 break;
459 }
460
461 rc = ble_uuid_init_from_buf(uuid, val, len);
462 if (rc != 0) {
463 return EINVAL;
464 } else {
465 return 0;
466 }
467 }
468
469 int
parse_arg_all(int argc,char ** argv)470 parse_arg_all(int argc, char **argv)
471 {
472 char *key;
473 char *val;
474 int i;
475
476 cmd_num_args = 0;
477
478 for (i = 0; i < argc; i++) {
479 key = strtok(argv[i], "=");
480 val = strtok(NULL, "=");
481
482 if (key != NULL && val != NULL) {
483 if (strlen(key) == 0) {
484 console_printf("Error: invalid argument: %s\n", argv[i]);
485 return -1;
486 }
487
488 if (cmd_num_args >= CMD_MAX_ARGS) {
489 console_printf("Error: too many arguments");
490 return -1;
491 }
492
493 cmd_args[cmd_num_args][0] = key;
494 cmd_args[cmd_num_args][1] = val;
495 cmd_num_args++;
496 }
497 }
498
499 return 0;
500 }
501
502 int
parse_eddystone_url(char * full_url,uint8_t * out_scheme,char * out_body,uint8_t * out_body_len,uint8_t * out_suffix)503 parse_eddystone_url(char *full_url, uint8_t *out_scheme, char *out_body,
504 uint8_t *out_body_len, uint8_t *out_suffix)
505 {
506 static const struct {
507 char *s;
508 uint8_t scheme;
509 } schemes[] = {
510 { "http://www.", BLE_EDDYSTONE_URL_SCHEME_HTTP_WWW },
511 { "https://www.", BLE_EDDYSTONE_URL_SCHEME_HTTPS_WWW },
512 { "http://", BLE_EDDYSTONE_URL_SCHEME_HTTP },
513 { "https://", BLE_EDDYSTONE_URL_SCHEME_HTTPS },
514 };
515
516 static const struct {
517 char *s;
518 uint8_t code;
519 } suffixes[] = {
520 { ".com/", BLE_EDDYSTONE_URL_SUFFIX_COM_SLASH },
521 { ".org/", BLE_EDDYSTONE_URL_SUFFIX_ORG_SLASH },
522 { ".edu/", BLE_EDDYSTONE_URL_SUFFIX_EDU_SLASH },
523 { ".net/", BLE_EDDYSTONE_URL_SUFFIX_NET_SLASH },
524 { ".info/", BLE_EDDYSTONE_URL_SUFFIX_INFO_SLASH },
525 { ".biz/", BLE_EDDYSTONE_URL_SUFFIX_BIZ_SLASH },
526 { ".gov/", BLE_EDDYSTONE_URL_SUFFIX_GOV_SLASH },
527 { ".com", BLE_EDDYSTONE_URL_SUFFIX_COM },
528 { ".org", BLE_EDDYSTONE_URL_SUFFIX_ORG },
529 { ".edu", BLE_EDDYSTONE_URL_SUFFIX_EDU },
530 { ".net", BLE_EDDYSTONE_URL_SUFFIX_NET },
531 { ".info", BLE_EDDYSTONE_URL_SUFFIX_INFO },
532 { ".biz", BLE_EDDYSTONE_URL_SUFFIX_BIZ },
533 { ".gov", BLE_EDDYSTONE_URL_SUFFIX_GOV },
534 };
535
536 char *prefix;
537 char *suffix;
538 int full_url_len;
539 int prefix_len;
540 int suffix_len;
541 int suffix_idx;
542 int rc;
543 int i;
544
545 full_url_len = strlen(full_url);
546
547 rc = BLE_HS_EINVAL;
548 for (i = 0; i < sizeof schemes / sizeof schemes[0]; i++) {
549 prefix = schemes[i].s;
550 prefix_len = strlen(schemes[i].s);
551
552 if (full_url_len >= prefix_len &&
553 memcmp(full_url, prefix, prefix_len) == 0) {
554
555 *out_scheme = i;
556 rc = 0;
557 break;
558 }
559 }
560 if (rc != 0) {
561 return rc;
562 }
563
564 rc = BLE_HS_EINVAL;
565 for (i = 0; i < sizeof suffixes / sizeof suffixes[0]; i++) {
566 suffix = suffixes[i].s;
567 suffix_len = strlen(suffixes[i].s);
568
569 suffix_idx = full_url_len - suffix_len;
570 if (suffix_idx >= prefix_len &&
571 memcmp(full_url + suffix_idx, suffix, suffix_len) == 0) {
572
573 *out_suffix = i;
574 rc = 0;
575 break;
576 }
577 }
578 if (rc != 0) {
579 *out_suffix = BLE_EDDYSTONE_URL_SUFFIX_NONE;
580 *out_body_len = full_url_len - prefix_len;
581 } else {
582 *out_body_len = full_url_len - prefix_len - suffix_len;
583 }
584
585 memcpy(out_body, full_url + prefix_len, *out_body_len);
586
587 return 0;
588 }
589