1 /*
2 * Copyright (C) 2015-2018 Alibaba Group Holding Limited
3 */
4 #include <stdio.h>
5
6 #include "string.h"
7 #include "linkkit/infra/infra_types.h"
8 #include "linkkit/infra/infra_defs.h"
9 #include "linkkit/infra/infra_string.h"
10 #include "linkkit/infra/infra_httpc.h"
11 #include "linkkit/infra/infra_sha256.h"
12 #include "dynreg_internal.h"
13 #include "linkkit/dynreg_api.h"
14 #ifdef MQTT_DYNAMIC_REGISTER
15 #include "linkkit/dev_sign_api.h"
16 #include "linkkit/mqtt_api.h"
17 #endif
18
19 #define HTTP_RESPONSE_PAYLOAD_LEN (256)
20
21 #define DYNREG_RANDOM_KEY_LENGTH (15)
22 #define DYNREG_SIGN_LENGTH (65)
23 #define DYNREG_SIGN_METHOD_HMACSHA256 "hmacsha256"
24
25 typedef struct {
26 char *payload;
27 int payload_len;
28 } dynreg_http_response_t;
29
_parse_string_value(char * payload,int * pos,int * start,int * end)30 static int _parse_string_value(char *payload, int *pos, int *start, int *end)
31 {
32 int idx = 0;
33
34 for (idx = *pos + 1; idx < strlen(payload); idx++) {
35 if (payload[idx] == '\"') {
36 break;
37 }
38 }
39 *start = *pos + 1;
40 *end = idx - 1;
41 *pos = idx;
42
43 return 0;
44 }
45
_parse_dynreg_value(char * payload,char * key,int * pos,int * start,int * end)46 static int _parse_dynreg_value(char *payload, char *key, int *pos, int *start,
47 int *end)
48 {
49 int idx = 0;
50 /* printf("=====%s\n",&payload[*pos]); */
51
52 if (memcmp(key, "code", strlen("code")) == 0) {
53 for (idx = *pos; idx < strlen(payload); idx++) {
54 if (payload[idx] < '0' || payload[idx] > '9') {
55 break;
56 }
57 }
58 *start = *pos;
59 *end = idx - 1;
60 *pos = *end;
61 return 0;
62 } else if (memcmp(key, "data", strlen("data")) == 0) {
63 int bracket_cnt = 0;
64 if (payload[*pos] != '{') {
65 return -1;
66 }
67 for (idx = *pos; idx < strlen(payload); idx++) {
68 if (payload[idx] == '{') {
69 bracket_cnt++;
70 } else if (payload[idx] == '}') {
71 bracket_cnt--;
72 }
73 if (bracket_cnt == 0) {
74 break;
75 }
76 }
77 *start = *pos;
78 *end = idx;
79 *pos = *end;
80 return 0;
81 } else {
82 if (payload[*pos] != '\"') {
83 return -1;
84 }
85 return _parse_string_value(payload, pos, start, end);
86 }
87
88 /* return -1; */
89 }
90
_parse_dynreg_result(char * payload,char * key,int * start,int * end)91 static int _parse_dynreg_result(char *payload, char *key, int *start, int *end)
92 {
93 int res = 0, idx = 0, pos = 0;
94
95 for (idx = 0; idx < strlen(payload); idx++) {
96 /* printf("loop start: %s\n",&payload[idx]); */
97 if (payload[idx] == '\"') {
98 for (pos = idx + 1; pos < strlen(payload); pos++) {
99 if (payload[pos] == '\"') {
100 /* printf("key: %.*s\n",pos - idx - 1, &payload[idx+1]); */
101 break;
102 }
103 }
104
105 if (pos == strlen(payload) || payload[pos + 1] != ':') {
106 return -1;
107 }
108
109 pos += 2;
110 res = _parse_dynreg_value(payload, key, &pos, start, end);
111 if (res == 0 && memcmp(key, &payload[idx + 1], strlen(key)) == 0) {
112 /* printf("value: %.*s\n",*end - *start + 1,&payload[*start]);
113 */
114 return 0;
115 }
116
117 idx = pos;
118 }
119 }
120
121 printf("exit 4\n");
122 return -1;
123 }
124
_calc_dynreg_sign(char product_key[IOTX_PRODUCT_KEY_LEN],char product_secret[IOTX_PRODUCT_SECRET_LEN],char device_name[IOTX_DEVICE_NAME_LEN],char random[DYNREG_RANDOM_KEY_LENGTH+1],char sign[DYNREG_SIGN_LENGTH])125 static int _calc_dynreg_sign(char product_key[IOTX_PRODUCT_KEY_LEN],
126 char product_secret[IOTX_PRODUCT_SECRET_LEN],
127 char device_name[IOTX_DEVICE_NAME_LEN],
128 char random[DYNREG_RANDOM_KEY_LENGTH + 1],
129 char sign[DYNREG_SIGN_LENGTH])
130 {
131 int sign_source_len = 0;
132 uint8_t signnum[32];
133 uint8_t *sign_source = NULL;
134 const char *dynamic_register_sign_fmt = "deviceName%sproductKey%srandom%s";
135
136 /* Start Dynamic Register */
137 memset(random, 0, DYNREG_RANDOM_KEY_LENGTH + 1);
138 memcpy(random, "8Ygb7ULYh53B6OA", strlen("8Ygb7ULYh53B6OA"));
139 dynreg_info("Random Key: %s", random);
140
141 /* Calculate SHA256 Value */
142 sign_source_len = strlen(dynamic_register_sign_fmt) + strlen(device_name) +
143 strlen(product_key) + strlen(random) + 1;
144 sign_source = dynreg_malloc(sign_source_len);
145 if (sign_source == NULL) {
146 dynreg_err("Memory Not Enough");
147 return FAIL_RETURN;
148 }
149 memset(sign_source, 0, sign_source_len);
150 HAL_Snprintf((char *)sign_source, sign_source_len,
151 dynamic_register_sign_fmt, device_name, product_key, random);
152
153 utils_hmac_sha256(sign_source, strlen((const char *)sign_source),
154 (uint8_t *)product_secret, strlen(product_secret),
155 signnum);
156 infra_hex2str(signnum, 32, sign);
157 dynreg_free(sign_source);
158 dynreg_info("Sign: %s", sign);
159
160 return SUCCESS_RETURN;
161 }
162
_recv_callback(char * ptr,int length,int total_length,void * userdata)163 static int _recv_callback(char *ptr, int length, int total_length,
164 void *userdata)
165 {
166 dynreg_http_response_t *response = (dynreg_http_response_t *)userdata;
167 if (strlen(response->payload) + length > response->payload_len) {
168 return FAIL_RETURN;
169 }
170 memcpy(response->payload + strlen(response->payload), ptr, length);
171
172 return length;
173 }
174
_fetch_dynreg_http_resp(char * request_payload,char * response_payload,iotx_http_region_types_t region,char device_secret[IOTX_DEVICE_SECRET_LEN])175 static int _fetch_dynreg_http_resp(char *request_payload,
176 char *response_payload,
177 iotx_http_region_types_t region,
178 char device_secret[IOTX_DEVICE_SECRET_LEN])
179 {
180 int res = 0;
181 const char *domain = NULL;
182 const char *url_format = "http://%s/auth/register/device";
183 char *url = NULL;
184 int url_len = 0;
185 const char *pub_key = NULL;
186 void *http_handle = NULL;
187 int port = 443;
188 iotx_http_method_t method = IOTX_HTTP_POST;
189 int timeout_ms = 10000;
190 char *header =
191 "Accept: text/xml,text/javascript,text/html,application/json\r\n"
192 "Content-Type: application/x-www-form-urlencoded\r\n";
193 int http_recv_maxlen = HTTP_RESPONSE_PAYLOAD_LEN;
194 dynreg_http_response_t response;
195 int start = 0, end = 0, data_start = 0, data_end = 0;
196
197 domain = g_infra_http_domain[region];
198 if (NULL == domain) {
199 dynreg_err("Get domain failed");
200 return FAIL_RETURN;
201 }
202 url_len = strlen(url_format) + strlen(domain) + 1;
203 url = (char *)dynreg_malloc(url_len);
204 if (NULL == url) {
205 dynreg_err("Not Enough Memory");
206 return FAIL_RETURN;
207 }
208 memset(url, 0, url_len);
209 HAL_Snprintf(url, url_len, url_format, domain);
210
211 memset(&response, 0, sizeof(dynreg_http_response_t));
212 response.payload = response_payload;
213 response.payload_len = HTTP_RESPONSE_PAYLOAD_LEN;
214 #ifdef SUPPORT_TLS
215 {
216 extern const char *iotx_ca_crt;
217 pub_key = iotx_ca_crt;
218 }
219 #endif
220
221 http_handle = wrapper_http_init();
222 wrapper_http_setopt(http_handle, IOTX_HTTPOPT_URL, (void *)url);
223 wrapper_http_setopt(http_handle, IOTX_HTTPOPT_PORT, (void *)&port);
224 wrapper_http_setopt(http_handle, IOTX_HTTPOPT_METHOD, (void *)&method);
225 wrapper_http_setopt(http_handle, IOTX_HTTPOPT_HEADER, (void *)header);
226 wrapper_http_setopt(http_handle, IOTX_HTTPOPT_CERT, (void *)pub_key);
227 wrapper_http_setopt(http_handle, IOTX_HTTPOPT_TIMEOUT, (void *)&timeout_ms);
228 wrapper_http_setopt(http_handle, IOTX_HTTPOPT_RECVCALLBACK,
229 (void *)_recv_callback);
230 wrapper_http_setopt(http_handle, IOTX_HTTPOPT_RECVMAXLEN,
231 (void *)&http_recv_maxlen);
232 wrapper_http_setopt(http_handle, IOTX_HTTPOPT_RECVCONTEXT,
233 (void *)&response);
234
235 res = wrapper_http_perform(http_handle, request_payload,
236 strlen(request_payload));
237 wrapper_http_deinit(&http_handle);
238
239 if (res < SUCCESS_RETURN) {
240 dynreg_err("Http Download Failed");
241 dynreg_free(url);
242 return FAIL_RETURN;
243 }
244 dynreg_free(url);
245 dynreg_info("Http Response Payload: %s", response_payload);
246
247 _parse_dynreg_result(response_payload, "code", &start, &end);
248 dynreg_info("Dynamic Register Code: %.*s", end - start + 1,
249 &response_payload[start]);
250
251 if (memcmp(&response_payload[start], "200", strlen("200")) != 0) {
252 return FAIL_RETURN;
253 }
254
255 _parse_dynreg_result(response_payload, "data", &data_start, &data_end);
256 /* dynreg_info("value: %.*s\n",data_end - data_start +
257 * 1,&response_payload[data_start]); */
258
259 _parse_dynreg_result(&response_payload[data_start + 1], "deviceSecret",
260 &start, &end);
261 dynreg_info("Dynamic Register Device Secret: %.*s", end - start + 1,
262 &response_payload[data_start + 1 + start]);
263
264 if (end - start + 1 > IOTX_DEVICE_SECRET_LEN) {
265 return FAIL_RETURN;
266 }
267
268 memcpy(device_secret, &response_payload[data_start + 1 + start],
269 end - start + 1);
270
271 return SUCCESS_RETURN;
272 }
273
_http_dynamic_register(iotx_http_region_types_t region,iotx_dev_meta_info_t * meta)274 int32_t _http_dynamic_register(iotx_http_region_types_t region,
275 iotx_dev_meta_info_t *meta)
276 {
277 int res = 0, dynamic_register_request_len = 0;
278 char sign[DYNREG_SIGN_LENGTH] = { 0 };
279 char random[DYNREG_RANDOM_KEY_LENGTH + 1] = { 0 };
280 const char *dynamic_register_format =
281 "productKey=%s&deviceName=%s&random=%s&sign=%s&signMethod=%s";
282 char *dynamic_register_request = NULL;
283 char *dynamic_register_response = NULL;
284
285 if (strlen(meta->product_key) > IOTX_PRODUCT_KEY_LEN ||
286 strlen(meta->product_secret) > IOTX_PRODUCT_SECRET_LEN ||
287 strlen(meta->device_name) > IOTX_DEVICE_NAME_LEN) {
288 return FAIL_RETURN;
289 }
290
291 /* Calcute Signature */
292 res = _calc_dynreg_sign(meta->product_key, meta->product_secret,
293 meta->device_name, random, sign);
294 if (res != SUCCESS_RETURN) {
295 dynreg_err("Calculate Sign Failed");
296 return FAIL_RETURN;
297 }
298
299 /* Assemble Http Dynamic Register Request Payload */
300 dynamic_register_request_len =
301 strlen(dynamic_register_format) + strlen(meta->product_key) +
302 strlen(meta->device_name) + strlen(random) + strlen(sign) +
303 strlen(DYNREG_SIGN_METHOD_HMACSHA256) + 1;
304 dynamic_register_request = dynreg_malloc(dynamic_register_request_len);
305 if (dynamic_register_request == NULL) {
306 dynreg_err("Not Enough Memory");
307 return FAIL_RETURN;
308 }
309 memset(dynamic_register_request, 0, dynamic_register_request_len);
310 HAL_Snprintf(dynamic_register_request, dynamic_register_request_len,
311 dynamic_register_format, meta->product_key, meta->device_name,
312 random, sign, DYNREG_SIGN_METHOD_HMACSHA256);
313
314 dynamic_register_response = dynreg_malloc(HTTP_RESPONSE_PAYLOAD_LEN);
315 if (dynamic_register_response == NULL) {
316 dynreg_err("Not Enough Memory");
317 dynreg_free(dynamic_register_request);
318 return FAIL_RETURN;
319 }
320 memset(dynamic_register_response, 0, HTTP_RESPONSE_PAYLOAD_LEN);
321
322 /* Send Http Request For Getting Device Secret */
323 res = _fetch_dynreg_http_resp(dynamic_register_request,
324 dynamic_register_response, region,
325 meta->device_secret);
326
327 #ifdef INFRA_LOG_NETWORK_PAYLOAD
328 dynreg_dbg("Downstream Payload:");
329 iotx_facility_json_print(dynamic_register_response, LOG_DEBUG_LEVEL, '<');
330 #endif
331
332 dynreg_free(dynamic_register_request);
333 dynreg_free(dynamic_register_response);
334 if (res != SUCCESS_RETURN) {
335 dynreg_err("Get Device Secret Failed");
336 return FAIL_RETURN;
337 }
338
339 return SUCCESS_RETURN;
340 }
341
342 #ifdef MQTT_DYNAMIC_REGISTER
_mqtt_dynreg_sign_password(iotx_dev_meta_info_t * meta_info,iotx_sign_mqtt_t * signout,char * rand)343 static int _mqtt_dynreg_sign_password(iotx_dev_meta_info_t *meta_info,
344 iotx_sign_mqtt_t *signout, char *rand)
345 {
346 char signsource[DEV_SIGN_SOURCE_MAXLEN] = { 0 };
347 uint16_t signsource_len = 0;
348 const char sign_fmt[] = "deviceName%sproductKey%srandom%s";
349 uint8_t sign_hex[32] = { 0 };
350
351 signsource_len = strlen(sign_fmt) + strlen(meta_info->device_name) + 1 +
352 strlen(meta_info->product_key) + strlen(rand) + 1;
353 if (signsource_len >= DEV_SIGN_SOURCE_MAXLEN) {
354 return ERROR_DEV_SIGN_SOURCE_TOO_SHORT;
355 }
356 memset(signsource, 0, signsource_len);
357 memcpy(signsource, "deviceName", strlen("deviceName"));
358 memcpy(signsource + strlen(signsource), meta_info->device_name,
359 strlen(meta_info->device_name));
360 memcpy(signsource + strlen(signsource), "productKey", strlen("productKey"));
361 memcpy(signsource + strlen(signsource), meta_info->product_key,
362 strlen(meta_info->product_key));
363 memcpy(signsource + strlen(signsource), "random", strlen("random"));
364 memcpy(signsource + strlen(signsource), rand, strlen(rand));
365
366 utils_hmac_sha256((const uint8_t *)signsource, strlen(signsource),
367 (uint8_t *)meta_info->product_secret,
368 strlen(meta_info->product_secret), sign_hex);
369 infra_hex2str(sign_hex, 32, signout->password);
370
371 return SUCCESS_RETURN;
372 }
373
_mqtt_dynreg_sign_clientid(iotx_dev_meta_info_t * meta_info,iotx_sign_mqtt_t * signout,char * rand)374 static int32_t _mqtt_dynreg_sign_clientid(iotx_dev_meta_info_t *meta_info,
375 iotx_sign_mqtt_t *signout, char *rand)
376 {
377 const char *clientid1 = "|authType=register,random=";
378 const char *clientid2 = ",signmethod=hmacsha256,securemode=2|";
379 uint32_t clientid_len = 0;
380
381 clientid_len = strlen(meta_info->product_key) + 1 +
382 strlen(meta_info->device_name) + strlen(clientid1) +
383 strlen(rand) + strlen(clientid2) + 1;
384 if (clientid_len >= DEV_SIGN_CLIENT_ID_MAXLEN) {
385 return ERROR_DEV_SIGN_CLIENT_ID_TOO_SHORT;
386 }
387 memset(signout->clientid, 0, clientid_len);
388 memcpy(signout->clientid, meta_info->product_key,
389 strlen(meta_info->product_key));
390 memcpy(signout->clientid + strlen(signout->clientid), ".", strlen("."));
391 memcpy(signout->clientid + strlen(signout->clientid),
392 meta_info->device_name, strlen(meta_info->device_name));
393 memcpy(signout->clientid + strlen(signout->clientid), clientid1,
394 strlen(clientid1));
395 memcpy(signout->clientid + strlen(signout->clientid), rand, strlen(rand));
396 memcpy(signout->clientid + strlen(signout->clientid), clientid2,
397 strlen(clientid2));
398
399 return SUCCESS_RETURN;
400 }
401
_mqtt_dynreg_topic_handle(void * pcontext,void * pclient,iotx_mqtt_event_msg_pt msg)402 void _mqtt_dynreg_topic_handle(void *pcontext, void *pclient,
403 iotx_mqtt_event_msg_pt msg)
404 {
405 int32_t res = 0;
406 char *ds = (char *)pcontext;
407 iotx_mqtt_topic_info_t *topic_info = (iotx_mqtt_topic_info_pt)msg->msg;
408 const char *asterisk = "**********************";
409
410 switch (msg->event_type) {
411 case IOTX_MQTT_EVENT_PUBLISH_RECEIVED:
412 {
413 /* print topic name and topic message */
414 char *device_secret = NULL;
415 uint32_t device_secret_len = 0;
416
417 /* parse secret */
418 res = infra_json_value((char *)topic_info->payload,
419 topic_info->payload_len, "deviceSecret",
420 strlen("deviceSecret"), &device_secret,
421 &device_secret_len);
422 if (res == SUCCESS_RETURN) {
423 memcpy(ds, device_secret + 1, device_secret_len - 2);
424 memcpy(device_secret + 1 + 5, asterisk, strlen(asterisk));
425 dynreg_info("Topic : %.*s", topic_info->topic_len,
426 topic_info->ptopic);
427 dynreg_info("Payload: %.*s", topic_info->payload_len,
428 topic_info->payload);
429 }
430 }
431 break;
432 default:
433 break;
434 }
435 }
436
_mqtt_dynamic_register(iotx_http_region_types_t region,iotx_dev_meta_info_t * meta)437 int32_t _mqtt_dynamic_register(iotx_http_region_types_t region,
438 iotx_dev_meta_info_t *meta)
439 {
440 void *pClient = NULL;
441 iotx_mqtt_param_t mqtt_params;
442 int32_t res = 0;
443 uint32_t length = 0, rand = 0;
444 char rand_str[9] = { 0 };
445 iotx_sign_mqtt_t signout;
446 uint64_t timestart = 0, timenow = 0;
447 char device_secret[IOTX_DEVICE_SECRET_LEN + 1] = { 0 };
448
449 memset(&signout, 0, sizeof(iotx_sign_mqtt_t));
450
451 /* setup hostname */
452 if (IOTX_HTTP_REGION_CUSTOM == region) {
453 if (g_infra_mqtt_domain[region] == NULL) {
454 return ERROR_DEV_SIGN_CUSTOM_DOMAIN_IS_NULL;
455 }
456
457 length = strlen(g_infra_mqtt_domain[region]) + 1;
458 if (length >= DEV_SIGN_HOSTNAME_MAXLEN) {
459 return ERROR_DEV_SIGN_HOST_NAME_TOO_SHORT;
460 }
461
462 memset(signout.hostname, 0, DEV_SIGN_HOSTNAME_MAXLEN);
463 memcpy(signout.hostname, g_infra_mqtt_domain[region],
464 strlen(g_infra_mqtt_domain[region]));
465 } else {
466 length =
467 strlen(meta->product_key) + strlen(g_infra_mqtt_domain[region]) + 2;
468 if (length >= DEV_SIGN_HOSTNAME_MAXLEN) {
469 return ERROR_DEV_SIGN_HOST_NAME_TOO_SHORT;
470 }
471 memset(signout.hostname, 0, DEV_SIGN_HOSTNAME_MAXLEN);
472 memcpy(signout.hostname, meta->product_key, strlen(meta->product_key));
473 memcpy(signout.hostname + strlen(signout.hostname), ".", strlen("."));
474 memcpy(signout.hostname + strlen(signout.hostname),
475 g_infra_mqtt_domain[region],
476 strlen(g_infra_mqtt_domain[region]));
477 }
478
479 /* setup port */
480 signout.port = 443;
481
482 /* setup username */
483 length = strlen(meta->device_name) + strlen(meta->product_key) + 2;
484 if (length >= DEV_SIGN_USERNAME_MAXLEN) {
485 return ERROR_DEV_SIGN_USERNAME_TOO_SHORT;
486 }
487 memset(signout.username, 0, DEV_SIGN_USERNAME_MAXLEN);
488 memcpy(signout.username, meta->device_name, strlen(meta->device_name));
489 memcpy(signout.username + strlen(signout.username), "&", strlen("&"));
490 memcpy(signout.username + strlen(signout.username), meta->product_key,
491 strlen(meta->product_key));
492
493 /* password */
494 rand = HAL_Random(0xffffffff);
495 infra_hex2str((unsigned char *)&rand, 4, rand_str);
496 res = _mqtt_dynreg_sign_password(meta, &signout, rand_str);
497 if (res < 0) {
498 return res;
499 }
500
501 /* client id */
502 res = _mqtt_dynreg_sign_clientid(meta, &signout, rand_str);
503 if (res < 0) {
504 return res;
505 }
506
507 memset(&mqtt_params, 0, sizeof(iotx_mqtt_param_t));
508 mqtt_params.host = signout.hostname;
509 mqtt_params.port = signout.port;
510 mqtt_params.username = signout.username;
511 mqtt_params.password = signout.password;
512 mqtt_params.client_id = signout.clientid;
513 mqtt_params.request_timeout_ms = 5000;
514 mqtt_params.clean_session = 1;
515 mqtt_params.keepalive_interval_ms = 60000;
516 mqtt_params.read_buf_size = 1000;
517 mqtt_params.write_buf_size = 1000;
518
519 pClient = wrapper_mqtt_init(&mqtt_params);
520 if (pClient == NULL) {
521 return FAIL_RETURN;
522 }
523
524 res = wrapper_mqtt_connect(pClient);
525 if (res < SUCCESS_RETURN) {
526 wrapper_mqtt_release(&pClient);
527 return FAIL_RETURN;
528 }
529
530 res = wrapper_mqtt_subscribe(pClient, "/ext/register", IOTX_MQTT_QOS1,
531 _mqtt_dynreg_topic_handle, device_secret);
532 if (res < 0) {
533 wrapper_mqtt_release(&pClient);
534 return FAIL_RETURN;
535 }
536
537 timestart = HAL_UptimeMs();
538 while (1) {
539 timenow = HAL_UptimeMs();
540 if (timenow < timestart) {
541 timestart = timenow;
542 }
543
544 if (timestart - timenow >= MQTT_DYNREG_TIMEOUT_MS) {
545 break;
546 }
547
548 wrapper_mqtt_yield(pClient, 200);
549
550 if (strlen(device_secret) > 0) {
551 break;
552 }
553 }
554
555 if (strlen(device_secret) > 0) {
556 res = SUCCESS_RETURN;
557 } else {
558 res = FAIL_RETURN;
559 }
560
561 wrapper_mqtt_release(&pClient);
562
563 memcpy(meta->device_secret, device_secret, strlen(device_secret));
564
565 return res;
566 }
567 #endif
568
IOT_Dynamic_Register(iotx_http_region_types_t region,iotx_dev_meta_info_t * meta)569 int32_t IOT_Dynamic_Register(iotx_http_region_types_t region,
570 iotx_dev_meta_info_t *meta)
571 {
572 #ifdef MQTT_DYNAMIC_REGISTER
573 return _mqtt_dynamic_register(region, meta);
574 #else
575 return _http_dynamic_register(region, meta);
576 #endif
577 }
578