void dslink_node_tree_free_basic(DSNode *root) { DSLINK_CHECKED_EXEC(free, (void *) root->path); DSLINK_CHECKED_EXEC(free, (void *) root->name); DSLINK_CHECKED_EXEC(free, (void *) root->profile); DSLINK_CHECKED_EXEC(json_delete, root->value_timestamp); DSLINK_CHECKED_EXEC(json_delete, root->value); if (root->children) { DSLINK_MAP_FREE(root->children, { if (entry->value) { dslink_node_tree_free_basic(entry->value); } });
DSNode *dslink_node_create(DSNode *parent, const char *name, const char *profile) { name = dslink_strdup(name); if (!name) { return NULL; } profile = dslink_strdup(profile); if (!profile) { free((void *) name); return NULL; } DSNode *node = calloc(1, sizeof(DSNode)); if (!node) { goto cleanup; } node->parent = parent; node->name = name; node->profile = profile; if (parent) { size_t pathLen = strlen(parent->path); size_t nameLen = strlen(name); char *path = malloc(pathLen + nameLen + 2); node->path = path; if (!path) { goto cleanup; } memcpy(path, parent->path, pathLen); *(path + pathLen) = '/'; memcpy(path + pathLen + 1, name, nameLen + 1); } else { node->path = calloc(1, sizeof(char)); if (!node->path) { goto cleanup; } } return node; cleanup: DSLINK_CHECKED_EXEC(free, (void *) name); DSLINK_CHECKED_EXEC(free, (void *) profile); if (node) { DSLINK_CHECKED_EXEC(free, (void *) node->path); free(node); } return NULL; }
static Socket *dslink_socket_init(uint_fast8_t secure) { if (secure) { SslSocket *s = malloc(sizeof(SslSocket)); if (!s) { return NULL; } s->secure = 1; s->socket_fd = malloc(sizeof(mbedtls_net_context)); s->entropy = malloc(sizeof(mbedtls_entropy_context)); s->drbg = malloc(sizeof(mbedtls_ctr_drbg_context)); s->ssl = malloc(sizeof(mbedtls_ssl_context)); s->conf = malloc(sizeof(mbedtls_ssl_config)); if (!(s->socket_fd && s->entropy && s->drbg && s->ssl && s->conf)) { DSLINK_CHECKED_EXEC(free, s->socket_fd); DSLINK_CHECKED_EXEC(free, s->entropy); DSLINK_CHECKED_EXEC(free, s->drbg); DSLINK_CHECKED_EXEC(free, s->ssl); DSLINK_CHECKED_EXEC(free, s->conf); free(s); return NULL; } mbedtls_net_init(s->socket_fd); mbedtls_entropy_init(s->entropy); mbedtls_ctr_drbg_init(s->drbg); mbedtls_ssl_init(s->ssl); mbedtls_ssl_config_init(s->conf); return (Socket *) s; } else { Socket *s = malloc(sizeof(Socket)); if (!s) { return NULL; } s->secure = 0; s->socket_fd = malloc(sizeof(mbedtls_net_context)); if (!s->socket_fd) { free(s); return NULL; } return s; } }
int dslink_init(int argc, char **argv, const char *name, uint8_t isRequester, uint8_t isResponder, DSLinkCallbacks *cbs) { mbedtls_ecdh_context ctx; DSLinkConfig config; Url *url = NULL; json_t *handshake = NULL; char *dsId = NULL; Socket *sock = NULL; DSLink link; memset(&link, 0, sizeof(DSLink)); int ret = 0; config.name = name; if ((ret = dslink_parse_opts(argc, argv, &config)) != 0) { if (ret == DSLINK_ALLOC_ERR) { log_fatal("Failed to allocate memory during argument parsing\n"); } return 1; } // TODO: move .key as a parameter if ((ret = dslink_handshake_key_pair_fs(&ctx, ".key")) != 0) { if (ret == DSLINK_CRYPT_KEY_DECODE_ERR) { log_fatal("Failed to decode existing key\n"); } else if (ret == DSLINK_OPEN_FILE_ERR) { log_fatal("Failed to write generated key to disk\n"); } else if (ret == DSLINK_CRYPT_KEY_PAIR_GEN_ERR) { log_fatal("Failed to generated key\n"); } else { log_fatal("Unknown error occurred during key handling: %d\n", ret); } ret = 1; goto exit; } url = dslink_url_parse(config.broker_url); if (!url) { log_fatal("Failed to parse url: %s\n", config.broker_url); ret = 1; goto exit; } if (isResponder) { link.responder = calloc(1, sizeof(Responder)); if (!link.responder) { log_fatal("Failed to create responder\n"); goto exit; } if (dslink_init_responder(link.responder) != 0) { log_fatal("Failed to initialize responder\n"); goto exit; } } if (cbs->init_cb) { cbs->init_cb(&link); } if ((ret = dslink_handshake_generate(url, &ctx, config.name, isRequester, isResponder, &handshake, &dsId)) != 0) { log_fatal("Handshake failed: %d\n", ret); ret = 1; goto exit; } const char *uri = json_string_value(json_object_get(handshake, "wsUri")); const char *tKey = json_string_value(json_object_get(handshake, "tempKey")); const char *salt = json_string_value(json_object_get(handshake, "salt")); if (!(uri && tKey && salt)) { log_fatal("Handshake didn't return the " "necessary parameters to complete\n"); ret = 1; goto exit; } if ((ret = dslink_handshake_connect_ws(url, &ctx, uri, tKey, salt, dsId, &sock)) != 0) { log_fatal("Failed to connect to the broker: %d\n", ret); ret = 1; goto exit; } else { log_info("Successfully connected to the broker\n"); } if (cbs->on_connected_cb) { cbs->on_connected_cb(&link); } link._socket = sock; dslink_handshake_handle_ws(&link); // TODO: automatic reconnecting log_warn("Disconnected from the broker\n") if (cbs->on_disconnected_cb) { cbs->on_disconnected_cb(&link); } exit: mbedtls_ecdh_free(&ctx); DSLINK_CHECKED_EXEC(dslink_socket_close, sock); if (link.responder) { if (link.responder->super_root) { dslink_node_tree_free(NULL, link.responder->super_root); } if (link.responder->open_streams) { DSLINK_MAP_FREE(link.responder->open_streams, { free(entry->key); free(entry->value); });
int dslink_handshake_generate(Url *url, mbedtls_ecdh_context *key, const char *name, uint8_t isRequester, uint8_t isResponder, json_t **handshake, char **dsId) { *handshake = NULL; Socket *sock = NULL; char *resp = NULL; char *body = NULL; int ret = 0; unsigned char pubKeyBin[65]; size_t pubKeyBinLen = 0; unsigned char pubKey[90]; size_t pubKeyLen = 0; if ((errno = mbedtls_ecp_point_write_binary(&key->grp, &key->Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &pubKeyBinLen, pubKeyBin, sizeof(pubKeyBin))) != 0) { ret = DSLINK_CRYPT_KEY_ENCODE_ERR; goto exit; } if ((errno = dslink_base64_url_encode(pubKey, sizeof(pubKey), &pubKeyLen, pubKeyBin, pubKeyBinLen)) != 0) { ret = DSLINK_CRYPT_BASE64_URL_ENCODE_ERR; goto exit; } { // Generate dsId unsigned char sha[32]; mbedtls_sha256(pubKeyBin, pubKeyBinLen, sha, 0); unsigned char tmp[45]; size_t tmpLen = 0; if ((errno = dslink_base64_url_encode((unsigned char *) tmp, sizeof(tmp), &tmpLen, sha, sizeof(sha))) != 0) { ret = DSLINK_CRYPT_BASE64_URL_ENCODE_ERR; goto exit; } size_t nameLen = strlen(name); *dsId = malloc(nameLen + tmpLen + 2); if (!(*dsId)) { ret = DSLINK_ALLOC_ERR; goto exit; } memcpy(*dsId, name, nameLen); *(*dsId + nameLen) = '-'; memcpy((*dsId + nameLen + 1), (char *) tmp, tmpLen); *(*dsId + nameLen + tmpLen + 1) = '\0'; } { // Create the request body json_t *obj = json_object(); if (!obj) { ret = DSLINK_ALLOC_ERR; goto exit; } json_object_set_new(obj, "publicKey", json_string((char *) pubKey)); json_object_set_new(obj, "isRequester", json_boolean(isRequester)); json_object_set_new(obj, "isResponder", json_boolean(isResponder)); json_object_set_new(obj, "version", json_string("1.1.2")); body = json_dumps(obj, JSON_INDENT(2)); json_delete(obj); if (!body) { ret = DSLINK_ALLOC_ERR; goto exit; } } char req[512]; size_t reqLen; { char uri[128]; snprintf(uri, sizeof(uri), "%s?dsId=%s", url->uri, *dsId); reqLen = snprintf(req, sizeof(req), DSLINK_POST_REQ, uri, url->host, url->port, (int) strlen(body), body); } if ((ret = dslink_socket_connect(&sock, url->host, url->port, url->secure)) != 0) { goto exit; } dslink_socket_write(sock, req, reqLen); int respLen = 0; while (1) { char buf[1024]; int read = dslink_socket_read(sock, buf, sizeof(buf) - 1); if (read <= 0) { break; } if (resp == NULL) { resp = malloc((size_t) read + 1); if (!resp) { ret = DSLINK_ALLOC_ERR; goto exit; } respLen = read; memcpy(resp, buf, read); *(resp + respLen) = '\0'; } else { char *tmp = realloc(resp, (size_t) respLen + read + 1); if (!tmp) { free(resp); ret = DSLINK_ALLOC_ERR; goto exit; } resp = tmp; memcpy(resp + respLen, buf, read); respLen += read; *(resp + respLen) = '\0'; } } if (!resp) { ret = DSLINK_HANDSHAKE_NO_RESPONSE; goto exit; } char *index = strstr(resp, "401 Unauthorized"); if (index) { ret = DSLINK_HANDSHAKE_UNAUTHORIZED; goto exit; } index = strchr(resp, '{'); if (!index) { ret = DSLINK_HANDSHAKE_INVALID_RESPONSE; goto exit; } char *json = index; index = strrchr(json, '}'); if (!index) { ret = DSLINK_HANDSHAKE_INVALID_RESPONSE; goto exit; } *(index + 1) = '\0'; json_error_t jsonErr; *handshake = json_loads(json, 0, &jsonErr); if (!(*handshake)) { ret = DSLINK_ALLOC_ERR; goto exit; } const char *id = json_string_value(json_object_get(*handshake, "id")); if (id) { free(*dsId); size_t size = strlen(id) + 1; *dsId = malloc(size); if (!(*dsId)) { ret = DSLINK_ALLOC_ERR; json_delete(*handshake); *handshake = NULL; goto exit; } memcpy(*dsId, id, size); } exit: DSLINK_CHECKED_EXEC(free, body); DSLINK_CHECKED_EXEC(free, resp); DSLINK_CHECKED_EXEC(dslink_socket_close, sock); return ret; }
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; }