void broker_subscribe_remote(DownstreamNode *respNode, SubRequester *subreq, const char *respPath) { DownstreamNode *reqNode = subreq->reqNode; ref_t *ref = dslink_map_get(&respNode->resp_sub_streams, (void*)respPath); BrokerSubStream *bss; if (ref) { bss = ref->data; } else { bss = broker_stream_sub_init(); bss->respSid = broker_node_incr_sid(respNode); bss->remote_path = dslink_strdup(respPath); bss->respNode = (BrokerNode*)respNode; // a invalid qos value, so the newQos != qos, // which will send a new subscribe method to responder bss->respQos = 0xFF; dslink_map_set(&respNode->resp_sub_streams, dslink_str_ref(bss->remote_path), dslink_ref(bss, NULL)); dslink_map_set(&respNode->resp_sub_sids, dslink_int_ref(bss->respSid), dslink_ref(bss, NULL)); } subreq->stream = bss; dslink_map_set(&bss->reqSubs, dslink_ref(reqNode, NULL), dslink_ref(subreq, NULL)); broker_update_stream_qos(bss); if (bss->last_value) { broker_update_sub_req(subreq, bss->last_value); } }
static uint8_t set_virtual_permission(const char* path, VirtualDownstreamNode* node, json_t *json) { if (!path || *path == 0) { List *permissions = permission_list_load(json); permission_list_free(node->permissionList); node->permissionList = permissions; return 0; } else { const char* next = strstr(path, "/"); char* name; if (next) { name = dslink_calloc(next - path + 1, 1); memcpy(name, path, next-path); next ++; // remove '/' } else { name = (char*)path; } ref_t *ref = dslink_map_get(&node->childrenNode, name); VirtualDownstreamNode *child; if (ref && ref->data) { child = ref->data; } else { child = dslink_calloc(1, sizeof(VirtualDownstreamNode)); virtual_downstream_node_init(child); dslink_map_set(&node->childrenNode, dslink_str_ref(name), dslink_ref(child, NULL)); } return set_virtual_permission(next, child, json); } }
void broker_handle_local_subscribe(BrokerNode *respNode, SubRequester *subreq) { DownstreamNode *reqNode = subreq->reqNode; if (!respNode->sub_stream) { respNode->sub_stream = broker_stream_sub_init(); respNode->sub_stream->respNode = respNode; if (respNode->value) { broker_update_sub_stream_value(respNode->sub_stream, respNode->value, NULL); } else { broker_update_sub_stream_value(respNode->sub_stream, json_null(), NULL); } } subreq->stream = respNode->sub_stream; dslink_map_set(&respNode->sub_stream->reqSubs, dslink_ref(reqNode, NULL), dslink_ref(subreq, NULL)); if (respNode->sub_stream->last_value) { broker_update_sub_req(subreq, respNode->sub_stream->last_value); } }
void broker_add_new_subscription(Broker *broker, SubRequester *subreq) { char *out = NULL; DownstreamNode * reqNode = subreq->reqNode; BrokerNode *respNode = broker_node_get(broker->root, subreq->path, &out); dslink_map_set(&reqNode->req_sub_paths, dslink_str_ref(subreq->path), dslink_ref(subreq, NULL)); dslink_map_set(&reqNode->req_sub_sids, dslink_int_ref(subreq->reqSid), dslink_ref(subreq, NULL)); if (!respNode) { if (dslink_str_starts_with(subreq->path, "/downstream/") || dslink_str_starts_with(subreq->path, "/upstream/")) { broker_subscribe_disconnected_remote(subreq->path, subreq); } else { broker_subscribe_local_nonexistent(subreq->path, subreq); } return; } if (respNode->type == REGULAR_NODE) { broker_handle_local_subscribe( respNode, subreq); } else { DownstreamNode *downNode = (DownstreamNode *)respNode; broker_subscribe_remote(downNode, subreq, out); } }
void broker_subscribe_local_nonexistent(const char *path, SubRequester *subreq) { Broker *broker = mainLoop->data; ref_t *ref = dslink_map_get(&broker->local_pending_sub, (char *) path); List *subs; if (ref) { subs = ref->data; } else { subs = dslink_calloc(1, sizeof(List)); list_init(subs); dslink_map_set(&broker->local_pending_sub, dslink_str_ref(path), dslink_ref(subs, subs_list_free)); } subreq->pendingNode = dslink_list_insert(subs, subreq); }
void broker_subscribe_disconnected_remote(const char *path, SubRequester *subreq) { Broker *broker = mainLoop->data; const size_t len = broker_downstream_node_base_len(path); if (len ==0) { // todo remove subreq? return; } ref_t *ref = dslink_map_getl(&broker->remote_pending_sub, (char *) path, len); List *subs; if (ref) { subs = ref->data; } else { subs = dslink_calloc(1, sizeof(List)); list_init(subs); dslink_map_set(&broker->remote_pending_sub, dslink_strl_ref(path, len), dslink_ref(subs, subs_list_free)); } subreq->pendingNode = dslink_list_insert(subs, subreq); }
static void handle_subscribe(RemoteDSLink *link, json_t *sub) { const char *path = json_string_value(json_object_get(sub, "path")); json_t *jSid = json_object_get(sub, "sid"); if (!(path && jSid)) { return; } PermissionLevel permissionOnPath = get_permission(path, link->broker->root, link); if (permissionOnPath < PERMISSION_READ) { return; } DownstreamNode *reqNode = link->node; uint32_t sid = (uint32_t) json_integer_value(jSid); json_t *jQos = json_object_get(sub, "qos"); uint8_t qos = 0; if (json_is_integer(jQos)) { qos = (uint8_t) json_integer_value(jQos); } // TODO check if sid or path already exist ref_t *idsub = dslink_map_get(&reqNode->req_sub_sids, &sid); ref_t *pathsub = dslink_map_get(&reqNode->req_sub_paths, (void*)path); if (idsub && pathsub && idsub->data == pathsub->data) { // update qos only SubRequester *reqsub = idsub->data; broker_update_sub_qos(reqsub, qos); return; } if (idsub) { // remove current sub; SubRequester *reqsub = idsub->data; broker_free_sub_requester(reqsub); } if (pathsub) { // update sid and qos on existing path; SubRequester *reqsub = pathsub->data; ref_t *pathidsub = dslink_map_remove_get(&reqNode->req_sub_sids, &reqsub->reqSid); if (pathidsub) { dslink_free(pathidsub); } reqsub->reqSid = sid; dslink_map_set(&reqNode->req_sub_sids, dslink_int_ref(sid), dslink_ref(reqsub, NULL)); broker_update_sub_qos(reqsub, qos); if (json_array_size(reqsub->qosQueue) > 0) { // send qos data broker_update_sub_req_qos(reqsub); } else if (reqsub->stream && reqsub->stream->last_value) { broker_update_sub_req(reqsub, reqsub->stream->last_value); } return; } SubRequester *subreq = broker_create_sub_requester(reqNode, path, sid, qos, NULL); if (qos & 2) { serialize_qos_queue(subreq, 0); } broker_add_new_subscription(link->broker, subreq); }
int broker_msg_handle_invoke(RemoteDSLink *link, json_t *req) { json_t *reqRid = json_object_get(req, "rid"); json_t *reqPath = json_object_get(req, "path"); if (!(reqRid && reqPath)) { return 1; } json_t *maxPermitJson = json_object_get(req, "permit"); PermissionLevel maxPermit = PERMISSION_CONFIG; if (json_is_string(maxPermitJson)) { maxPermit = permission_str_level(json_string_value(maxPermitJson)); } const char *path = json_string_value(reqPath); char *out = NULL; BrokerNode *node = broker_node_get(link->broker->root, path, &out); if (!node) { broker_utils_send_closed_resp(link, req, "disconnected"); return 0; } Broker *broker = mainLoop->data; PermissionLevel permissionOnPath = get_permission(path, broker->root, link); if (permissionOnPath > maxPermit) { permissionOnPath = maxPermit; } if (permissionOnPath == PERMISSION_NONE) { broker_utils_send_closed_resp(link, req, "permissionDenied"); return 0; } if (node->type == REGULAR_NODE) { json_t *invokableJson = json_object_get(node->meta, "$invokable"); PermissionLevel level = permission_str_level(json_string_value(invokableJson)); if (level > permissionOnPath) { broker_utils_send_closed_resp(link, req, "permissionDenied"); } else if (node->on_invoke) { node->on_invoke(link, node, req, maxPermit); } return 0; } else if (node->type != DOWNSTREAM_NODE) { // Unknown node type broker_utils_send_closed_resp(link, req, "disconnected"); return 0; } DownstreamNode *ds = (DownstreamNode *) node; uint32_t rid = broker_node_incr_rid(ds); if (!ds->link) { broker_utils_send_closed_resp(link, req, "disconnected"); return 0; } BrokerInvokeStream *s = broker_stream_invoke_init(); s->responder_rid = rid; s->responder = ds->link; s->resp_close_cb = remote_invoke_resp_disconnected; s->requester_rid = (uint32_t) json_integer_value(reqRid); s->requester = link; s->req_close_cb = remote_invoke_req_closed; ref_t *refStream = dslink_ref(s, NULL); dslink_map_set(&ds->link->responder_streams, dslink_int_ref(rid), refStream); ref_t *findref = dslink_map_remove_get(&link->requester_streams, &s->requester_rid); if (findref) { BrokerStream *oldstream = findref->data; if (oldstream->req_close_cb) { oldstream->req_close_cb(oldstream, link); } broker_stream_free(oldstream); dslink_decref(findref); } dslink_map_set(&link->requester_streams, dslink_int_ref(s->requester_rid), dslink_incref(refStream)); send_invoke_request(ds, req, rid, out, permissionOnPath); return 0; }
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; }
json_t *broker_handshake_handle_conn(Broker *broker, const char *dsId, const char *token, json_t *handshake) { if (dslink_map_contains(&broker->client_connecting, (void *) dsId)) { ref_t *ref = dslink_map_remove_get(&broker->client_connecting, (void *) dsId); RemoteDSLink *link = ref->data; dslink_map_remove(&broker->client_connecting, (void *) link->name); broker_remote_dslink_free(link); dslink_free(link); dslink_decref(ref); } RemoteDSLink *link = dslink_calloc(1, sizeof(RemoteDSLink)); json_t *resp = json_object(); if (!(link && resp)) { goto fail; } if (broker_remote_dslink_init(link) != 0) { goto fail; } link->broker = broker; link->auth = dslink_calloc(1, sizeof(RemoteAuth)); if (!link->auth) { goto fail; } if (dslink_handshake_generate_key_pair(&link->auth->tempKey) != 0) { log_err("Failed to create temporary key for DSLink\n"); goto fail; } { json_t *jsonPubKey = json_object_get(handshake, "publicKey"); if (!jsonPubKey) { goto fail; } const char *tmp = json_string_value(jsonPubKey); if (!tmp) { goto fail; } tmp = dslink_strdup(tmp); if (!tmp) { goto fail; } link->auth->pubKey = tmp; } char tempKey[90]; size_t tempKeyLen = 0; if (dslink_handshake_encode_pub_key(&link->auth->tempKey, tempKey, sizeof(tempKey), &tempKeyLen) != 0) { goto fail; } if (generate_salt((unsigned char *) link->auth->salt, sizeof(link->auth->salt)) != 0) { goto fail; } json_object_set_new_nocheck(resp, "wsUri", json_string_nocheck("/ws")); json_object_set_new_nocheck(resp, "tempKey", json_string_nocheck(tempKey)); json_object_set_new_nocheck(resp, "salt", json_string_nocheck(link->auth->salt)); if (json_boolean_value(json_object_get(handshake, "isResponder"))) { link->isResponder = 1; } if (json_boolean_value(json_object_get(handshake, "isRequester"))) { link->isRequester = 1; } json_t *linkData = json_object_get(handshake, "linkData"); if (json_is_object(linkData)) { json_incref(linkData); link->linkData = linkData; } { char buf[512] = {0}; snprintf(buf, sizeof(buf), "/downstream/"); char *name = buf + sizeof("/downstream/")-1; size_t dsIdLen = strlen(dsId); if (dsIdLen < 44) { goto fail; } size_t nameLen = dsIdLen - 43; if (dsId[nameLen - 1] == '-') { nameLen--; } int nodeExists = 0; // find a valid name from broker->client_names memcpy(name, dsId, nameLen); while (1) { ref_t *ref = dslink_map_get(&broker->client_connecting, name); if (ref) { RemoteDSLink *l = ref->data; if (l && l->dsId && strcmp(l->dsId->data, dsId) == 0) { dslink_map_remove(&broker->client_connecting, name); broker_remote_dslink_free(l); break; } else { name[nameLen] = dsId[nameLen]; nameLen++; } } ref = dslink_map_get(broker->downstream->children, (void *) name); if (ref == NULL) { break; } if (!((DownstreamNode *) ref->data)->dsId || strcmp(dsId, ((DownstreamNode *) ref->data)->dsId->data) == 0) { nodeExists = 1; break; } name[nameLen] = dsId[nameLen]; nameLen++; } if (!nodeExists && broker_enable_token) { if (!token) { log_err("Failed to connet, need token\n"); goto fail; } BrokerNode* tokenNode = get_token_node(token, dsId); if (tokenNode) { DownstreamNode *node = broker_init_downstream_node(broker->downstream, name); if (json_is_true(json_object_get(node->meta, "$$managed"))) { json_object_set_new_nocheck(node->meta, "$$token", json_string_nocheck(tokenNode->name)); } node->dsId = dslink_str_ref(dsId); if (broker->downstream->list_stream) { update_list_child(broker->downstream, broker->downstream->list_stream, link->name); } json_t *group = json_object_get(tokenNode->meta, "$$group"); if (json_is_string(group)) { json_object_set_nocheck(node->meta, "$$group", group); } token_used(tokenNode); broker_downstream_nodes_changed(broker); } else { log_err("Invalid token: %s\n", token); goto fail; } } json_object_set_new_nocheck(resp, "path", json_string_nocheck(buf)); link->path = dslink_strdup(buf); if (!link->path) { goto fail; } link->name = link->path + sizeof("/downstream/") - 1; // add to connecting map with the name if (dslink_map_set(&broker->client_connecting, dslink_ref((void *) link->name, NULL), dslink_ref(link, NULL)) != 0) { dslink_free((void *) link->path); goto fail; } } { ref_t *tmp = dslink_ref(dslink_strdup(dsId), dslink_free); if (!tmp) { goto fail; } // add to connecting map with dsId if (dslink_map_set(&broker->client_connecting, tmp, dslink_ref(link, NULL)) != 0) { dslink_free(tmp); goto fail; } } return resp; fail: if (link) { broker_remote_dslink_free(link); dslink_free((void *) link->path); dslink_free(link); } DSLINK_CHECKED_EXEC(json_decref, resp); return NULL; }
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; }