Skip to content
Snippets Groups Projects
Select Git revision
  • windows_ci
  • master default
  • cert_pk_id
  • proxy_push_result
  • cnode_put_id
  • update-windows-build
  • proxy
  • resubscribe_on_token_change
  • actions
  • client_mode
  • llhttp
  • search_node_add
  • crypto_aes_gcm_argon2
  • ios_notifications
  • log_fmt
  • v2asio
  • fix-msvc
  • message_split
  • meson
  • build_unify
  • v3.4.0
  • v3.3.1
  • v3.3.1rc1
  • v3.3.1rc2
  • v3.3.0
  • v3.2.0
  • v3.1.11
  • v3.1.10
  • v3.1.9
  • v3.1.8.2
  • v3.1.8.1
  • v3.1.8
  • v3.1.7
  • v3.1.6
  • v3.1.5
  • v3.1.4
  • v3.1.3
  • v3.1.2
  • v3.1
  • v3.0.1
40 results

opendht.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    opendht.cpp 18.99 KiB
    /*
     *  Copyright (C) 2014-2025 Savoir-faire Linux Inc.
     *  Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
     *
     *  This program is free software; you can redistribute it and/or modify
     *  it under the terms of the GNU General Public License as published by
     *  the Free Software Foundation; either version 3 of the License, or
     *  (at your option) any later version.
     *
     *  This program is distributed in the hope that it will be useful,
     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     *  GNU General Public License for more details.
     *
     *  You should have received a copy of the GNU General Public License
     *  along with this program. If not, see <https://www.gnu.org/licenses/>.
     */
    
    #include "opendht_c.h"
    
    #include <opendht.h>
    #include <opendht/log.h>
    
    using ValueSp = std::shared_ptr<dht::Value>;
    using PrivkeySp = std::shared_ptr<dht::crypto::PrivateKey>;
    using PubkeySp = std::shared_ptr<const dht::crypto::PublicKey>;
    using CertSp = std::shared_ptr<dht::crypto::Certificate>;
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    #include <errno.h>
    
    const char* dht_version()
    {
        return dht::version();
    }
    
    // dht::InfoHash
    
    inline dht_infohash dht_infohash_to_c(const dht::InfoHash& h) {
        dht_infohash ret;
        *reinterpret_cast<dht::InfoHash*>(&ret) = h;
        return ret;
    }
    
    inline dht_pkid dht_pkid_to_c(const dht::PkId& h) {
        dht_pkid ret;
        *reinterpret_cast<dht::PkId*>(&ret) = h;
        return ret;
    }
    
    const char* dht_infohash_print(const dht_infohash* h) {
        return reinterpret_cast<const dht::InfoHash*>(h)->to_c_str();
    }
    
    void dht_infohash_zero(dht_infohash* h) {
        *reinterpret_cast<dht::InfoHash*>(h) = dht::InfoHash{};
    }
    
    void dht_infohash_random(dht_infohash* h) {
        *reinterpret_cast<dht::InfoHash*>(h) = dht::InfoHash::getRandom();
    }
    
    void dht_infohash_get(dht_infohash* h, const uint8_t* dat, size_t dat_size) {
        *reinterpret_cast<dht::InfoHash*>(h) = dht::InfoHash::get(dat, dat_size);
    }
    
    void dht_infohash_get_from_string(dht_infohash* h, const char* dat) {
        *reinterpret_cast<dht::InfoHash*>(h) = dht::InfoHash::get((const uint8_t*)dat, (size_t)strlen(dat));
    }
    
    bool dht_infohash_is_zero(const dht_infohash* h) {
        return !static_cast<bool>(*reinterpret_cast<const dht::InfoHash*>(h));
    }
    
    void dht_infohash_from_hex(dht_infohash* h, const char* dat) {
        *h = dht_infohash_to_c(dht::InfoHash(std::string_view(dat, HASH_LEN*2)));
    }
    
    void dht_infohash_from_hex_null(dht_infohash* h, const char* dat) {
        *h = dht_infohash_to_c(dht::InfoHash(std::string_view(dat)));
    }
    
    const char* dht_pkid_print(const dht_pkid* h) {
        return reinterpret_cast<const dht::PkId*>(h)->to_c_str();
    }
    
    // dht::Blob
    void dht_blob_delete(dht_blob* data) {
        delete reinterpret_cast<dht::Blob*>(data);
    }
    
    dht_data_view dht_blob_get_data(const dht_blob* data) {
        dht_data_view view;
        view.data = reinterpret_cast<const dht::Blob*>(data)->data();
        view.size = reinterpret_cast<const dht::Blob*>(data)->size();
        return view;
    }
    
    // dht::Value
    dht_data_view dht_value_get_data(const dht_value* data) {
        const ValueSp& vsp(*reinterpret_cast<const ValueSp*>(data));
        dht_data_view view;
        view.data = vsp->data.data();
        view.size = vsp->data.size();
        return view;
    }
    
    dht_value_id dht_value_get_id(const dht_value* data) {
        const ValueSp& vsp(*reinterpret_cast<const ValueSp*>(data));
        return vsp->id;
    }
    
    dht_publickey* dht_value_get_owner(const dht_value* data) {
        const ValueSp& vsp(*reinterpret_cast<const ValueSp*>(data));
        return vsp->owner ? reinterpret_cast<dht_publickey*>(new PubkeySp(vsp->owner)) : nullptr;
    }
    
    dht_infohash dht_value_get_recipient(const dht_value* data) {
        const ValueSp& vsp(*reinterpret_cast<const ValueSp*>(data));
        return dht_infohash_to_c(vsp->recipient);
    }
    
    const char* dht_value_get_user_type(const dht_value* data) {
        const ValueSp& vsp(*reinterpret_cast<const ValueSp*>(data));
        return vsp->user_type.c_str();
    }
    
    void dht_value_set_user_type(dht_value* data, const char* user_type) {
        (*reinterpret_cast<ValueSp*>(data))->user_type = user_type;
    }
    
    dht_value* dht_value_with_id_new(const uint8_t* data, size_t size, uint64_t value_id) {
        return reinterpret_cast<dht_value*>(new ValueSp(std::make_shared<dht::Value>(dht::ValueType::USER_DATA.id, data, size, value_id)));
    }
    
    dht_value* dht_value_new(const uint8_t* data, size_t size) {
        return dht_value_with_id_new(data, size, 0);
    }
    
    dht_value* dht_value_new_from_string(const char* str) {
        ValueSp value = std::make_shared<dht::Value>((const uint8_t*)str, strlen(str));
        value->user_type = "text/plain";
        return reinterpret_cast<dht_value*>(new ValueSp(std::move(value)));
    }
    
    dht_value* dht_value_ref(const dht_value* v) {
        return reinterpret_cast<dht_value*>(new ValueSp(*reinterpret_cast<const ValueSp*>(v)));
    }
    
    void dht_value_unref(dht_value* v) {
        delete reinterpret_cast<ValueSp*>(v);
    }
    
    // dht::crypto::PublicKey
    dht_publickey* dht_publickey_import(const uint8_t* dat, size_t dat_size) {
        try {
            return reinterpret_cast<dht_publickey*>(new PubkeySp(std::make_shared<const dht::crypto::PublicKey>(dat, dat_size)));
        } catch (const dht::crypto::CryptoException& e) {
            return nullptr;
        }
    }
    
    void dht_publickey_delete(dht_publickey* pk) {
        delete reinterpret_cast<PubkeySp*>(pk);
    }
    
    int dht_publickey_export(const dht_publickey* pk, char* out, size_t* outlen) {
        const auto& pkey = *reinterpret_cast<const PubkeySp*>(pk);
        return pkey->pack((uint8_t*)out, outlen);
    }
    
    dht_infohash dht_publickey_get_id(const dht_publickey* pk) {
        const auto& pkey = *reinterpret_cast<const PubkeySp*>(pk);
        return dht_infohash_to_c(pkey->getId());
    }
    
    dht_pkid dht_publickey_get_long_id(const dht_publickey* pk) {
        const auto& pkey = *reinterpret_cast<const PubkeySp*>(pk);
        return dht_pkid_to_c(pkey->getLongId());
    }
    
    bool dht_publickey_check_signature(const dht_publickey* pk, const char* data, size_t data_size, const char* signature, size_t signature_size) {
        const auto& pkey = *reinterpret_cast<const PubkeySp*>(pk);
        return pkey->checkSignature((const uint8_t*)data, data_size, (const uint8_t*)signature, signature_size);
    }
    
    dht_blob* dht_publickey_encrypt(const dht_publickey* pk, const char* data, size_t data_size) {
        const auto& pkey = *reinterpret_cast<const PubkeySp*>(pk);
        try {
            auto rdata = std::make_unique<dht::Blob>();
            *rdata = pkey->encrypt((const uint8_t*)data, data_size);
            return (dht_blob*)rdata.release();
        } catch (...) {
            return nullptr;
        }
    }
    
    // dht::crypto::PrivateKey
    dht_privatekey* dht_privatekey_generate(unsigned key_length_bits) {
        if (key_length_bits == 0)
            key_length_bits = 4096;
        return reinterpret_cast<dht_privatekey*>(new PrivkeySp(std::make_shared<dht::crypto::PrivateKey>(dht::crypto::PrivateKey::generate(key_length_bits))));
    }
    
    dht_privatekey* dht_privatekey_import(const uint8_t* dat, size_t dat_size, const char* password) {
        try {
            return reinterpret_cast<dht_privatekey*>(new PrivkeySp(std::make_shared<dht::crypto::PrivateKey>(dat, dat_size, password)));
        } catch (const dht::crypto::CryptoException& e) {
            return nullptr;
        }
    }
    
    int dht_privatekey_export(const dht_privatekey* k, char* out, size_t* out_size, const char* password) {
        if (!out or !out_size or !*out_size)
            return -1;
        const auto& key = *reinterpret_cast<const PrivkeySp*>(k);
        return key->serialize((uint8_t*)out, out_size, password);
    }
    
    dht_publickey* dht_privatekey_get_publickey(const dht_privatekey* k) {
        const auto& key = *reinterpret_cast<const PrivkeySp*>(k);
        return reinterpret_cast<dht_publickey*>(new PubkeySp(key->getSharedPublicKey()));
    }
    
    dht_blob* dht_privatekey_decrypt(const dht_privatekey* k, const char* data, size_t data_size) {
        const auto& key = *reinterpret_cast<const PrivkeySp*>(k);
        try {
            auto rdata = std::make_unique<dht::Blob>();
            *rdata = key->decrypt((const uint8_t*)data, data_size);
            return (dht_blob*)rdata.release();
        } catch (...) {
            return nullptr;
        }
    }
    
    void dht_privatekey_delete(dht_privatekey* pk) {
        delete reinterpret_cast<PrivkeySp*>(pk);
    }
    
    // dht::crypto::Certificate
    dht_certificate* dht_certificate_import(const uint8_t* dat, size_t dat_size) {
        try {
            return reinterpret_cast<dht_certificate*>(new CertSp(std::make_shared<dht::crypto::Certificate>(dat, dat_size)));
        } catch (const dht::crypto::CryptoException& e) {
            return nullptr;
        }
    }
    
    void dht_certificate_delete(dht_certificate* c) {
        delete reinterpret_cast<CertSp*>(c);
    }
    
    dht_infohash dht_certificate_get_id(const dht_certificate* c) {
        const auto& cert = *reinterpret_cast<const CertSp*>(c);
        return dht_infohash_to_c(cert->getId());
    }
    
    dht_pkid dht_certificate_get_long_id(const dht_certificate* c) {
        const auto& cert = *reinterpret_cast<const CertSp*>(c);
        return dht_pkid_to_c(cert->getLongId());
    }
    
    dht_publickey* dht_certificate_get_publickey(const dht_certificate* c) {
        const auto& cert = *reinterpret_cast<const CertSp*>(c);
        return reinterpret_cast<dht_publickey*>(new PubkeySp(cert->getSharedPublicKey()));
    }
    
    // dht::crypto::Identity
    inline dht::crypto::Identity dht_identity_from_c(const dht_identity* cid) {
        dht::crypto::Identity id {};
        if (cid and cid->privatekey)
            id.first = *reinterpret_cast<const PrivkeySp*>(cid->privatekey);
        if (cid and cid->certificate)
            id.second = *reinterpret_cast<const CertSp*>(cid->certificate);
        return id;
    }
    
    inline dht_identity dht_identity_to_c(const dht::crypto::Identity& id) {
        dht_identity cid {};
        cid.privatekey = id.first ? reinterpret_cast<dht_privatekey*>(new PrivkeySp(id.first)) : NULL;
        cid.certificate = id.second ? reinterpret_cast<dht_certificate*>(new CertSp(id.second)) : NULL;
        return cid;
    }
    
    OPENDHT_C_PUBLIC dht_identity dht_identity_generate(const char* common_name, const dht_identity* ca) {
        return dht_identity_to_c(dht::crypto::generateIdentity(common_name, dht_identity_from_c(ca)));
    }
    
    OPENDHT_C_PUBLIC void dht_identity_delete(dht_identity* id) {
        if (id->certificate) {
            dht_certificate_delete(id->certificate);
            id->certificate = NULL;
        }
        if (id->privatekey) {
            dht_privatekey_delete(id->privatekey);
            id->privatekey = NULL;
        }
    }
    
    // config
    void dht_runner_config_default(dht_runner_config* config) {
        bzero(config, sizeof(dht_runner_config));
        config->threaded = true;
    }
    
    // dht::DhtRunner
    dht_runner* dht_runner_new(void) {
        return reinterpret_cast<dht_runner*>(new dht::DhtRunner);
    }
    
    void dht_runner_delete(dht_runner* runner) {
        delete reinterpret_cast<dht::DhtRunner*>(runner);
    }
    
    int dht_runner_run(dht_runner* r, in_port_t port) {
        auto runner = reinterpret_cast<dht::DhtRunner*>(r);
        try {
            runner->run(port, {}, true);
        } catch(...) {
            return ENOTCONN;
        }
        return 0;
    }
    
    int dht_runner_run_config(dht_runner* r, in_port_t port, const dht_runner_config* conf) {
        auto runner = reinterpret_cast<dht::DhtRunner*>(r);
        try {
            dht::DhtRunner::Config config;
            config.dht_config.node_config.is_bootstrap     = conf->dht_config.node_config.is_bootstrap;
            config.dht_config.node_config.maintain_storage = conf->dht_config.node_config.maintain_storage;
            config.dht_config.node_config.node_id          = *reinterpret_cast<const dht::InfoHash*>(&conf->dht_config.node_config.node_id);
            config.dht_config.node_config.network          = conf->dht_config.node_config.network;
            config.dht_config.node_config.persist_path     = conf->dht_config.node_config.persist_path
                                                                 ? std::string(conf->dht_config.node_config.persist_path)
                                                                 : std::string{};
    
            if (conf->dht_config.id.privatekey)
                config.dht_config.id.first = *reinterpret_cast<const PrivkeySp*>(conf->dht_config.id.privatekey);
    
            if (conf->dht_config.id.certificate)
                config.dht_config.id.second = *reinterpret_cast<const CertSp*>(conf->dht_config.id.certificate);
    
            config.threaded         = conf->threaded;
            config.proxy_server     = conf->proxy_server ? std::string(conf->proxy_server) : std::string{};
            config.push_node_id     = conf->push_node_id ? std::string(conf->push_node_id) : std::string{};
            config.push_token       = conf->push_token ? std::string(conf->push_token) : std::string{};
            config.push_topic       = conf->push_topic ? std::string(conf->push_topic) : std::string{};
            config.push_platform    = conf->push_platform ? std::string(conf->push_platform) : std::string{};
            config.proxy_user_agent = conf->proxy_user_agent ? std::string(conf->proxy_user_agent) : std::string{};
            config.peer_discovery   = conf->peer_discovery;
            config.peer_publish     = conf->peer_publish;
            if (conf->server_ca)
                config.server_ca    = *reinterpret_cast<const CertSp*>(conf->server_ca);
            config.client_identity  = dht_identity_from_c(&conf->client_identity);
    
            dht::DhtRunner::Context context;
            if (conf->log) {
                context.logger = dht::log::getStdLogger();
            }
            runner->run(port, config, std::move(context));
        } catch(...) {
            return ENOTCONN;
        }
        return 0;
    }
    
    void dht_runner_ping(dht_runner* r, struct sockaddr* addr, socklen_t addr_len, dht_done_cb done_cb, void* cb_user_data) {
        auto runner = reinterpret_cast<dht::DhtRunner*>(r);
        if (done_cb) {
            runner->bootstrap(dht::SockAddr(addr, addr_len), [done_cb, cb_user_data](bool ok){
                done_cb(ok, cb_user_data);
            });
        } else {
            runner->bootstrap(dht::SockAddr(addr, addr_len));
        }
    }
    
    void dht_runner_bootstrap(dht_runner* r, const char* host, const char* service) {
        auto runner = reinterpret_cast<dht::DhtRunner*>(r);
        if (service)
            runner->bootstrap(host, service);
        else
            runner->bootstrap(host);
    }
    
    void dht_runner_get(dht_runner* r, const dht_infohash* h, dht_get_cb cb, dht_done_cb done_cb, void* cb_user_data) {
        auto runner = reinterpret_cast<dht::DhtRunner*>(r);
        auto hash = reinterpret_cast<const dht::InfoHash*>(h);
        runner->get(*hash, [cb,cb_user_data](std::shared_ptr<dht::Value> value){
            return cb(reinterpret_cast<const dht_value*>(&value), cb_user_data);
        }, [done_cb, cb_user_data](bool ok){
            if (done_cb)
                done_cb(ok, cb_user_data);
        });
    }
    
    struct ScopeGuardCb {
        ScopeGuardCb(dht_shutdown_cb cb, void* data)
         : onDestroy(cb), userData(data) {}
    
        ~ScopeGuardCb() {
            if (onDestroy)
                onDestroy((void*)userData);
        }
    private:
        const dht_shutdown_cb onDestroy;
        void const* userData;
    };
    
    dht_op_token* dht_runner_listen(dht_runner* r, const dht_infohash* h, dht_value_cb cb, dht_shutdown_cb done_cb, void* cb_user_data) {
        auto runner = reinterpret_cast<dht::DhtRunner*>(r);
        auto hash = reinterpret_cast<const dht::InfoHash*>(h);
        auto fret = new std::future<size_t>;
        *fret = runner->listen(*hash, [
            cb,
            cb_user_data,
            guard = done_cb ? std::make_shared<ScopeGuardCb>(done_cb, cb_user_data) : std::shared_ptr<ScopeGuardCb>{}
        ](const std::vector<std::shared_ptr<dht::Value>>& values, bool expired) {
            for (const auto& value : values) {
                if (not cb(reinterpret_cast<const dht_value*>(&value), expired, cb_user_data))
                    return false;
            }
            return true;
        });
        return (dht_op_token*)fret;
    }
    
    void dht_runner_cancel_listen(dht_runner* r, const dht_infohash* h, dht_op_token* t) {
        auto runner = reinterpret_cast<dht::DhtRunner*>(r);
        auto hash = reinterpret_cast<const dht::InfoHash*>(h);
        auto token = reinterpret_cast<std::future<size_t>*>(t);
        runner->cancelListen(*hash, std::move(*token));
    }
    
    void dht_op_token_delete(dht_op_token* token) {
        delete reinterpret_cast<std::future<size_t>*>(token);
    }
    
    void dht_runner_put(dht_runner* r, const dht_infohash* h, const dht_value* v, dht_done_cb done_cb, void* cb_user_data, bool permanent) {
        auto runner = reinterpret_cast<dht::DhtRunner*>(r);
        auto hash = reinterpret_cast<const dht::InfoHash*>(h);
        auto value = reinterpret_cast<const ValueSp*>(v);
        runner->put(*hash, *value, [done_cb, cb_user_data](bool ok){
            if (done_cb)
                done_cb(ok, cb_user_data);
        }, dht::time_point::max(), permanent);
    }
    
    void dht_runner_put_signed(dht_runner* r, const dht_infohash* h, const dht_value* v, dht_done_cb done_cb, void* cb_user_data, bool permanent) {
        auto runner = reinterpret_cast<dht::DhtRunner*>(r);
        auto hash = reinterpret_cast<const dht::InfoHash*>(h);
        auto value = reinterpret_cast<const ValueSp*>(v);
        runner->putSigned(*hash, *value, [done_cb, cb_user_data](bool ok){
            if (done_cb)
                done_cb(ok, cb_user_data);
        }, permanent);
    }
    
    void dht_runner_put_encrypted(dht_runner* r, const dht_infohash* h, const dht_infohash* to, const dht_value* v, dht_done_cb done_cb, void* cb_user_data, bool permanent) {
        auto runner = reinterpret_cast<dht::DhtRunner*>(r);
        auto hash = reinterpret_cast<const dht::InfoHash*>(h);
        auto toHash = reinterpret_cast<const dht::InfoHash*>(to);
        auto value = reinterpret_cast<const ValueSp*>(v);
        runner->putEncrypted(*hash, *toHash, *value, [done_cb, cb_user_data](bool ok){
            if (done_cb)
                done_cb(ok, cb_user_data);
        }, permanent);
    }
    
    void dht_runner_cancel_put(dht_runner* r, const dht_infohash* h, dht_value_id value_id) {
        auto runner = reinterpret_cast<dht::DhtRunner*>(r);
        auto hash = reinterpret_cast<const dht::InfoHash*>(h);
        runner->cancelPut(*hash, value_id);
    }
    
    void dht_runner_shutdown(dht_runner* r, dht_shutdown_cb done_cb, void* cb_user_data) {
        auto runner = reinterpret_cast<dht::DhtRunner*>(r);
        runner->shutdown([done_cb, cb_user_data](){
            if (done_cb)
                done_cb(cb_user_data);
        });
    }
    
    bool dht_runner_is_running(const dht_runner* r) {
        if (not r) return false;
        auto runner = reinterpret_cast<const dht::DhtRunner*>(r);
        return runner->isRunning();
    }
    
    in_port_t dht_runner_get_bound_port(const dht_runner* r, sa_family_t af) {
        auto runner = reinterpret_cast<const dht::DhtRunner*>(r);
        return runner->getBoundPort(af);
    }
    
    dht_infohash dht_runner_get_node_id(const dht_runner* r) {
        auto runner = reinterpret_cast<const dht::DhtRunner*>(r);
        dht_infohash ret;
        *reinterpret_cast<dht::InfoHash*>(&ret) = runner->getNodeId();
        return ret;
    }
    
    dht_infohash dht_runner_get_id(const dht_runner* r) {
        auto runner = reinterpret_cast<const dht::DhtRunner*>(r);
        dht_infohash ret;
        *reinterpret_cast<dht::InfoHash*>(&ret) = runner->getId();
        return ret;
    }
    
    struct sockaddr** dht_runner_get_public_address(const dht_runner* r) {
        auto runner = reinterpret_cast<const dht::DhtRunner*>(r);
        auto addrs = const_cast<dht::DhtRunner*>(runner)->getPublicAddress();
        if (addrs.empty())
            return nullptr;
        auto ret = (struct sockaddr**)malloc(sizeof(struct sockaddr*) * (addrs.size() + 1));
        for (size_t i=0; i<addrs.size(); i++)
            ret[i] = addrs[i].release();
        ret[addrs.size()] = nullptr;
        return ret;
    }
    
    #ifdef __cplusplus
    }
    #endif