Пример #1
0
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);
            }
        });
Пример #2
0
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;
}
Пример #3
0
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;
    }
}
Пример #4
0
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);
            });
Пример #5
0
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;
}
Пример #6
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;
}