Browse Source

Merge pull request #684 from PurpleI2P/openssl

recent changes
pull/63/merge
orignal 8 years ago committed by GitHub
parent
commit
bc92586323
  1. 7
      Crypto.cpp
  2. 10
      Crypto.h
  3. 2
      DaemonLinux.cpp
  4. 24
      Destination.cpp
  5. 15
      Destination.h
  6. 10
      FS.cpp
  7. 2
      I2PTunnel.cpp
  8. 2
      Identity.cpp
  9. 2
      Makefile
  10. 1
      NetDb.h
  11. 5
      README.md
  12. 32
      Win32/installer.iss
  13. 2
      android/AndroidManifest.xml
  14. 16
      contrib/rpm/i2pd.service
  15. 129
      contrib/rpm/i2pd.spec
  16. 7
      debian/changelog
  17. 25
      docs/build_notes_unix.md
  18. 2
      docs/i2pd.conf
  19. 4
      version.h

7
Crypto.cpp

@ -135,11 +135,8 @@ namespace crypto
DSA * CreateDSA () DSA * CreateDSA ()
{ {
DSA * dsa = DSA_new (); DSA * dsa = DSA_new ();
dsa->p = BN_dup (dsap); DSA_set0_pqg (dsa, BN_dup (dsap), BN_dup (dsaq), BN_dup (dsag));
dsa->q = BN_dup (dsaq); DSA_set0_key (dsa, NULL, NULL);
dsa->g = BN_dup (dsag);
dsa->priv_key = NULL;
dsa->pub_key = NULL;
return dsa; return dsa;
} }

10
Crypto.h

@ -279,6 +279,16 @@ namespace crypto
void InitCrypto (bool precomputation); void InitCrypto (bool precomputation);
void TerminateCrypto (); void TerminateCrypto ();
// take care about openssl version
#include <openssl/opensslv.h>
#if (OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER) // 1.1.0 or LibreSSL
// define getters and setters introduced in 1.1.0
inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) { d->p = p; d->q = q; d->g = g; return 1; }
inline int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) { d->pub_key = pub_key; d->priv_key = priv_key; return 1; }
#endif
} }
} }

2
DaemonLinux.cpp

@ -75,10 +75,12 @@ namespace i2p
return false; return false;
} }
#if !defined(__OpenBSD__)
// point std{in,out,err} descriptors to /dev/null // point std{in,out,err} descriptors to /dev/null
stdin = freopen("/dev/null", "r", stdin); stdin = freopen("/dev/null", "r", stdin);
stdout = freopen("/dev/null", "w", stdout); stdout = freopen("/dev/null", "w", stdout);
stderr = freopen("/dev/null", "w", stderr); stderr = freopen("/dev/null", "w", stderr);
#endif
} }
// Pidfile // Pidfile

24
Destination.cpp

@ -373,26 +373,24 @@ namespace client
for (int i = 0; i < num; i++) for (int i = 0; i < num; i++)
{ {
i2p::data::IdentHash peerHash (buf + 33 + i*32); i2p::data::IdentHash peerHash (buf + 33 + i*32);
auto floodfill = i2p::data::netdb.FindRouter (peerHash); if (!request->excluded.count (peerHash) && !i2p::data::netdb.FindRouter (peerHash))
if (floodfill)
{
LogPrint (eLogInfo, "Destination: Requesting ", key.ToBase64 (), " at ", peerHash.ToBase64 ());
if (SendLeaseSetRequest (key, floodfill, request))
found = true;
}
else
{ {
LogPrint (eLogInfo, "Destination: Found new floodfill, request it"); // TODO: recheck this message LogPrint (eLogInfo, "Destination: Found new floodfill, request it"); // TODO: recheck this message
i2p::data::netdb.RequestDestination (peerHash); i2p::data::netdb.RequestDestination (peerHash);
} }
} }
if (!found)
LogPrint (eLogError, "Destination: Suggested floodfills are not presented in netDb"); auto floodfill = i2p::data::netdb.GetClosestFloodfill (key, request->excluded);
if (floodfill)
{
LogPrint (eLogInfo, "Destination: Requesting ", key.ToBase64 (), " at ", floodfill->GetIdentHash ().ToBase64 ());
if (SendLeaseSetRequest (key, floodfill, request))
found = true;
}
} }
else
LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills");
if (!found) if (!found)
{ {
LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills");
if (request->requestComplete) request->requestComplete (nullptr); if (request->requestComplete) request->requestComplete (nullptr);
m_LeaseSetRequests.erase (key); m_LeaseSetRequests.erase (key);
} }
@ -718,6 +716,7 @@ namespace client
return false; return false;
} }
#ifdef I2LUA
void ClientDestination::Ready(ReadyPromise & p) void ClientDestination::Ready(ReadyPromise & p)
{ {
ScheduleCheckForReady(&p); ScheduleCheckForReady(&p);
@ -741,6 +740,7 @@ namespace client
else // we are not ready else // we are not ready
ScheduleCheckForReady(p); ScheduleCheckForReady(p);
} }
#endif
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len) void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
{ {

15
Destination.h

@ -8,7 +8,9 @@
#include <set> #include <set>
#include <string> #include <string>
#include <functional> #include <functional>
#ifdef I2LUA
#include <future> #include <future>
#endif
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "Identity.h" #include "Identity.h"
#include "TunnelPool.h" #include "TunnelPool.h"
@ -145,8 +147,13 @@ namespace client
class ClientDestination: public LeaseSetDestination class ClientDestination: public LeaseSetDestination
{ {
public: public:
#ifdef I2LUA
// type for informing that a client destination is ready // type for informing that a client destination is ready
typedef std::promise<std::shared_ptr<ClientDestination> > ReadyPromise; typedef std::promise<std::shared_ptr<ClientDestination> > ReadyPromise;
// informs promise with shared_from_this() when this destination is ready to use
// if cancelled before ready, informs promise with nullptr
void Ready(ReadyPromise & p);
#endif
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr); ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~ClientDestination (); ~ClientDestination ();
@ -154,10 +161,6 @@ namespace client
bool Start (); bool Start ();
bool Stop (); bool Stop ();
// informs promise with shared_from_this() when this destination is ready to use
// if cancelled before ready, informs promise with nullptr
void Ready(ReadyPromise & p);
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); }; void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
@ -191,10 +194,10 @@ namespace client
std::shared_ptr<ClientDestination> GetSharedFromThis () std::shared_ptr<ClientDestination> GetSharedFromThis ()
{ return std::static_pointer_cast<ClientDestination>(shared_from_this ()); } { return std::static_pointer_cast<ClientDestination>(shared_from_this ()); }
void PersistTemporaryKeys (); void PersistTemporaryKeys ();
#ifdef I2LUA
void ScheduleCheckForReady(ReadyPromise * p); void ScheduleCheckForReady(ReadyPromise * p);
void HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p); void HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p);
#endif
private: private:
i2p::data::PrivateKeys m_Keys; i2p::data::PrivateKeys m_Keys;

10
FS.cpp

@ -46,8 +46,18 @@ namespace fs {
} }
#if defined(WIN32) || defined(_WIN32) #if defined(WIN32) || defined(_WIN32)
char localAppData[MAX_PATH]; char localAppData[MAX_PATH];
// check executable directory first
GetModuleFileName (NULL, localAppData, MAX_PATH);
auto execPath = boost::filesystem::path(localAppData).parent_path();
// if config file exists in .exe's folder use it
if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string
dataDir = execPath.string ();
else
{
// otherwise %appdata%
SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData); SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData);
dataDir = std::string(localAppData) + "\\" + appName; dataDir = std::string(localAppData) + "\\" + appName;
}
return; return;
#elif defined(MAC_OSX) #elif defined(MAC_OSX)
char *home = getenv("HOME"); char *home = getenv("HOME");

2
I2PTunnel.cpp

@ -176,6 +176,8 @@ namespace client
else else
Terminate (); Terminate ();
} }
else
Terminate ();
} }
else else
Write (m_StreamBuffer, bytes_transferred); Write (m_StreamBuffer, bytes_transferred);

2
Identity.cpp

@ -309,6 +309,7 @@ namespace data
void IdentityEx::CreateVerifier () const void IdentityEx::CreateVerifier () const
{ {
if (m_Verifier) return; // don't create again
auto keyType = GetSigningKeyType (); auto keyType = GetSigningKeyType ();
switch (keyType) switch (keyType)
{ {
@ -476,6 +477,7 @@ namespace data
void PrivateKeys::CreateSigner () const void PrivateKeys::CreateSigner () const
{ {
if (m_Signer) return;
switch (m_Public->GetSigningKeyType ()) switch (m_Public->GetSigningKeyType ())
{ {
case SIGNING_KEY_TYPE_DSA_SHA1: case SIGNING_KEY_TYPE_DSA_SHA1:

2
Makefile

@ -21,7 +21,7 @@ ifeq ($(UNAME),Darwin)
else else
include Makefile.osx include Makefile.osx
endif endif
else ifeq ($(shell echo $(UNAME) | $(GREP) -c FreeBSD),1) else ifeq ($(shell echo $(UNAME) | $(GREP) -Ec '(Free|Open)BSD'),1)
DAEMON_SRC += DaemonLinux.cpp DAEMON_SRC += DaemonLinux.cpp
include Makefile.bsd include Makefile.bsd
else ifeq ($(UNAME),Linux) else ifeq ($(UNAME),Linux)

1
NetDb.h

@ -8,7 +8,6 @@
#include <string> #include <string>
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <future>
#include "Base.h" #include "Base.h"
#include "Gzip.h" #include "Gzip.h"

5
README.md

@ -1,6 +1,8 @@
i2pd i2pd
==== ====
[Русская версия](https://github.com/PurpleI2P/i2pd_docs_ru/blob/master/README.md)
i2pd (I2P Daemon) is a full-featured C++ implementation of I2P client. i2pd (I2P Daemon) is a full-featured C++ implementation of I2P client.
I2P (Invisible Internet Protocol) is a universal anonymous network layer. I2P (Invisible Internet Protocol) is a universal anonymous network layer.
@ -37,11 +39,12 @@ i2pd from source on your OS.
* Mac OS X * Mac OS X
* FreeBSD * FreeBSD
* Android * Android
* iOS
Using i2pd Using i2pd
---------- ----------
See [documentation](https://i2pd.readthedocs.io/en/latest/) and See [documentation](https://i2pd.readthedocs.io/en/latest/usage.html) and
[example config file](https://github.com/PurpleI2P/i2pd/blob/openssl/docs/i2pd.conf). [example config file](https://github.com/PurpleI2P/i2pd/blob/openssl/docs/i2pd.conf).
Donations Donations

32
Win32/installer.iss

@ -0,0 +1,32 @@
#define I2Pd_AppName "i2pd"
#define I2Pd_ver "2.10.0"
[Setup]
AppName={#I2Pd_AppName}
AppVersion={#I2Pd_ver}
DefaultDirName={pf}\I2Pd
DefaultGroupName=I2Pd
UninstallDisplayIcon={app}\I2Pd.exe
OutputDir=.
LicenseFile=../LICENSE
OutputBaseFilename=setup_{#I2Pd_AppName}_v{#I2Pd_ver}
InternalCompressLevel=ultra64
Compression=lzma/ultra64
SolidCompression=true
ArchitecturesInstallIn64BitMode=x64
[Files]
Source: "..\i2pd_x86.exe"; DestDir: "{app}"; DestName: "i2pd.exe"; Flags: ignoreversion; Check: not IsWin64
Source: "..\i2pd_x64.exe"; DestDir: "{app}"; DestName: "i2pd.exe"; Flags: ignoreversion; Check: IsWin64
Source: "..\README.md"; DestDir: "{app}"; DestName: "Readme.txt"; Flags: onlyifdoesntexist
Source: "..\docs\i2pd.conf"; DestDir: "{userappdata}\i2pd"; Flags: onlyifdoesntexist
Source: "..\docs\subscriptions.txt"; DestDir: "{userappdata}\i2pd"; Flags: onlyifdoesntexist
Source: "..\docs\tunnels.conf"; DestDir: "{userappdata}\i2pd"; Flags: onlyifdoesntexist
Source: "..\contrib\*"; DestDir: "{userappdata}\i2pd"; Flags: onlyifdoesntexist recursesubdirs createallsubdirs
[Icons]
Name: "{group}\I2Pd"; Filename: "{app}\i2pd.exe"
Name: "{group}\Readme"; Filename: "{app}\Readme.txt"
[UninstallDelete]
Type: filesandordirs; Name: {app}\*

2
android/AndroidManifest.xml

@ -2,7 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.purplei2p.i2pd" package="org.purplei2p.i2pd"
android:versionCode="1" android:versionCode="1"
android:versionName="1.0"> android:versionName="2.10.0">
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="24"/> <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="24"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>

16
contrib/rpm/i2pd.service

@ -0,0 +1,16 @@
[Unit]
Description=I2P router
After=network.target
[Service]
User=i2pd
Group=i2pd
Type=simple
ExecStart=/usr/bin/i2pd --service
PIDFile=/var/lib/i2pd/i2pd.pid
Restart=always
PrivateTmp=true
[Install]
WantedBy=multi-user.target

129
contrib/rpm/i2pd.spec

@ -0,0 +1,129 @@
Name: i2pd
Version: 2.10.0
Release: 3%{?dist}
Summary: I2P router written in C++
License: BSD
URL: https://github.com/PurpleI2P/i2pd
Source0: https://github.com/PurpleI2P/i2pd/archive/%{version}/%name-%version.tar.gz
%if 0%{?rhel} == 7
BuildRequires: cmake3
%else
BuildRequires: cmake
%endif
BuildRequires: chrpath
BuildRequires: gcc-c++
BuildRequires: zlib-devel
BuildRequires: boost-devel
BuildRequires: openssl-devel
BuildRequires: miniupnpc-devel
BuildRequires: systemd-units
%description
C++ implementation of I2P.
%package systemd
Summary: Files to run I2P router under systemd
Requires: i2pd
Requires: systemd
Requires(pre): %{_sbindir}/useradd %{_sbindir}/groupadd
Obsoletes: %{name}-daemon
%description systemd
C++ implementation of I2P.
This package contains systemd unit file to run i2pd as a system service
using dedicated user's permissions.
%prep
%setup -q
%build
cd build
%if 0%{?rhel} == 7
%cmake3 \
-DWITH_LIBRARY=OFF \
-DWITH_UPNP=ON \
-DWITH_HARDENING=ON \
-DBUILD_SHARED_LIBS:BOOL=OFF
%else
%cmake \
-DWITH_LIBRARY=OFF \
-DWITH_UPNP=ON \
-DWITH_HARDENING=ON \
-DBUILD_SHARED_LIBS:BOOL=OFF
%endif
make %{?_smp_mflags}
%install
cd build
chrpath -d i2pd
install -D -m 755 i2pd %{buildroot}%{_bindir}/i2pd
install -D -m 644 %{_builddir}/%{name}-%{version}/contrib/rpm/i2pd.service %{buildroot}/%{_unitdir}/i2pd.service
install -d -m 700 %{buildroot}/%{_sharedstatedir}/i2pd
%pre systemd
getent group i2pd >/dev/null || %{_sbindir}/groupadd -r i2pd
getent passwd i2pd >/dev/null || \
%{_sbindir}/useradd -r -g i2pd -s %{_sbindir}/nologin \
-d %{_sharedstatedir}/i2pd -c 'I2P Service' i2pd
%post systemd
%systemd_post i2pd.service
%preun systemd
%systemd_preun i2pd.service
%postun systemd
%systemd_postun_with_restart i2pd.service
%files
%doc LICENSE README.md
%_bindir/i2pd
%files systemd
/%_unitdir/i2pd.service
%dir %attr(0700,i2pd,i2pd) %_sharedstatedir/i2pd
%changelog
* Tue Oct 20 2016 Anatolii Vorona <vorona.tolik@gmail.com> - 2.10.0-3
- add support C7
- move rpm-related files to contrib folder
* Sun Oct 16 2016 Oleg Girko <ol@infoserver.lv> - 2.10.0-1
- update to 2.10.0
* Sun Aug 14 2016 Oleg Girko <ol@infoserver.lv> - 2.9.0-1
- update to 2.9.0
* Sun Aug 07 2016 Oleg Girko <ol@infoserver.lv> - 2.8.0-2
- rename daemon subpackage to systemd
* Sat Aug 06 2016 Oleg Girko <ol@infoserver.lv> - 2.8.0-1
- update to 2.8.0
- remove wrong rpath from i2pd binary
- add daemon subpackage with systemd unit file
* Sat May 21 2016 Oleg Girko <ol@infoserver.lv> - 2.7.0-1
- update to 2.7.0
* Tue Apr 05 2016 Oleg Girko <ol@infoserver.lv> - 2.6.0-1
- update to 2.6.0
* Tue Jan 26 2016 Yaroslav Sidlovsky <zawertun@gmail.com> - 2.3.0-1
- initial package for version 2.3.0

7
debian/changelog vendored

@ -1,3 +1,10 @@
i2pd (2.10.0-1) unstable; urgency=low
* updated to version 2.10.0/0.9.27
* reseed.verify set to true by default
-- orignal <orignal@i2pmail.org> Sun, 16 Oct 2016 13:55:40 +0000
i2pd (2.9.0-1) unstable; urgency=low i2pd (2.9.0-1) unstable; urgency=low
* updated to version 2.9.0 * updated to version 2.9.0

25
docs/build_notes_unix.md

@ -4,8 +4,9 @@ Building on Unix systems
First of all we need to make sure that all dependencies are satisfied. First of all we need to make sure that all dependencies are satisfied.
This doc is trying to cover: This doc is trying to cover:
* [Debian/Ubuntu](#debianubuntu) (contains packaging instructions) * [Debian/Ubuntu](#debian-ubuntu) (contains packaging instructions)
* [Fedora/Centos](#fedoracentos) * [Fedora/Centos](#fedora-centos)
* [Fedora/Centos](#mac-os-x)
* [FreeBSD](#freebsd) * [FreeBSD](#freebsd)
Make sure you have all required dependencies for your system successfully installed. Make sure you have all required dependencies for your system successfully installed.
@ -73,15 +74,6 @@ sudo yum install make cmake gcc gcc-c++
*Latest Fedora system using [DNF](https://en.wikipedia.org/wiki/DNF_(software)) instead of YUM by default, you may prefer to use DNF, but YUM should be ok* *Latest Fedora system using [DNF](https://en.wikipedia.org/wiki/DNF_(software)) instead of YUM by default, you may prefer to use DNF, but YUM should be ok*
> *Centos 7 has CMake 2.8.11 in the official repositories that too old to build i2pd, CMake >=2.8.12 is required*
> You could build CMake for Centos manualy(WARNING there are a lot of build dependencies!):
> ```bash
> wget https://kojipkgs.fedoraproject.org/packages/cmake/2.8.12/3.fc21/src/cmake-2.8.12-3.fc21.src.rpm
> yum-builddep cmake-2.8.12-3.fc21.src.rpm
> rpmbuild --rebuild cmake-2.8.12-3.fc21.src.rpm
> yum install ~/rpmbuild/RPMS/x86_64/cmake-2.8.12-3.el7.centos.x86_64.rpm
> ```
Also you will need a bunch of development libraries Also you will need a bunch of development libraries
```bash ```bash
sudo yum install boost-devel openssl-devel sudo yum install boost-devel openssl-devel
@ -91,11 +83,20 @@ If you need UPnP support (don't forget to run CMake with `WITH_UPNP=ON`) miniupn
```bash ```bash
miniupnpc-devel miniupnpc-devel
``` ```
> *Centos 7 has CMake 2.8.11 in the official repositories that too old to build i2pd, CMake >=2.8.12 is required.*
>
> But you can use cmake3 from the epel repository:
> ```bash
> yum install epel-release -y
> yum install make cmake3 gcc gcc-c++ miniupnpc-devel boost-devel openssl-devel -y
> cmake3 -DWITH_LIBRARY=OFF -DWITH_UPNP=ON -DWITH_HARDENING=ON -DBUILD_SHARED_LIBS:BOOL=OFF
> make
> ```
MAC OS X MAC OS X
-------- --------
Requires homebrew Requires [homebrew](http://brew.sh/)
```bash ```bash
brew install libressl boost brew install libressl boost

2
docs/i2pd.conf

@ -92,6 +92,8 @@ ipv6 = false
# name = I2Pd # name = I2Pd
[reseed] [reseed]
## Enable or disable reseed data verification.
verify = true
## URLs to request reseed data from, separated by comma ## URLs to request reseed data from, separated by comma
## Default: "mainline" I2P Network reseeds ## Default: "mainline" I2P Network reseeds
# urls = https://reseed.i2p-projekt.de/,https://i2p.mooo.com/netDb/,https://netdb.i2p2.no/ # urls = https://reseed.i2p-projekt.de/,https://i2p.mooo.com/netDb/,https://netdb.i2p2.no/

4
version.h

@ -7,7 +7,7 @@
#define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c) #define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c)
#define I2PD_VERSION_MAJOR 2 #define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 9 #define I2PD_VERSION_MINOR 10
#define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_PATCH 0 #define I2PD_VERSION_PATCH 0
#define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO)
@ -21,7 +21,7 @@
#define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9 #define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 26 #define I2P_VERSION_MICRO 27
#define I2P_VERSION_PATCH 0 #define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

Loading…
Cancel
Save