List *permission_list_load(json_t *json) { if (!json_is_array(json) || json_array_size(json) == 0) { return NULL; } List *rslt = dslink_calloc(1, sizeof(List)); list_init(rslt); size_t idx; json_t *value; json_array_foreach(json, idx, value) { if (json_array_size(value) == 2) { json_t *v0 = json_array_get(value, 0); json_t *v1 = json_array_get(value, 1); if (json_is_string(v0) && json_is_string(v1)) { const char* vc0 = json_string_value(v0); const char* vc1 = json_string_value(v1); PermissionLevel p = permission_str_level(vc1); if (p <= PERMISSION_CONFIG) { PermissionPair * pair = dslink_malloc(sizeof(PermissionPair)); pair->group = dslink_strdup(vc0); pair->permission = p; dslink_list_insert(rslt, pair); } } } } return rslt; }
static void broker_server_new_client(uv_poll_t *poll, int status, int events) { (void) status; (void) events; Server *server = poll->data; Client *client = dslink_calloc(1, sizeof(Client)); if (!client) { goto fail; } client->server = server; client->sock = dslink_socket_init(0); if (!client->sock) { dslink_free(client); goto fail; } if (mbedtls_net_accept(&server->srv, &client->sock->socket_ctx, NULL, 0, NULL) != 0) { log_warn("Failed to accept a client connection\n"); goto fail_poll_setup; } uv_poll_t *clientPoll = dslink_malloc(sizeof(uv_poll_t)); if (!clientPoll) { goto fail_poll_setup; } uv_loop_t *loop = poll->loop; if (uv_poll_init(loop, clientPoll, client->sock->socket_ctx.fd) != 0) { dslink_free(clientPoll); goto fail_poll_setup; } clientPoll->data = client; client->poll = clientPoll; uv_poll_start(clientPoll, UV_READABLE, broker_server_client_ready); log_debug("Accepted a client connection\n"); return; fail: { mbedtls_net_context tmp; mbedtls_net_init(&tmp); mbedtls_net_accept(&server->srv, &tmp, NULL, 0, NULL); mbedtls_net_free(&tmp); } return; fail_poll_setup: dslink_socket_free(client->sock); dslink_free(client); }
char *dslink_strdupl(const char *str, size_t len) { if (!str) { return NULL; } char *tmp = dslink_malloc(len + 1); if (!tmp) { return NULL; } memcpy(tmp, str, len); tmp[len] = '\0'; return tmp; }
char *dslink_strdup(const char *str) { if (!str) { return NULL; } size_t strSize = strlen(str) + 1; char *tmp = dslink_malloc(strSize); if (!tmp) { return NULL; } memcpy(tmp, str, strSize); return tmp; }
void permission_groups_load(PermissionGroups* groups, const char *dsId, const char* str) { if (groups->groups) { permission_groups_free(groups); } size_t allocatedLen = 1; size_t len = 0; if (str) { allocatedLen = 4; groups->groups = dslink_malloc(sizeof(char*) * allocatedLen); const char *start = str; const char *end = str - 1; do { ++end; if (*end == ',' || *end == '\0') { if (end > start) { // +1 for current value, +1 for the dsId if (len + 2 > allocatedLen) { allocatedLen *= 2; groups->groups = dslink_realloc(groups->groups, sizeof(char*) * allocatedLen); } groups->groups[len] = dslink_strdupl(start, end - start); ++len; } start = end + 1; } }while (*end); } else { groups->groups = dslink_malloc(sizeof(char*)); } // dsId as a permission group groups->groups[len] = dslink_strdup(dsId); ++len; groups->groupLen = len; }
char *dslink_str_escape(const char *data) { if (!data) { return NULL; } size_t lenoff = 1; const char *search = data; while (*search) { if (*search <= ',' || *search == '/' || *search == ':' || *search == '=') { lenoff += 2; } ++search; } char *rslt = dslink_malloc((search-data) + lenoff); char *pt = rslt; while (*data) { if (*data <= ',' || *data == '/' || *data == ':' || *data == '=') { *pt = '%'; *(pt + 1) = encodeBase16((*data)>>4); *(pt + 2) = encodeBase16((*data)&0xF); } else {
static char *dslink_str_replace_all_rep(const char *haystack, const char *needle, const size_t needleLen, const char *replacement, const size_t replacementLen, const int shouldDup) { char *start = strstr(haystack, needle); if (!start) { if (shouldDup) { return dslink_strdup(haystack); } else { return (char *) haystack; } } const size_t haystackLen = strlen(haystack); char *dup = dslink_malloc(haystackLen - needleLen + replacementLen + 1); if (!dup) { return NULL; } size_t len = start - haystack; memcpy(dup, haystack, len); memcpy(dup + len, replacement, replacementLen); len += replacementLen; size_t remainder = (haystack + haystackLen) - (start + needleLen); memcpy(dup + len, start + needleLen, remainder); dup[haystackLen - needleLen + replacementLen] = '\0'; if (!shouldDup) { dslink_free((char *) haystack); } return dslink_str_replace_all_rep(dup, needle, needleLen, replacement, replacementLen, 0); }
static int dslink_map_get_raw_node(Map *map, MapNode **node, ref_t *key) { int ret = 0; size_t len = map->key_len_calc(key->data); size_t index = dslink_map_index_of_key(map, key->data, len); *node = map->table[index]; if (!(*node)) { *node = map->table[index] = dslink_malloc(sizeof(MapNode)); if (*node) { (*node)->entry = dslink_malloc(sizeof(MapEntry)); if (!(*node)->entry) { map->table[index] = NULL; dslink_free(*node); *node = NULL; goto exit; } (*node)->entry->node = *node; (*node)->entry->key = key; (*node)->entry->value = NULL; (*node)->next = NULL; (*node)->prev = NULL; } } else { while (1) { if (map->cmp((*node)->entry->key->data, key->data, len) == 0) { dslink_decref((*node)->entry->key); (*node)->entry->key = key; return 1; } MapNode *tmp = (*node)->next; if (tmp == NULL) { tmp = dslink_malloc(sizeof(MapNode)); if (!tmp) { *node = NULL; break; } tmp->entry = dslink_malloc(sizeof(MapEntry)); if (!tmp->entry) { dslink_free(*node); *node = NULL; break; } tmp->entry->key = key; tmp->entry->value = NULL; tmp->entry->node = tmp; tmp->next = NULL; tmp->prev = *node; (*node)->next = tmp; *node = tmp; break; } *node = tmp; } } exit: if (!(*node)) { return DSLINK_ALLOC_ERR; } map->size++; list_insert_node(&map->list, (*node)->entry); return ret; }
int dslink_request_handle(DSLink *link, json_t *req) { const char *method = json_string_value(json_object_get(req, "method")); if (!method) { return 1; } if (strcmp(method, "list") == 0) { const char *path = json_string_value(json_object_get(req, "path")); DSNode *node = dslink_node_get_path(link->responder->super_root, path); return dslink_response_list(link, req, node); } else if (strcmp(method, "subscribe") == 0) { json_t *paths = json_object_get(req, "paths"); json_t *rid = json_object_get(req, "rid"); return dslink_response_sub(link, paths, rid); } else if (strcmp(method, "unsubscribe") == 0) { json_t *sids = json_object_get(req, "sids"); json_t *rid = json_object_get(req, "rid"); return dslink_response_unsub(link, sids, rid); } else if (strcmp(method, "invoke") == 0) { const char *path = json_string_value(json_object_get(req, "path")); DSNode *node = dslink_node_get_path(link->responder->super_root, path); if (node && node->on_invocation) { Stream *stream = dslink_malloc(sizeof(Stream)); if (!stream) { return 1; } stream->type = INVOCATION_STREAM; stream->path = dslink_strdup(node->path); ref_t *stream_ref = dslink_ref(stream, free_stream); json_t *jsonRid = json_object_get(req, "rid"); json_t *params = json_object_get(req, "params"); node->on_invocation(link, node, jsonRid, params, stream_ref); if (stream->unused != 1) { dslink_decref(stream_ref); } else { ref_t *rid = dslink_ref(dslink_malloc(sizeof(uint32_t)), dslink_free); { uint32_t r = (uint32_t) json_integer_value(jsonRid); *((uint32_t *) rid->data) = r; } if (dslink_map_set(link->responder->open_streams, rid, stream_ref) != 0) { dslink_free(rid); dslink_free(stream_ref); free_stream(stream); return 1; } } } } else if (strcmp(method, "set") == 0) { const char *path = json_string_value(json_object_get(req, "path")); json_t *value = json_object_get(req, "value"); DSNode *node = dslink_node_get_path(link->responder->super_root, path); if (node) { ref_t *writable_ref = dslink_map_get(node->meta_data, "$writable"); if (writable_ref && json_is_string((json_t*) writable_ref->data)) { if (node->on_value_set) { node->on_value_set(link, node, value); } else { dslink_node_update_value(link, node, value); } } } } else if (strcmp(method, "close") == 0) { json_t *rid = json_object_get(req, "rid"); uint32_t ridi = (uint32_t) json_integer_value(rid); ref_t *stream_ref = dslink_map_remove_get(link->responder->open_streams, &ridi); if (stream_ref) { Stream *stream = stream_ref->data; DSNode *node = NULL; if (stream->path) { node = dslink_node_get_path(link->responder->super_root, stream->path); } if (stream->on_close != NULL) { stream->on_close(link, node, stream); } if (stream->type == LIST_STREAM) { dslink_map_remove(link->responder->list_subs, (void *) stream->path); } dslink_decref(stream_ref); } } else { log_warn("Unrecognized method: %s\n", method); } return 0; }
int broker_handshake_handle_ws(Broker *broker, Client *client, const char *dsId, const char *auth, const char *wsAccept) { ref_t *oldDsId = NULL; ref_t *ref = dslink_map_remove_get(&broker->client_connecting, (char *) dsId); if (!ref) { return 1; } RemoteDSLink *link = ref->data; dslink_decref(ref); if (link->name) { dslink_map_remove(&broker->client_connecting, (char *) link->name); } if (!(auth && link->auth->pubKey)) { return 1; } uv_timer_t *ping_timer = NULL; int ret = 0; { // Perform auth check char expectedAuth[90]; if (dslink_handshake_gen_auth_key(&link->auth->tempKey, link->auth->pubKey, link->auth->salt, (unsigned char *) expectedAuth, sizeof(expectedAuth)) != 0) { ret = 1; goto exit; } if (strcmp(expectedAuth, auth) != 0) { ret = 1; goto exit; } } DownstreamNode *node = NULL; int pendingUpdateList = 0; { // Handle retrieval of the downstream node ref = dslink_map_get(broker->downstream->children, (char *) link->name); if (!ref) { node = broker_init_downstream_node(broker->downstream, link->name); if (!node) { ret = 1; goto exit; } oldDsId = dslink_ref(dslink_strdup(dsId), dslink_free); if (broker->downstream->list_stream) { pendingUpdateList = 1; } broker_downstream_nodes_changed(broker); } else { node = ref->data; oldDsId = node->dsId; } } if (node->link) { Client *c = node->link->client; broker_close_link(node->link); uv_poll_t *poll = c->poll; dslink_socket_free(c->sock); dslink_free(c); uv_close((uv_handle_t *) poll, broker_free_handle); } // add permission group to link json_t *group = json_object_get(node->meta, "$$group"); permission_groups_load(&link->permission_groups, dsId, json_string_value(group)); link->client = client; link->dsId = oldDsId; link->node = node; node->dsId = oldDsId; client->sock_data = link; json_object_set_new(node->meta, "$$dsId", json_string_nocheck(dsId)); wslay_event_context_ptr ws; if (wslay_event_context_server_init(&ws, broker_ws_callbacks(), link) != 0) { ret = 1; goto exit; } link->ws = ws; broker_ws_send_init(client->sock, wsAccept); ping_timer = dslink_malloc(sizeof(uv_timer_t)); ping_timer->data = link; uv_timer_init(link->client->poll->loop, ping_timer); uv_timer_start(ping_timer, dslink_handle_ping, 1000, 30000); link->pingTimerHandle = ping_timer; // set the ->link and update all existing stream broker_dslink_connect(node, link); if (pendingUpdateList) { update_list_child(broker->downstream, broker->downstream->list_stream, link->name); } log_info("DSLink `%s` has connected\n", dsId); exit: mbedtls_ecdh_free(&link->auth->tempKey); dslink_free((void *) link->auth->pubKey); dslink_free(link->auth); link->auth = NULL; if (ret != 0) { dslink_map_free(&link->requester_streams); dslink_map_free(&link->responder_streams); dslink_free((char *)link->path); dslink_free(link); if (ping_timer) { uv_timer_stop(ping_timer); uv_close((uv_handle_t *) ping_timer, broker_free_handle); } } return ret; }