#include "shim.h"
#include <cstdlib>
#include <cstring>
#include <memory>
#include <thread>
#include <chrono>
#include "api.h"
#include "Destination.h"
#include "Streaming.h"
#include "Identity.h"
struct I2pdDestination {
std::shared_ptr<i2p::client::ClientDestination> ptr;
};
struct I2pdStream {
std::shared_ptr<i2p::stream::Stream> ptr;
};
extern "C" {
void i2pd_init(const char *app_name) {
if (!app_name)
return;
try {
char name[256];
std::strncpy(name, app_name, sizeof(name) - 1);
name[sizeof(name) - 1] = '\0';
char *argv[] = {name};
i2p::api::InitI2P(1, argv, app_name);
} catch (...) {
}
}
void i2pd_start(void) {
try {
i2p::api::StartI2P();
} catch (...) {
}
}
void i2pd_stop(void) {
try {
i2p::api::StopI2P();
} catch (...) {
}
}
void i2pd_terminate(void) {
try {
i2p::api::TerminateI2P();
} catch (...) {
}
}
I2pdDestination *i2pd_create_transient_destination(void) {
try {
auto dest = i2p::api::CreateLocalDestination(true);
if (!dest)
return nullptr;
return new I2pdDestination{dest};
} catch (...) {
return nullptr;
}
}
int i2pd_generate_keys(int sig_type, int crypto_type, unsigned char **out_buf, size_t *out_len) {
(void)crypto_type;
if (!out_buf || !out_len)
return 0;
try {
auto keys = i2p::data::PrivateKeys::CreateRandomKeys(
static_cast<i2p::data::SigningKeyType>(sig_type),
i2p::data::CRYPTO_KEY_TYPE_ELGAMAL,
true);
size_t len = keys.GetFullLen();
auto *buf = static_cast<unsigned char *>(std::malloc(len));
if (!buf)
return 0;
keys.ToBuffer(buf, len);
*out_buf = buf;
*out_len = len;
return 1;
} catch (...) {
return 0;
}
}
I2pdDestination *i2pd_create_persistent_destination(const unsigned char *keys_buf, size_t keys_len, int is_public, const char *encryption_types_csv) {
if (!keys_buf || !keys_len)
return nullptr;
try {
i2p::data::PrivateKeys keys;
if (keys.FromBuffer(keys_buf, keys_len) == 0)
return nullptr;
std::shared_ptr<i2p::client::ClientDestination> dest;
if (encryption_types_csv && *encryption_types_csv) {
i2p::util::Mapping params;
params.Insert(i2p::client::I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, encryption_types_csv);
dest = i2p::api::CreateLocalDestination(keys, is_public != 0, ¶ms);
} else {
dest = i2p::api::CreateLocalDestination(keys, is_public != 0);
}
if (!dest)
return nullptr;
return new I2pdDestination{dest};
} catch (...) {
return nullptr;
}
}
void i2pd_free_buffer(unsigned char *ptr) {
std::free(ptr);
}
void i2pd_destroy_destination(I2pdDestination *dest) {
if (!dest)
return;
try {
i2p::api::DestroyLocalDestination(dest->ptr);
} catch (...) {
}
delete dest;
}
char *i2pd_destination_b32_address(I2pdDestination *dest) {
if (!dest || !dest->ptr)
return nullptr;
try {
std::string addr = dest->ptr->GetIdentHash().ToBase32() + ".b32.i2p";
char *out = static_cast<char *>(std::malloc(addr.size() + 1));
if (!out)
return nullptr;
std::memcpy(out, addr.c_str(), addr.size() + 1);
return out;
} catch (...) {
return nullptr;
}
}
int i2pd_destination_ident_hash(I2pdDestination *dest, unsigned char *out) {
if (!dest || !dest->ptr || !out)
return 0;
try {
std::memcpy(out, dest->ptr->GetIdentHash().data(), 32);
return 1;
} catch (...) {
return 0;
}
}
void i2pd_free_string(char *ptr) {
std::free(ptr);
}
void i2pd_accept_stream(I2pdDestination *dest, I2pdAcceptCallback cb, void *ctx) {
if (!dest || !dest->ptr || !cb)
return;
try {
i2p::api::AcceptStream(dest->ptr, [cb, ctx](std::shared_ptr<i2p::stream::Stream> stream) {
if (!stream)
return;
auto *s = new I2pdStream{stream};
cb(ctx, s);
});
} catch (...) {
}
}
I2pdStream *i2pd_create_stream(I2pdDestination *dest, const unsigned char *remote_ident_hash, int timeout_seconds) {
if (!dest || !dest->ptr || !remote_ident_hash)
return nullptr;
try {
i2p::data::IdentHash hash(remote_ident_hash);
auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(timeout_seconds);
while (true) {
auto stream = i2p::api::CreateStream(dest->ptr, hash);
if (stream)
return new I2pdStream{stream};
if (std::chrono::steady_clock::now() >= deadline)
return nullptr;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
} catch (...) {
return nullptr;
}
}
long i2pd_stream_send(I2pdStream *stream, const unsigned char *buf, size_t len) {
if (!stream || !stream->ptr)
return -1;
try {
return static_cast<long>(stream->ptr->Send(buf, len));
} catch (...) {
return -1;
}
}
long i2pd_stream_receive(I2pdStream *stream, unsigned char *buf, size_t len, int timeout_seconds) {
if (!stream || !stream->ptr)
return -1;
try {
return static_cast<long>(stream->ptr->Receive(buf, len, timeout_seconds));
} catch (...) {
return -1;
}
}
int i2pd_stream_is_open(I2pdStream *stream) {
if (!stream || !stream->ptr)
return 0;
try {
return stream->ptr->IsOpen() ? 1 : 0;
} catch (...) {
return 0;
}
}
void i2pd_stream_close(I2pdStream *stream) {
if (!stream || !stream->ptr)
return;
try {
stream->ptr->Close();
} catch (...) {
}
}
void i2pd_destroy_stream(I2pdStream *stream) {
if (!stream)
return;
delete stream;
}
}