Merge pull request #1583 from PurpleI2P/openssl

2.35.0
This commit is contained in:
orignal 2020-11-30 14:53:45 -05:00 committed by GitHub
commit 2ff6f9d346
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
86 changed files with 1781 additions and 1241 deletions

20
.github/workflows/build-osx.yml vendored Normal file
View File

@ -0,0 +1,20 @@
name: Build on OSX
on: [push, pull_request]
jobs:
build:
name: With USE_UPNP=${{ matrix.with_upnp }}
runs-on: macOS-latest
strategy:
fail-fast: true
matrix:
with_upnp: ['yes', 'no']
steps:
- uses: actions/checkout@v2
- name: install packages
run: |
brew update
brew install boost miniupnpc openssl@1.1
- name: build application
run: make HOMEBREW=1 USE_UPNP=${{ matrix.with_upnp }} PREFIX=$GITHUB_WORKSPACE/output -j3

View File

@ -26,4 +26,6 @@ jobs:
install: base-devel mingw-w64-${{ matrix.arch }}-gcc mingw-w64-${{ matrix.arch }}-boost mingw-w64-${{ matrix.arch }}-openssl mingw-w64-${{ matrix.arch }}-miniupnpc
update: true
- name: build application
run: make USE_UPNP=yes DEBUG=no -j3
run: |
mkdir -p obj/Win32 obj/libi2pd obj/libi2pd_client obj/daemon
make USE_UPNP=yes DEBUG=no -j3

View File

@ -1,10 +1,10 @@
name: Build on Ubuntu with make
name: Build on Ubuntu
on: [push, pull_request]
jobs:
build:
name: Building with USE_UPNP=${{ matrix.with_upnp }} flag
name: With USE_UPNP=${{ matrix.with_upnp }}
runs-on: ubuntu-16.04
strategy:
fail-fast: true
@ -18,4 +18,20 @@ jobs:
sudo apt-get update
sudo apt-get install build-essential libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev
- name: build application
run: make USE_AVX=no USE_AESNI=no USE_UPNP=${{ matrix.with_upnp }} -j3
run: make USE_UPNP=${{ matrix.with_upnp }} -j3
build_qt:
name: With QT GUI
runs-on: ubuntu-16.04
steps:
- uses: actions/checkout@v2
- name: install packages
run: |
sudo add-apt-repository ppa:mhier/libboost-latest
sudo apt-get update
sudo apt-get install build-essential qt5-default libqt5gui5 libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev
- name: build application
run: |
cd qt/i2pd_qt
qmake
make -j3

3
.gitignore vendored
View File

@ -3,11 +3,12 @@
router.info
router.keys
i2p
libi2pd.so
netDb
/i2pd
/libi2pd.a
/libi2pdclient.a
/libi2pd.so
/libi2pdclient.so
*.exe

View File

@ -1,6 +1,57 @@
# for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog
## [2.35.0] - 2020-11-30
### Added
- ECIES-x25519 routers
- Random intro keys for SSU
- Graceful shutdown timer for windows
- Send queue for I2CP messages
- Update DSA router keys to EdDSA
- TCP_QUICKACK for NTCP2 sockets on Linux
### Changed
- Exclude floodfills with DSA signatures and < 0.9.28
- Random intervals between tunnel tests and manage for tunnel pools
- Don't replace an addressbook record by one with DSA signature
- Publish RouterInfo after update
- Create paired inbound tunnels if no inbound tunnels yet
- Reseed servers list
### Fixed
- Transient signature length, if different from identity
- Terminate I2CP session if destroyed
- RouterInfo publishing confirmation
- Check if ECIES-X25519-AEAD-Ratchet session expired before generating more tags
- Correct block size for delivery type local for ECIES-X25519-AEAD-Ratchet
## [2.34.0] - 2020-10-27
### Added
- Ping responses for streaming
- STREAM FORWARD for SAM
- Tunnels through ECIES-x25519 routers
- Single thread for I2CP
- Shared transient destination between proxies
- Database lookups from ECIES destinations with ratchets response
- Handle WebDAV HTTP methods
- Don't try to connect or build tunnels if offline
- Validate IP when trying connect to remote peer
- Handle ICMP responses and WinAPI errors for SSU
### Changed
- Removed NTCP
- Dropped gcc 4.7 support
- Encyption type 0,4 by default for client tunnels
- Stripped out some HTTP header for HTTP server response
- HTTP 1.1 addressbook requests
- Set LeaseSet type to 3 for ratchets if not specified
- Handle SSU v4 and v6 messages in one thread
- Eliminate DH keys thread
### Fixed
- Random crashes on I2CP session disconnect
- Stream through racthets hangs if first SYN was not acked
- Check "Last-Modified" instead "If-Modified-Since" for addressbook reponse
- Trim behind ECIESx25519 tags
- Few bugs with Android main activity
- QT visual and layout issues
## [2.33.0] - 2020-08-24
### Added
- Shared transient addresses

View File

@ -1,4 +1,4 @@
Copyright (c) 2013-2015, The PurpleI2P Project
Copyright (c) 2013-2020, The PurpleI2P Project
All rights reserved.

View File

@ -4,17 +4,15 @@ ARLIB := libi2pd.a
SHLIB_CLIENT := libi2pdclient.so
ARLIB_CLIENT := libi2pdclient.a
I2PD := i2pd
GREP := grep
DEPS := obj/make.dep
LIB_SRC_DIR := libi2pd
LIB_CLIENT_SRC_DIR := libi2pd_client
DAEMON_SRC_DIR := daemon
# import source files lists
include filelist.mk
USE_AESNI := yes
USE_AVX := yes
USE_STATIC := no
USE_MESHNET := no
USE_UPNP := no
@ -51,7 +49,12 @@ ifeq ($(USE_MESHNET),yes)
NEEDED_CXXFLAGS += -DMESHNET
endif
NEEDED_CXXFLAGS += -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR)
NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR)
LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d)
all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD)
@ -72,32 +75,29 @@ api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
## custom FLAGS to work at build-time.
deps: mk_obj_dir
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) -MM *.cpp > $(DEPS)
@sed -i -e '/\.o:/ s/^/obj\//' $(DEPS)
obj/%.o: %.cpp
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(CPU_FLAGS) -c -o $@ $<
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -c -o $@ $<
# '-' is 'ignore if missing' on first run
-include $(DEPS)
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT)
$(CXX) -o $@ $^ $(LDFLAGS) $(LDLIBS)
$(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS)
$(SHLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
$(SHLIB): $(LIB_OBJS)
ifneq ($(USE_STATIC),yes)
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
endif
$(SHLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
$(SHLIB_CLIENT): $(LIB_CLIENT_OBJS)
ifneq ($(USE_STATIC),yes)
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB)
endif
$(ARLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
$(ARLIB): $(LIB_OBJS)
$(AR) -r $@ $^
$(ARLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
$(ARLIB_CLIENT): $(LIB_CLIENT_OBJS)
$(AR) -r $@ $^
clean:
@ -123,7 +123,6 @@ doxygen:
.PHONY: all
.PHONY: clean
.PHONY: deps
.PHONY: doxygen
.PHONY: dist
.PHONY: last-dist
@ -131,3 +130,4 @@ doxygen:
.PHONY: api_client
.PHONY: mk_obj_dir
.PHONY: install
.PHONY: strip

View File

@ -35,10 +35,7 @@ endif
# Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2
# Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic
ifeq ($(USE_AESNI),yes)
CXXFLAGS += -maes
endif
ifeq ($(USE_AVX),1)
CXXFLAGS += -mavx
CXXFLAGS += -D__AES__ -maes
endif
install: all

View File

@ -1,5 +1,5 @@
# set defaults instead redefine
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation -Wno-psabi
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-psabi
LDFLAGS ?= ${LD_DEBUG}
## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time
@ -33,7 +33,7 @@ ifeq ($(USE_STATIC),yes)
# NOTE: on glibc you will get this warning:
# Using 'getaddrinfo' in statically linked applications requires at runtime
# the shared libraries from the glibc version used for linking
LIBDIR := /usr/lib
LIBDIR := /usr/lib/$(SYS)
LDLIBS += $(LIBDIR)/libboost_system.a
LDLIBS += $(LIBDIR)/libboost_date_time.a
LDLIBS += $(LIBDIR)/libboost_filesystem.a
@ -41,37 +41,24 @@ ifeq ($(USE_STATIC),yes)
LDLIBS += $(LIBDIR)/libssl.a
LDLIBS += $(LIBDIR)/libcrypto.a
LDLIBS += $(LIBDIR)/libz.a
LDLIBS += -lpthread -static-libstdc++ -static-libgcc -lrt -ldl
USE_AESNI := no
ifeq ($(USE_UPNP),yes)
LDLIBS += $(LIBDIR)/libminiupnpc.a
endif
LDLIBS += -lpthread -ldl
else
LDLIBS += -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
endif
# UPNP Support (miniupnpc 1.5 and higher)
ifeq ($(USE_UPNP),yes)
CXXFLAGS += -DUSE_UPNP
ifeq ($(USE_STATIC),yes)
LDLIBS += $(LIBDIR)/libminiupnpc.a
else
LDLIBS += -lminiupnpc
endif
endif
ifeq ($(USE_AESNI),yes)
#check if AES-NI is supported by CPU
ifneq ($(shell $(GREP) -c aes /proc/cpuinfo),0)
machine := $(shell uname -m)
ifeq ($(machine), aarch64)
CXXFLAGS += -DARM64AES
else
CPU_FLAGS += -maes
endif
endif
# UPNP Support (miniupnpc 1.5 and higher)
ifeq ($(USE_UPNP),yes)
NEEDED_CXXFLAGS += -DUSE_UPNP
endif
ifeq ($(USE_AVX),yes)
#check if AVX supported by CPU
ifneq ($(shell $(GREP) -c avx /proc/cpuinfo),0)
CPU_FLAGS += -mavx
ifeq ($(USE_AESNI),yes)
ifeq (, $(findstring arm, $(SYS))$(findstring aarch64, $(SYS))) # no arm and aarch64 in dumpmachine
NEEDED_CXXFLAGS += -D__AES__ -maes
endif
endif

View File

@ -1,9 +1,11 @@
USE_WIN32_APP=yes
CXX = g++
# Build application with GUI (tray, main window)
USE_WIN32_APP := yes
WINDRES = windres
CXXFLAGS := ${CXX_DEBUG} -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN
INCFLAGS = -Idaemon -I.
LDFLAGS := ${LD_DEBUG} -Wl,-Bstatic -static-libgcc -static-libstdc++
CXXFLAGS := $(CXX_DEBUG) -D_MT -DWIN32_LEAN_AND_MEAN -fPIC -msse
INCFLAGS = -I$(DAEMON_SRC_DIR) -IWin32
LDFLAGS := ${LD_DEBUG} -Wl,-Bstatic -static-libgcc
# detect proper flag for c++11 support by compilers
CXXVER := $(shell $(CXX) -dumpversion)
@ -38,29 +40,21 @@ LDLIBS += \
-liphlpapi \
-lole32 \
-luuid \
-lstdc++ \
-lpthread
ifeq ($(USE_WIN32_APP), yes)
CXXFLAGS += -DWIN32_APP
NEEDED_CXXFLAGS += -DWIN32_APP
LDFLAGS += -mwindows
DAEMON_RC += Win32/Resource.rc
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
endif
ifeq ($(USE_WINXP_FLAGS), yes)
CXXFLAGS += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
NEEDED_CXXFLAGS += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
endif
# don't change following line to ifeq ($(USE_AESNI),yes) !!!
ifeq ($(USE_AESNI),1)
CPU_FLAGS += -maes
else
CPU_FLAGS += -msse
endif
ifeq ($(USE_AVX),1)
CPU_FLAGS += -mavx
ifeq ($(USE_AESNI),yes)
NEEDED_CXXFLAGS += -D__AES__ -maes
endif
ifeq ($(USE_ASLR),yes)

View File

@ -22,12 +22,8 @@ ifeq ($(USE_UPNP),yes)
endif
endif
ifeq ($(USE_AESNI),1)
CXXFLAGS += -maes
ifeq ($(USE_AESNI),yes)
CXXFLAGS += -D__AES__ -maes
else
CXXFLAGS += -msse
endif
ifeq ($(USE_AVX),1)
CXXFLAGS += -mavx
endif

View File

@ -14,10 +14,10 @@
#include "Log.h"
#ifdef _WIN32
#include "Win32/Win32Service.h"
#include "Win32Service.h"
#ifdef WIN32_APP
#include <windows.h>
#include "Win32/Win32App.h"
#include "Win32App.h"
#endif
namespace i2p

View File

@ -25,7 +25,7 @@ BEGIN
VALUE "FileDescription", "C++ I2P daemon"
VALUE "FileVersion", I2PD_VERSION
VALUE "InternalName", CODENAME
VALUE "LegalCopyright", "Copyright (C) 2013-2017, The PurpleI2P Project"
VALUE "LegalCopyright", "Copyright (C) 2013-2020, The PurpleI2P Project"
VALUE "OriginalFilename", "i2pd"
VALUE "ProductName", "Purple I2P"
VALUE "ProductVersion", I2P_VERSION

View File

@ -43,10 +43,7 @@ namespace i2p
{
namespace win32
{
static DWORD GracefulShutdownEndtime = 0;
typedef DWORD (* IPN)();
IPN GetTickCountLocal = (IPN)GetProcAddress (GetModuleHandle ("KERNEL32.dll"), "GetTickCount");
DWORD g_GracefulShutdownEndtime = 0;
static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem)
{
@ -167,9 +164,9 @@ namespace win32
s << "; ";
s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n";
s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ());
if (GracefulShutdownEndtime != 0)
if (g_GracefulShutdownEndtime != 0)
{
DWORD GracefulTimeLeft = (GracefulShutdownEndtime - GetTickCountLocal()) / 1000;
DWORD GracefulTimeLeft = (g_GracefulShutdownEndtime - GetTickCount()) / 1000;
s << "Graceful shutdown, time left: "; ShowUptime(s, GracefulTimeLeft);
}
else
@ -247,7 +244,7 @@ namespace win32
i2p::context.SetAcceptsTunnels (false);
SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second
GracefulShutdownEndtime = GetTickCountLocal() + 10*60*1000;
g_GracefulShutdownEndtime = GetTickCount() + 10*60*1000;
i2p::util::DaemonWin32::Instance ().isGraceful = true;
return 0;
}
@ -256,7 +253,7 @@ namespace win32
i2p::context.SetAcceptsTunnels (true);
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER);
GracefulShutdownEndtime = 0;
g_GracefulShutdownEndtime = 0;
i2p::util::DaemonWin32::Instance ().isGraceful = false;
return 0;
}
@ -343,7 +340,7 @@ namespace win32
{
case IDT_GRACEFUL_SHUTDOWN_TIMER:
{
GracefulShutdownEndtime = 0;
g_GracefulShutdownEndtime = 0;
PostMessage (hWnd, WM_CLOSE, 0, 0); // exit
return 0;
}

View File

@ -15,6 +15,8 @@ namespace i2p
{
namespace win32
{
extern DWORD g_GracefulShutdownEndtime;
bool StartWin32App ();
void StopWin32App ();
int RunWin32App ();

View File

@ -15,10 +15,10 @@
android:allowBackup="true"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light.DarkActionBar"
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true"
>
android:theme="@android:style/Theme.Holo.Light.DarkActionBar"
android:usesCleartextTraffic="true">
<activity android:name=".WebConsoleActivity"></activity>
<receiver android:name=".NetworkStateChangeReceiver">
<intent-filter>
@ -31,10 +31,10 @@
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".I2PDActivity"
android:label="@string/app_name" />
@ -52,4 +52,5 @@
android:value="org.purplei2p.i2pd.I2PDPermsAskerActivity" />
</activity>
</application>
</manifest>

View File

@ -30,8 +30,8 @@ android {
applicationId "org.purplei2p.i2pd"
targetSdkVersion 29
minSdkVersion 14
versionCode 2330
versionName "2.33.0"
versionCode 2350
versionName "2.35.0"
setProperty("archivesBaseName", archivesBaseName + "-" + versionName)
ndk {
abiFilters 'armeabi-v7a'

View File

@ -1,12 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/layout_prompt"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context=".I2PDActivity">
tools:context=".WebConsoleActivity">
<WebView
android:id="@+id/webview1"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>

View File

@ -1,181 +0,0 @@
package org.purplei2p.i2pd;
import java.util.HashSet;
import java.util.Set;
import android.os.Environment;
import android.util.Log;
import org.purplei2p.i2pd.R;
public class DaemonSingleton {
private static final String TAG = "i2pd";
private static final DaemonSingleton instance = new DaemonSingleton();
public interface StateUpdateListener {
void daemonStateUpdate();
}
private final Set<StateUpdateListener> stateUpdateListeners = new HashSet<>();
public static DaemonSingleton getInstance() {
return instance;
}
public synchronized void addStateChangeListener(StateUpdateListener listener) {
stateUpdateListeners.add(listener);
}
public synchronized void removeStateChangeListener(StateUpdateListener listener) {
stateUpdateListeners.remove(listener);
}
private synchronized void setState(State newState) {
if (newState == null)
throw new NullPointerException();
State oldState = state;
if (oldState == null)
throw new NullPointerException();
if (oldState.equals(newState))
return;
state = newState;
fireStateUpdate1();
}
public synchronized void stopAcceptingTunnels() {
if (isStartedOkay()) {
setState(State.gracefulShutdownInProgress);
I2PD_JNI.stopAcceptingTunnels();
}
}
public synchronized void startAcceptingTunnels() {
if (isStartedOkay()) {
setState(State.startedOkay);
I2PD_JNI.startAcceptingTunnels();
}
}
public synchronized void reloadTunnelsConfigs() {
if (isStartedOkay()) {
I2PD_JNI.reloadTunnelsConfigs();
}
}
public synchronized int GetTransitTunnelsCount() {
return I2PD_JNI.GetTransitTunnelsCount();
}
private volatile boolean startedOkay;
public enum State {
uninitialized(R.string.uninitialized),
starting(R.string.starting),
jniLibraryLoaded(R.string.jniLibraryLoaded),
startedOkay(R.string.startedOkay),
startFailed(R.string.startFailed),
gracefulShutdownInProgress(R.string.gracefulShutdownInProgress),
stopped(R.string.stopped);
State(int statusStringResourceId) {
this.statusStringResourceId = statusStringResourceId;
}
private final int statusStringResourceId;
public int getStatusStringResourceId() {
return statusStringResourceId;
}
};
private volatile State state = State.uninitialized;
public State getState() {
return state;
}
{
setState(State.starting);
new Thread(new Runnable() {
@Override
public void run() {
try {
I2PD_JNI.loadLibraries();
setState(State.jniLibraryLoaded);
} catch (Throwable tr) {
lastThrowable = tr;
setState(State.startFailed);
return;
}
try {
synchronized (DaemonSingleton.this) {
I2PD_JNI.setDataDir(Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd");
daemonStartResult = I2PD_JNI.startDaemon();
if ("ok".equals(daemonStartResult)) {
setState(State.startedOkay);
setStartedOkay(true);
} else
setState(State.startFailed);
}
} catch (Throwable tr) {
lastThrowable = tr;
setState(State.startFailed);
}
}
}, "i2pdDaemonStart").start();
}
private Throwable lastThrowable;
private String daemonStartResult = "N/A";
private void fireStateUpdate1() {
Log.i(TAG, "daemon state change: " + state);
for (StateUpdateListener listener : stateUpdateListeners) {
try {
listener.daemonStateUpdate();
} catch (Throwable tr) {
Log.e(TAG, "exception in listener ignored", tr);
}
}
}
public Throwable getLastThrowable() {
return lastThrowable;
}
public String getDaemonStartResult() {
return daemonStartResult;
}
private final Object startedOkayLock = new Object();
public boolean isStartedOkay() {
synchronized (startedOkayLock) {
return startedOkay;
}
}
private void setStartedOkay(boolean startedOkay) {
synchronized (startedOkayLock) {
this.startedOkay = startedOkay;
}
}
public synchronized void stopDaemon() {
if (isStartedOkay()) {
try {
I2PD_JNI.stopDaemon();
} catch(Throwable tr) {
Log.e(TAG, "", tr);
}
setStartedOkay(false);
setState(State.stopped);
}
}
}

View File

@ -0,0 +1,374 @@
package org.purplei2p.i2pd;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Set;
import android.annotation.TargetApi;
import android.content.res.AssetManager;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import androidx.annotation.RequiresApi;
public class DaemonWrapper {
private static final String TAG = "i2pd";
private final AssetManager assetManager;
private final ConnectivityManager connectivityManager;
private String i2pdpath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd/";
private boolean assetsCopied;
public interface StateUpdateListener {
void daemonStateUpdate(State oldValue, State newValue);
}
private final Set<StateUpdateListener> stateUpdateListeners = new HashSet<>();
public synchronized void addStateChangeListener(StateUpdateListener listener) {
stateUpdateListeners.add(listener);
}
public synchronized void removeStateChangeListener(StateUpdateListener listener) {
stateUpdateListeners.remove(listener);
}
private synchronized void setState(State newState) {
if (newState == null)
throw new NullPointerException();
State oldState = state;
if (oldState == null)
throw new NullPointerException();
if (oldState.equals(newState))
return;
state = newState;
fireStateUpdate1(oldState, newState);
}
public synchronized void stopAcceptingTunnels() {
if (isStartedOkay()) {
setState(State.gracefulShutdownInProgress);
I2PD_JNI.stopAcceptingTunnels();
}
}
public synchronized void startAcceptingTunnels() {
if (isStartedOkay()) {
setState(State.startedOkay);
I2PD_JNI.startAcceptingTunnels();
}
}
public synchronized void reloadTunnelsConfigs() {
if (isStartedOkay()) {
I2PD_JNI.reloadTunnelsConfigs();
}
}
public int getTransitTunnelsCount() {
return I2PD_JNI.GetTransitTunnelsCount();
}
public enum State {
uninitialized(R.string.uninitialized),
starting(R.string.starting),
jniLibraryLoaded(R.string.jniLibraryLoaded),
startedOkay(R.string.startedOkay),
startFailed(R.string.startFailed),
gracefulShutdownInProgress(R.string.gracefulShutdownInProgress),
stopped(R.string.stopped);
State(int statusStringResourceId) {
this.statusStringResourceId = statusStringResourceId;
}
private final int statusStringResourceId;
public int getStatusStringResourceId() {
return statusStringResourceId;
}
public boolean isStartedOkay() {
return equals(State.startedOkay) || equals(State.gracefulShutdownInProgress);
}
}
private volatile State state = State.uninitialized;
public State getState() {
return state;
}
public DaemonWrapper(AssetManager assetManager, ConnectivityManager connectivityManager){
this.assetManager = assetManager;
this.connectivityManager = connectivityManager;
setState(State.starting);
new Thread(() -> {
try {
processAssets();
I2PD_JNI.loadLibraries();
setState(State.jniLibraryLoaded);
registerNetworkCallback();
} catch (Throwable tr) {
lastThrowable = tr;
setState(State.startFailed);
return;
}
try {
synchronized (DaemonWrapper.this) {
I2PD_JNI.setDataDir(Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd");
daemonStartResult = I2PD_JNI.startDaemon();
if ("ok".equals(daemonStartResult)) {
setState(State.startedOkay);
} else
setState(State.startFailed);
}
} catch (Throwable tr) {
lastThrowable = tr;
setState(State.startFailed);
}
}, "i2pdDaemonStart").start();
}
private Throwable lastThrowable;
private String daemonStartResult = "N/A";
private void fireStateUpdate1(State oldValue, State newValue) {
Log.i(TAG, "daemon state change: " + state);
for (StateUpdateListener listener : stateUpdateListeners) {
try {
listener.daemonStateUpdate(oldValue, newValue);
} catch (Throwable tr) {
Log.e(TAG, "exception in listener ignored", tr);
}
}
}
public Throwable getLastThrowable() {
return lastThrowable;
}
public String getDaemonStartResult() {
return daemonStartResult;
}
public boolean isStartedOkay() {
return getState().isStartedOkay();
}
public synchronized void stopDaemon() {
if (isStartedOkay()) {
try {
I2PD_JNI.stopDaemon();
} catch(Throwable tr) {
Log.e(TAG, "", tr);
}
setState(State.stopped);
}
}
private void processAssets() {
if (!assetsCopied) {
try {
assetsCopied = true;
File holderFile = new File(i2pdpath, "assets.ready");
String versionName = BuildConfig.VERSION_NAME; // here will be app version, like 2.XX.XX
StringBuilder text = new StringBuilder();
if (holderFile.exists()) {
try { // if holder file exists, read assets version string
FileReader fileReader = new FileReader(holderFile);
try {
BufferedReader br = new BufferedReader(fileReader);
try {
String line;
while ((line = br.readLine()) != null) {
text.append(line);
}
}finally {
try {
br.close();
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
} finally {
try {
fileReader.close();
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
// if version differs from current app version or null, try to delete certificates folder
if (!text.toString().contains(versionName))
try {
boolean deleteResult = holderFile.delete();
if (!deleteResult)
Log.e(TAG, "holderFile.delete() returned " + deleteResult + ", absolute path='" + holderFile.getAbsolutePath() + "'");
File certPath = new File(i2pdpath, "certificates");
deleteRecursive(certPath);
}
catch (Throwable tr) {
Log.e(TAG, "", tr);
}
// copy assets. If processed file exists, it won't be overwritten
copyAsset("addressbook");
copyAsset("certificates");
copyAsset("tunnels.d");
copyAsset("i2pd.conf");
copyAsset("subscriptions.txt");
copyAsset("tunnels.conf");
// update holder file about successful copying
FileWriter writer = new FileWriter(holderFile);
try {
writer.append(versionName);
} finally {
try {
writer.close();
} catch (IOException e) {
Log.e(TAG,"on writer close", e);
}
}
}
catch (Throwable tr)
{
Log.e(TAG,"on assets copying", tr);
}
}
}
/**
* Copy the asset at the specified path to this app's data directory. If the
* asset is a directory, its contents are also copied.
*
* @param path
* Path to asset, relative to app's assets directory.
*/
private void copyAsset(String path) {
// If we have a directory, we make it and recurse. If a file, we copy its
// contents.
try {
String[] contents = assetManager.list(path);
// The documentation suggests that list throws an IOException, but doesn't
// say under what conditions. It'd be nice if it did so when the path was
// to a file. That doesn't appear to be the case. If the returned array is
// null or has 0 length, we assume the path is to a file. This means empty
// directories will get turned into files.
if (contents == null || contents.length == 0) {
copyFileAsset(path);
return;
}
// Make the directory.
File dir = new File(i2pdpath, path);
boolean result = dir.mkdirs();
Log.d(TAG, "dir.mkdirs() returned " + result);
// Recurse on the contents.
for (String entry : contents) {
copyAsset(path + '/' + entry);
}
} catch (IOException e) {
Log.e(TAG, "ex ignored for path='" + path + "'", e);
}
}
/**
* Copy the asset file specified by path to app's data directory. Assumes
* parent directories have already been created.
*
* @param path
* Path to asset, relative to app's assets directory.
*/
private void copyFileAsset(String path) {
File file = new File(i2pdpath, path);
if (!file.exists()) {
try {
try (InputStream in = assetManager.open(path)) {
try (OutputStream out = new FileOutputStream(file)) {
byte[] buffer = new byte[1024];
int read = in.read(buffer);
while (read != -1) {
out.write(buffer, 0, read);
read = in.read(buffer);
}
}
}
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
}
private void deleteRecursive(File fileOrDirectory) {
if (fileOrDirectory.isDirectory()) {
File[] files = fileOrDirectory.listFiles();
if (files != null) {
for (File child : files) {
deleteRecursive(child);
}
}
}
boolean deleteResult = fileOrDirectory.delete();
if (!deleteResult)
Log.e(TAG, "fileOrDirectory.delete() returned " + deleteResult + ", absolute path='" + fileOrDirectory.getAbsolutePath() + "'");
}
private void registerNetworkCallback(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) registerNetworkCallback0();
}
@TargetApi(Build.VERSION_CODES.M)
private void registerNetworkCallback0() {
NetworkRequest request = new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
.build();
NetworkStateCallbackImpl networkCallback = new NetworkStateCallbackImpl();
connectivityManager.registerNetworkCallback(request, networkCallback);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private static final class NetworkStateCallbackImpl extends ConnectivityManager.NetworkCallback {
@Override
public void onAvailable(Network network) {
super.onAvailable(network);
I2PD_JNI.onNetworkStateChanged(true);
Log.i(TAG, "NetworkCallback.onAvailable");
}
@Override
public void onLost(Network network) {
super.onLost(network);
I2PD_JNI.onNetworkStateChanged(false);
Log.i(TAG, " NetworkCallback.onLost");
}
}
}

View File

@ -19,13 +19,24 @@ public class ForegroundService extends Service {
private volatile boolean shown;
private final DaemonSingleton.StateUpdateListener daemonStateUpdatedListener =
new DaemonSingleton.StateUpdateListener() {
private static ForegroundService instance;
private static volatile DaemonWrapper daemon;
private static final Object initDeinitLock = new Object();
private final DaemonWrapper.StateUpdateListener daemonStateUpdatedListener =
new DaemonWrapper.StateUpdateListener() {
@Override
public void daemonStateUpdate() {
public void daemonStateUpdate(DaemonWrapper.State oldValue, DaemonWrapper.State newValue) {
updateNotificationText();
}
};
private void updateNotificationText() {
try {
synchronized (ForegroundService.this) {
synchronized (initDeinitLock) {
if (shown) cancelNotification();
showNotification();
}
@ -33,14 +44,13 @@ public class ForegroundService extends Service {
Log.e(TAG,"error ignored",tr);
}
}
};
private NotificationManager notificationManager;
// Unique Identification Number for the Notification.
// We use it on Notification start, and to cancel it.
private int NOTIFICATION = 1;
private static final int NOTIFICATION = 1;
/**
* Class for clients to access. Because we know this service always
@ -53,16 +63,27 @@ public class ForegroundService extends Service {
}
}
public static void init(DaemonWrapper daemon) {
ForegroundService.daemon = daemon;
initCheck();
}
private static void initCheck() {
synchronized (initDeinitLock) {
if (instance != null && daemon != null) instance.setListener();
}
}
@Override
public void onCreate() {
notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
synchronized (this) {
DaemonSingleton.getInstance().addStateChangeListener(daemonStateUpdatedListener);
if (!shown) daemonStateUpdatedListener.daemonStateUpdate();
instance = this;
initCheck();
}
// Tell the user we started.
// Toast.makeText(this, R.string.i2pd_service_started, Toast.LENGTH_SHORT).show();
private void setListener() {
daemon.addStateChangeListener(daemonStateUpdatedListener);
updateNotificationText();
}
@Override
@ -73,11 +94,24 @@ public class ForegroundService extends Service {
@Override
public void onDestroy() {
DaemonSingleton.getInstance().removeStateChangeListener(daemonStateUpdatedListener);
cancelNotification();
deinitCheck();
instance=null;
}
private synchronized void cancelNotification() {
public static void deinit() {
deinitCheck();
}
private static void deinitCheck() {
synchronized (initDeinitLock) {
if (daemon != null && instance != null)
daemon.removeStateChangeListener(instance.daemonStateUpdatedListener);
}
}
private void cancelNotification() {
synchronized (initDeinitLock) {
// Cancel the persistent notification.
notificationManager.cancel(NOTIFICATION);
@ -87,6 +121,7 @@ public class ForegroundService extends Service {
//Toast.makeText(this, R.string.i2pd_service_stopped, Toast.LENGTH_SHORT).show();
shown = false;
}
}
@Override
public IBinder onBind(Intent intent) {
@ -100,9 +135,11 @@ public class ForegroundService extends Service {
/**
* Show a notification while this service is running.
*/
private synchronized void showNotification() {
private void showNotification() {
synchronized (initDeinitLock) {
if (daemon != null) {
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(DaemonSingleton.getInstance().getState().getStatusStringResourceId());
CharSequence text = getText(daemon.getState().getStatusStringResourceId());
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
@ -116,8 +153,10 @@ public class ForegroundService extends Service {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId)
.setOngoing(true)
.setSmallIcon(R.drawable.itoopie_notification_icon); // the status icon
if(Build.VERSION.SDK_INT >= 16) builder = builder.setPriority(Notification.PRIORITY_DEFAULT);
if(Build.VERSION.SDK_INT >= 21) builder = builder.setCategory(Notification.CATEGORY_SERVICE);
if (Build.VERSION.SDK_INT >= 16)
builder = builder.setPriority(Notification.PRIORITY_DEFAULT);
if (Build.VERSION.SDK_INT >= 21)
builder = builder.setCategory(Notification.CATEGORY_SERVICE);
Notification notification = builder
.setTicker(text) // the status text
.setWhen(System.currentTimeMillis()) // the time stamp
@ -131,6 +170,8 @@ public class ForegroundService extends Service {
startForeground(NOTIFICATION, notification);
shown = true;
}
}
}
@RequiresApi(Build.VERSION_CODES.O)
private synchronized String createNotificationChannel() {
@ -144,6 +185,4 @@ public class ForegroundService extends Service {
else Log.e(TAG, "error: NOTIFICATION_SERVICE is null");
return channelId;
}
private static final DaemonSingleton daemon = DaemonSingleton.getInstance();
}

View File

@ -1,13 +1,5 @@
package org.purplei2p.i2pd;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Timer;
@ -15,7 +7,6 @@ import java.util.TimerTask;
import android.Manifest;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
@ -24,16 +15,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.res.AssetManager;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.Uri;
import android.os.Bundle;
import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import android.os.PowerManager;
import android.preference.PreferenceManager;
@ -46,7 +32,6 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
@ -60,25 +45,24 @@ import android.webkit.WebViewClient;
import static android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS;
public class I2PDActivity extends Activity {
private WebView webView;
private static final String TAG = "i2pdActvt";
private static final int MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000;
public static final String PACKAGE_URI_SCHEME = "package:";
private TextView textView;
private boolean assetsCopied;
private NetworkStateCallback networkCallback;
private String i2pdpath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd/";
//private ConfigParser parser = new ConfigParser(i2pdpath); // TODO:
//private ConfigParser parser = new ConfigParser(i2pdpath); // TODO
private static final DaemonSingleton daemon = DaemonSingleton.getInstance();
private static volatile DaemonWrapper daemon;
private final DaemonSingleton.StateUpdateListener daemonStateUpdatedListener = new DaemonSingleton.StateUpdateListener() {
private final DaemonWrapper.StateUpdateListener daemonStateUpdatedListener = new DaemonWrapper.StateUpdateListener() {
@Override
public void daemonStateUpdate() {
processAssets();
public void daemonStateUpdate(DaemonWrapper.State oldValue, DaemonWrapper.State newValue) {
updateStatusText();
}
};
private void updateStatusText() {
runOnUiThread(() -> {
try {
if (textView == null)
@ -88,16 +72,16 @@ public class I2PDActivity extends Activity {
textView.setText(throwableToString(tr));
return;
}
DaemonSingleton.State state = daemon.getState();
String startResultStr = DaemonSingleton.State.startFailed.equals(state) ? String.format(": %s", daemon.getDaemonStartResult()) : "";
String graceStr = DaemonSingleton.State.gracefulShutdownInProgress.equals(state) ? String.format(": %s %s", formatGraceTimeRemaining(), getText(R.string.remaining)) : "";
DaemonWrapper.State state = daemon.getState();
String startResultStr = DaemonWrapper.State.startFailed.equals(state) ? String.format(": %s", daemon.getDaemonStartResult()) : "";
String graceStr = DaemonWrapper.State.gracefulShutdownInProgress.equals(state) ? String.format(": %s %s", formatGraceTimeRemaining(), getText(R.string.remaining)) : "";
textView.setText(String.format("%s%s%s", getText(state.getStatusStringResourceId()), startResultStr, graceStr));
} catch (Throwable tr) {
Log.e(TAG,"error ignored",tr);
}
});
}
};
private static volatile long graceStartedMillis;
private static final Object graceStartedMillis_LOCK = new Object();
private Menu optionsMenu;
@ -117,10 +101,16 @@ public class I2PDActivity extends Activity {
Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState);
if (daemon==null) {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
daemon = new DaemonWrapper(getAssets(), connectivityManager);
}
ForegroundService.init(daemon);
textView = new TextView(this);
setContentView(textView);
daemon.addStateChangeListener(daemonStateUpdatedListener);
daemonStateUpdatedListener.daemonStateUpdate();
daemonStateUpdatedListener.daemonStateUpdate(DaemonWrapper.State.uninitialized, daemon.getState());
// request permissions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
@ -145,15 +135,13 @@ public class I2PDActivity extends Activity {
openBatteryOptimizationDialogIfNeeded();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
registerNetworkCallback();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
textView = null;
ForegroundService.deinit();
daemon.removeStateChangeListener(daemonStateUpdatedListener);
//cancelGracefulStop0();
try {
@ -288,15 +276,8 @@ public class I2PDActivity extends Activity {
return true;
case R.id.action_start_webview:
setContentView(R.layout.webview);
this.webView = (WebView) findViewById(R.id.webview1);
this.webView.setWebViewClient(new WebViewClient());
WebSettings webSettings = this.webView.getSettings();
webSettings.setBuiltInZoomControls(true);
webSettings.setJavaScriptEnabled(false);
this.webView.loadUrl("http://127.0.0.1:7070"); // TODO: instead 7070 I2Pd....HttpPort
break;
startActivity(new Intent(getApplicationContext(), WebConsoleActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
@ -335,7 +316,7 @@ public class I2PDActivity extends Activity {
private static volatile Timer gracefulQuitTimer;
private void i2pdGracefulStop() {
if (daemon.getState() == DaemonSingleton.State.stopped) {
if (daemon.getState() == DaemonWrapper.State.stopped) {
Toast.makeText(this, R.string.already_stopped, Toast.LENGTH_SHORT).show();
return;
}
@ -384,9 +365,10 @@ public class I2PDActivity extends Activity {
if (gracefulQuitTimerOld != null)
gracefulQuitTimerOld.cancel();
if(daemon.GetTransitTunnelsCount() <= 0) { // no tunnels left
if(daemon.getTransitTunnelsCount() <= 0) { // no tunnels left
Log.d(TAG, "no transit tunnels left, stopping");
i2pdStop();
return;
}
final Timer gracefulQuitTimer = new Timer(true);
@ -402,7 +384,7 @@ public class I2PDActivity extends Activity {
final TimerTask tickerTask = new TimerTask() {
@Override
public void run() {
daemonStateUpdatedListener.daemonStateUpdate();
updateStatusText();
}
};
gracefulQuitTimer.scheduleAtFixedRate(tickerTask, 0/*start delay*/, 1000/*millis period*/);
@ -427,167 +409,6 @@ public class I2PDActivity extends Activity {
});
}
/**
* Copy the asset at the specified path to this app's data directory. If the
* asset is a directory, its contents are also copied.
*
* @param path
* Path to asset, relative to app's assets directory.
*/
private void copyAsset(String path) {
AssetManager manager = getAssets();
// If we have a directory, we make it and recurse. If a file, we copy its
// contents.
try {
String[] contents = manager.list(path);
// The documentation suggests that list throws an IOException, but doesn't
// say under what conditions. It'd be nice if it did so when the path was
// to a file. That doesn't appear to be the case. If the returned array is
// null or has 0 length, we assume the path is to a file. This means empty
// directories will get turned into files.
if (contents == null || contents.length == 0) {
copyFileAsset(path);
return;
}
// Make the directory.
File dir = new File(i2pdpath, path);
boolean result = dir.mkdirs();
Log.d(TAG, "dir.mkdirs() returned " + result);
// Recurse on the contents.
for (String entry : contents) {
copyAsset(path + '/' + entry);
}
} catch (IOException e) {
Log.e(TAG, "ex ignored for path='" + path + "'", e);
}
}
/**
* Copy the asset file specified by path to app's data directory. Assumes
* parent directories have already been created.
*
* @param path
* Path to asset, relative to app's assets directory.
*/
private void copyFileAsset(String path) {
File file = new File(i2pdpath, path);
if (!file.exists()) {
try {
try (InputStream in = getAssets().open(path)) {
try (OutputStream out = new FileOutputStream(file)) {
byte[] buffer = new byte[1024];
int read = in.read(buffer);
while (read != -1) {
out.write(buffer, 0, read);
read = in.read(buffer);
}
}
}
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
}
private void deleteRecursive(File fileOrDirectory) {
if (fileOrDirectory.isDirectory()) {
File[] files = fileOrDirectory.listFiles();
if (files != null) {
for (File child : files) {
deleteRecursive(child);
}
}
}
boolean deleteResult = fileOrDirectory.delete();
if (!deleteResult)
Log.e(TAG, "fileOrDirectory.delete() returned " + deleteResult + ", absolute path='" + fileOrDirectory.getAbsolutePath() + "'");
}
private void processAssets() {
if (!assetsCopied) {
try {
assetsCopied = true; // prevent from running on every state update
File holderFile = new File(i2pdpath, "assets.ready");
String versionName = BuildConfig.VERSION_NAME; // here will be app version, like 2.XX.XX
StringBuilder text = new StringBuilder();
if (holderFile.exists()) {
try { // if holder file exists, read assets version string
FileReader fileReader = new FileReader(holderFile);
try {
BufferedReader br = new BufferedReader(fileReader);
try {
String line;
while ((line = br.readLine()) != null) {
text.append(line);
}
}finally {
try {
br.close();
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
} finally {
try {
fileReader.close();
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
// if version differs from current app version or null, try to delete certificates folder
if (!text.toString().contains(versionName))
try {
boolean deleteResult = holderFile.delete();
if (!deleteResult)
Log.e(TAG, "holderFile.delete() returned " + deleteResult + ", absolute path='" + holderFile.getAbsolutePath() + "'");
File certPath = new File(i2pdpath, "certificates");
deleteRecursive(certPath);
}
catch (Throwable tr) {
Log.e(TAG, "", tr);
}
// copy assets. If processed file exists, it won't be overwritten
copyAsset("addressbook");
copyAsset("certificates");
copyAsset("tunnels.d");
copyAsset("i2pd.conf");
copyAsset("subscriptions.txt");
copyAsset("tunnels.conf");
// update holder file about successful copying
FileWriter writer = new FileWriter(holderFile);
try {
writer.append(versionName);
} finally {
try {
writer.close();
} catch (IOException e) {
Log.e(TAG,"on writer close", e);
}
}
}
catch (Throwable tr)
{
Log.e(TAG,"on assets copying", tr);
}
}
}
@SuppressLint("BatteryLife")
private void openBatteryOptimizationDialogIfNeeded() {
boolean questionEnabled = getPreferences().getBoolean(getBatteryOptimizationPreferenceKey(), true);
@ -642,33 +463,6 @@ public class I2PDActivity extends Activity {
return "show_battery_optimization" + (device == null ? "" : device);
}
@TargetApi(Build.VERSION_CODES.M)
private void registerNetworkCallback() {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkRequest request = new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
.build();
networkCallback = new NetworkStateCallback();
connectivityManager.registerNetworkCallback(request, networkCallback);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private final class NetworkStateCallback extends ConnectivityManager.NetworkCallback {
@Override
public void onAvailable(Network network) {
super.onAvailable(network);
I2PD_JNI.onNetworkStateChanged(true);
Log.i(TAG, "NetworkCallback.onAvailable");
}
@Override
public void onLost(Network network) {
super.onLost(network);
I2PD_JNI.onNetworkStateChanged(false);
Log.i(TAG, " NetworkCallback.onLost");
}
}
private void quit() {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

View File

@ -0,0 +1,39 @@
package org.purplei2p.i2pd;
import android.app.Activity;
import android.os.Bundle;
import android.view.MenuItem;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import java.util.Objects;
public class WebConsoleActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web_console);
Objects.requireNonNull(getActionBar()).setDisplayHomeAsUpEnabled(true);
final WebView webView = findViewById(R.id.webview1);
webView.setWebViewClient(new WebViewClient());
final WebSettings webSettings = webView.getSettings();
webSettings.setBuiltInZoomControls(true);
webSettings.setJavaScriptEnabled(false);
webView.loadUrl("http://127.0.0.1:7070"); // TODO: instead 7070 I2Pd....HttpPort
}
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id==android.R.id.home) {
finish();
return true;
}
return false;
}
}

View File

@ -1,4 +1,4 @@
version: 2.33.0.{build}
version: 2.35.0.{build}
pull_requests:
do_not_increment_build_number: true
branches:

View File

@ -11,9 +11,8 @@ if(WIN32 OR MSVC OR MSYS OR MINGW)
message(SEND_ERROR "cmake build for windows is not supported. Please use MSYS2 with makefiles in project root.")
endif()
# configurale options
option(WITH_AESNI "Use AES-NI instructions set" OFF)
option(WITH_AVX "Use AVX instructions" OFF)
# configurable options
option(WITH_AESNI "Use AES-NI instructions set" ON)
option(WITH_HARDENING "Use hardening compiler flags" OFF)
option(WITH_LIBRARY "Build library" ON)
option(WITH_BINARY "Build binary" ON)
@ -189,13 +188,11 @@ if(UNIX)
endif()
endif()
if(WITH_AESNI)
# Note: AES-NI and AVX is available on x86-based CPU's.
# Here also ARM64 implementation, but currently we don't support it.
if(WITH_AESNI AND (ARCHITECTURE MATCHES "x86_64" OR ARCHITECTURE MATCHES "i386"))
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes")
add_definitions(-DAESNI)
endif()
if(WITH_AVX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx")
add_definitions(-D__AES__)
endif()
if(WITH_ADDRSANITIZER)
@ -309,7 +306,6 @@ message(STATUS "Architecture : ${ARCHITECTURE}")
message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}")
message(STATUS "Options:")
message(STATUS " AESNI : ${WITH_AESNI}")
message(STATUS " AVX : ${WITH_AVX}")
message(STATUS " HARDENING : ${WITH_HARDENING}")
message(STATUS " LIBRARY : ${WITH_LIBRARY}")
message(STATUS " BINARY : ${WITH_BINARY}")

View File

@ -2,7 +2,7 @@
setlocal enableextensions enabledelayedexpansion
title Building i2pd
REM Copyright (c) 2013-2017, The PurpleI2P Project
REM Copyright (c) 2013-2020, The PurpleI2P Project
REM This file is part of Purple i2pd project and licensed under BSD3
REM See full license text in LICENSE file at top of project tree
@ -23,17 +23,17 @@ set "xSH=%WD%bash -lc"
set "FILELIST=i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates contrib/tunnels.d"
REM detecting number of processors and subtract 1.
set /a threads=%NUMBER_OF_PROCESSORS%-1
REM detecting number of processors
set /a threads=%NUMBER_OF_PROCESSORS%
REM we must work in root of repo
cd ..
REM deleting old log files
del /S build_*.log >> nul
del /S build_*.log >> nul 2>&1
echo Receiving latest commit and cleaning up...
%xSH% "git pull && make clean" > build/build_git.log 2>&1
%xSH% "git checkout contrib/* && git pull && make clean" > build\build.log 2>&1
echo.
REM set to variable current commit hash
@ -43,16 +43,17 @@ FOR /F "usebackq" %%a IN (`%xSH% 'git describe --tags'`) DO (
%xSH% "echo To use configs and certificates, move all files and certificates folder from contrib directory here. > README.txt" >> nul
REM converting configuration files to DOS format (usable in default notepad)
%xSH% "unix2dos contrib/i2pd.conf contrib/tunnels.conf contrib/tunnels.d/*" >> build\build.log 2>&1
REM starting building
set MSYSTEM=MINGW32
set bitness=32
call :BUILDING
echo.
set MSYSTEM=MINGW64
set bitness=64
call :BUILDING
echo.
REM building for WinXP
set "WD=C:\msys64-xp\usr\bin\"
@ -62,7 +63,10 @@ set "xSH=%WD%bash -lc"
call :BUILDING_XP
echo.
del README.txt >> nul
REM compile installer
C:\PROGRA~2\INNOSE~1\ISCC.exe /dI2Pd_ver="%tag%" build\win_installer.iss >> build\build.log 2>&1
del README.txt i2pd_x32.exe i2pd_x64.exe i2pd_xp.exe >> nul
echo Build complete...
pause
@ -70,20 +74,13 @@ exit /b 0
:BUILDING
%xSH% "make clean" >> nul
echo Building i2pd %tag% for win%bitness%:
echo Build AVX+AESNI...
%xSH% "make DEBUG=no USE_UPNP=yes USE_AVX=1 USE_AESNI=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_avx_aesni.zip %FILELIST% && make clean" > build/build_win%bitness%_avx_aesni_%tag%.log 2>&1
echo Build AVX...
%xSH% "make DEBUG=no USE_UPNP=yes USE_AVX=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_avx.zip %FILELIST% && make clean" > build/build_win%bitness%_avx_%tag%.log 2>&1
echo Build AESNI...
%xSH% "make DEBUG=no USE_UPNP=yes USE_AESNI=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_aesni.zip %FILELIST% && make clean" > build/build_win%bitness%_aesni_%tag%.log 2>&1
echo Build without extensions...
%xSH% "make DEBUG=no USE_UPNP=yes -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw.zip %FILELIST% && make clean" > build/build_win%bitness%_%tag%.log 2>&1
echo Building i2pd %tag% for win%bitness%
%xSH% "make DEBUG=no USE_UPNP=yes -j%threads% && cp i2pd.exe i2pd_x%bitness%.exe && zip -r9 build/i2pd_%tag%_win%bitness%_mingw.zip %FILELIST% && make clean" > build\build_win%bitness%_%tag%.log 2>&1
goto EOF
:BUILDING_XP
%xSH% "make clean" >> nul
echo Building i2pd %tag% for winxp...
%xSH% "make DEBUG=no USE_UPNP=yes USE_WINXP_FLAGS=yes -j%threads% && zip -r9 build/i2pd_%tag%_winxp_mingw.zip %FILELIST% && make clean" > build/build_winxp_%tag%.log 2>&1
echo Building i2pd %tag% for winxp
%xSH% "make DEBUG=no USE_UPNP=yes USE_WINXP_FLAGS=yes -j%threads% && cp i2pd.exe i2pd_xp.exe && zip -r9 build/i2pd_%tag%_winxp_mingw.zip %FILELIST% && make clean" > build\build_winxp_%tag%.log 2>&1
:EOF

View File

@ -1,6 +1,8 @@
#define I2Pd_AppName "i2pd"
#define I2Pd_ver "2.33.0"
#define I2Pd_Publisher "PurpleI2P"
; Get application version from compiled binary
; Disabled to use definition from command line
;#define I2Pd_ver GetFileVersionString(AddBackslash(SourcePath) + "..\i2pd_x64.exe")
[Setup]
AppName={#I2Pd_AppName}
@ -10,9 +12,9 @@ DefaultDirName={pf}\I2Pd
DefaultGroupName=I2Pd
UninstallDisplayIcon={app}\I2Pd.exe
OutputDir=.
LicenseFile=../LICENSE
LicenseFile=..\LICENSE
OutputBaseFilename=setup_{#I2Pd_AppName}_v{#I2Pd_ver}
SetupIconFile=mask.ico
SetupIconFile=..\Win32\mask.ico
InternalCompressLevel=ultra64
Compression=lzma/ultra64
SolidCompression=true
@ -23,10 +25,12 @@ AppID={{621A23E0-3CF4-4BD6-97BC-4835EA5206A2}
AppPublisherURL=http://i2pd.website/
AppSupportURL=https://github.com/PurpleI2P/i2pd/issues
AppUpdatesURL=https://github.com/PurpleI2P/i2pd/releases
CloseApplications=yes
[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: ..\i2pd_x32.exe; DestDir: {app}; DestName: i2pd.exe; Flags: ignoreversion; Check: not IsWin64; MinVersion: 6.0
Source: ..\i2pd_x64.exe; DestDir: {app}; DestName: i2pd.exe; Flags: ignoreversion; Check: IsWin64; MinVersion: 6.0
Source: ..\i2pd_xp.exe; DestDir: {app}; DestName: i2pd.exe; Flags: ignoreversion; Check: IsWin64; OnlyBelowVersion: 6.0
Source: ..\README.md; DestDir: {app}; DestName: Readme.txt; Flags: onlyifdoesntexist
Source: ..\contrib\i2pd.conf; DestDir: {userappdata}\i2pd; Flags: onlyifdoesntexist
Source: ..\contrib\subscriptions.txt; DestDir: {userappdata}\i2pd; Flags: onlyifdoesntexist

View File

@ -1,6 +1,6 @@
#!/bin/bash
# Copyright (c) 2013-2017, The PurpleI2P Project
# Copyright (c) 2013-2020, The PurpleI2P Project
#
# This file is part of Purple i2pd project and licensed under BSD3
#

View File

@ -1,6 +1,6 @@
#!/bin/sh
# Copyright (c) 2013-2019, The PurpleI2P Project
# Copyright (c) 2013-2020, The PurpleI2P Project
#
# This file is part of Purple i2pd project and licensed under BSD3
#

View File

@ -1,32 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFeTCCA2GgAwIBAgIEZZozujANBgkqhkiG9w0BAQ0FADBtMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEWMBQGA1UEAwwNbWVlaEBtYWlsLmky
cDAeFw0xNDA2MjgyMjQ5MDlaFw0yNDA2MjcyMjQ5MDlaMG0xCzAJBgNVBAYTAlhY
MQswCQYDVQQIEwJYWDELMAkGA1UEBxMCWFgxHjAcBgNVBAoTFUkyUCBBbm9ueW1v
dXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRYwFAYDVQQDDA1tZWVoQG1haWwuaTJw
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnVnmPE4uUvCky0yCnnVH
cJEDqzwDPupx0zr0YDlhZk5VOPPecx5haayJ/V6nXPc1aVVWn+CHfedcF2aBgN4K
5aBueS/l6l5WHcv02DofAqlTmyAws3oQeR1qoTuW24cKRtLR7h5bxv63f6bgp6e+
RihFNez6UxErnRPuJOJEO2Im6EgVp6fz7tQ7R35zxAUeES2YILPySvzy2vYm/EEG
jXX7Ap2A5svVo90xCMOeUZ/55vLsjyIshN+tV87U4xwvAkUmwsmWVHm3BQpHkI6z
zMJie6epB8Bqm0GYm0EcElJH4OCxGTvDLoghpswbuUO7iy3JSfoL7ZCnoiQdK9K4
yVVChj8lG+r7KaTowK96iZep+sZefjOt5VFGuW2Fi/WBv3ldiLlJAo/ZfrUM4+vG
fyNBXbl6bX87uTCGOT1p3dazo+zJMsAZ+Y93DlM/mDEWFa1kKNrs74syzaWEqF4L
KQE6VoYn80OOzafSigTVQgSwUtQtB0XGhMzJhyxU2XHWe1LFIy7Pta0B+lDiZj7c
I8nXxYjsDfEu/Elj/Ra9N6bH0awmgB5JDa+Tbir+oEM5SyDfpSaCGuatdGxjweGI
kVmFU0SqCZV/8TXbIu6MUVzTZMZVT94edifFSRad4fqw7eZbSXlPu++3d1/btn6h
ibM04nkv0mm+FxCKB/wdAkECAwEAAaMhMB8wHQYDVR0OBBYEFO7jIkSRkoXyJcho
9/Q0gDOINa5EMA0GCSqGSIb3DQEBDQUAA4ICAQBzfWO7+8HWOKLaYWToJ6XZbpNF
3wXv1yC4W/HRR80m4JSsq9r0d7838Nvd7vLVP6MY6MaVb/JnV76FdQ5WQ6ticD0Y
o3zmpqqbKVSspN0lrkig4surT88AjfVQz/vEIzKNQEbpzc3hC2LCiE2u+cK/ix4j
b9RohnaPvwLnew5RNQRpcmk+XejaNITISr2yQIwXL7TEYy8HdGCfzFSSFhKe9vkb
GsWS5ASrUzRoprswmlgRe8gEHI+d51Z7mWgna0/5mBz9bH/3QXtpxlLWm3bVV+kt
pZjQDTHE0GqG2YsD1Gmp4LU/JFhCojMTtiPCXmr9KFtpiVlx06DuKm5PC8Ak+5w+
m/DQYYfv9z+AA5Y430bjnzwg67bhqVyyek4wcDQinFswv3h4bIB7CJujDcEqXXza
lhG1ufPPCUTMrVjh7AShohZraqlSlyQPY9vEppLwD4W1d+MqDHM7ljOH7gQYaUPi
wE30AdXEOxLZcT3aRKxkKf2esNofSuUC/+NXQvPjpuI4UJKO3eegi+M9dbnKoNWs
MPPLPpycecWPheFYM5K6Ao63cjlUY2wYwCfDTFgjA5q8i/Rp7i6Z6fLE3YWJ4VdR
WOFB7hlluQ//jMW6M1qz6IYXmlUjcXl81VEvlOH/QBNrPvX3I3SYXYgVRnVGUudB
o3eNsanvTU+TIFBh2Q==
-----END CERTIFICATE-----

View File

@ -229,3 +229,12 @@ verify = true
[persist]
## Save peer profiles on disk (default: true)
# profiles = true
[cpuext]
## Use CPU AES-NI instructions set when work with cryptography when available (default: true)
# aesni = true
## Use CPU AVX instructions set when work with cryptography when available (default: true)
# avx = true
## Force usage of CPU instructions set, even if they not found
## DO NOT TOUCH that option if you really don't know what are you doing!
# force = false

View File

@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git
Version: 2.33.0
Version: 2.35.0
Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd
@ -56,18 +56,31 @@ cd build
%endif
%endif
%if 0%{?mageia} > 7
pushd build
make %{?_smp_mflags}
popd
%else
make %{?_smp_mflags}
%if 0%{?fedora} >= 33
pushd %{_target_platform}
%endif
%if 0%{?mageia} > 7
pushd build
%endif
make %{?_smp_mflags}
%if 0%{?fedora} >= 33
popd
%endif
%if 0%{?mageia} > 7
popd
%endif
%install
pushd build
%if 0%{?fedora} >= 33
pushd %{_target_platform}
%endif
%if 0%{?mageia}
pushd build
%endif
@ -124,6 +137,12 @@ getent passwd i2pd >/dev/null || \
%changelog
* Mon Nov 30 2020 orignal <i2porignal@yandex.ru> - 2.35.0
- update to 2.35.0
* Tue Oct 27 2020 orignal <i2porignal@yandex.ru> - 2.34.0
- update to 2.34.0
* Mon Aug 24 2020 orignal <i2porignal@yandex.ru> - 2.33.0
- update to 2.33.0

View File

@ -1,5 +1,5 @@
Name: i2pd
Version: 2.33.0
Version: 2.35.0
Release: 1%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd-git
@ -54,18 +54,31 @@ cd build
%endif
%endif
%if 0%{?mageia} > 7
pushd build
make %{?_smp_mflags}
popd
%else
make %{?_smp_mflags}
%if 0%{?fedora} >= 33
pushd %{_target_platform}
%endif
%if 0%{?mageia} > 7
pushd build
%endif
make %{?_smp_mflags}
%if 0%{?fedora} >= 33
popd
%endif
%if 0%{?mageia} > 7
popd
%endif
%install
pushd build
%if 0%{?fedora} >= 33
pushd %{_target_platform}
%endif
%if 0%{?mageia}
pushd build
%endif
@ -122,6 +135,12 @@ getent passwd i2pd >/dev/null || \
%changelog
* Mon Nov 30 2020 orignal <i2porignal@yandex.ru> - 2.35.0
- update to 2.35.0
* Tue Oct 27 2020 orignal <i2porignal@yandex.ru> - 2.34.0
- update to 2.34.0
* Mon Aug 24 2020 orignal <i2porignal@yandex.ru> - 2.33.0
- update to 2.33.0

View File

@ -128,7 +128,10 @@ namespace i2p
LogPrint(eLogDebug, "FS: data directory: ", datadir);
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
i2p::crypto::InitCrypto (precomputation);
bool aesni; i2p::config::GetOption("cpuext.aesni", aesni);
bool avx; i2p::config::GetOption("cpuext.avx", avx);
bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt);
i2p::crypto::InitCrypto (precomputation, aesni, avx, forceCpuExt);
int netID; i2p::config::GetOption("netid", netID);
i2p::context.SetNetID (netID);

View File

@ -30,8 +30,9 @@
#include "Daemon.h"
#include "util.h"
#include "ECIESX25519AEADRatchetSession.h"
#ifdef WIN32_APP
#include "Win32/Win32App.h"
#include "Win32App.h"
#endif
// For image and info
@ -69,7 +70,7 @@ namespace http {
" .menu { float: left; } .menu a, .commands a { display: block; }\r\n"
" .listitem { display: block; font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
" .tableitem { font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
" .content { float: left; font-size: 1em; margin-left: 4em; max-width: 46em; overflow: auto; }\r\n"
" .content { float: left; font-size: 1em; margin-left: 4em; max-width: 45em; overflow: auto; }\r\n"
" .tunnel.established { color: #56B734; } .tunnel.expiring { color: #D3AE3F; }\r\n"
" .tunnel.failed { color: #D33F3F; } .tunnel.building { color: #434343; }\r\n"
" caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n"
@ -270,8 +271,18 @@ namespace http {
}
s << "<br>\r\n";
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
if (auto remains = Daemon.gracefulShutdownInterval)
s << "<b>Stopping in:</b> " << remains << " seconds<br>\r\n";
if (auto remains = Daemon.gracefulShutdownInterval) {
s << "<b>Stopping in:</b> ";
ShowUptime(s, remains);
s << "<br>\r\n";
}
#elif defined(WIN32_APP)
if (i2p::win32::g_GracefulShutdownEndtime != 0) {
uint16_t remains = (i2p::win32::g_GracefulShutdownEndtime - GetTickCount()) / 1000;
s << "<b>Stopping in:</b> ";
ShowUptime(s, remains);
s << "<br>\r\n";
}
#endif
auto family = i2p::context.GetFamily ();
if (family.length () > 0)
@ -838,6 +849,7 @@ namespace http {
case i2p::client::eSAMSocketTypeSession : s << "session"; break;
case i2p::client::eSAMSocketTypeStream : s << "stream"; break;
case i2p::client::eSAMSocketTypeAcceptor : s << "acceptor"; break;
case i2p::client::eSAMSocketTypeForward : s << "forward"; break;
default: s << "unknown"; break;
}
s << " [" << it->GetSocket ().remote_endpoint() << "]";

12
debian/changelog vendored
View File

@ -1,3 +1,15 @@
i2pd (2.35.0-1) unstable; urgency=high
* updated to version 2.35.0/0.9.48
-- orignal <orignal@i2pmail.org> Mon, 30 Nov 2020 16:00:00 +0000
i2pd (2.34.0-1) unstable; urgency=medium
* updated to version 2.34.0
-- orignal <orignal@i2pmail.org> Tue, 27 Oct 2020 16:00:00 +0000
i2pd (2.33.0-1) unstable; urgency=medium
* updated to version 2.33.0/0.9.47

2
debian/compat vendored
View File

@ -1 +1 @@
9
10

View File

@ -1,15 +0,0 @@
Index: i2pd/Makefile
===================================================================
--- i2pd.orig/Makefile
+++ i2pd/Makefile
@@ -13,8 +13,8 @@ DAEMON_SRC_DIR := daemon
include filelist.mk
-USE_AESNI := yes
-USE_AVX := yes
+USE_AESNI := no
+USE_AVX := no
USE_STATIC := no
USE_MESHNET := no
USE_UPNP := no

View File

@ -1,2 +1 @@
01-tune-build-opts.patch
02-fix-1210.patch
01-fix-1210.patch

View File

@ -27,37 +27,32 @@ namespace cpu
bool aesni = false;
bool avx = false;
void Detect()
void Detect(bool AesSwitch, bool AvxSwitch, bool force)
{
#if defined(__AES__) || defined(__AVX__)
#if defined(__x86_64__) || defined(__i386__)
int info[4];
__cpuid(0, info[0], info[1], info[2], info[3]);
if (info[0] >= 0x00000001) {
__cpuid(0x00000001, info[0], info[1], info[2], info[3]);
#ifdef __AES__
aesni = info[2] & bit_AES; // AESNI
#endif // __AES__
#ifdef __AVX__
avx = info[2] & bit_AVX; // AVX
#endif // __AVX__
#if defined (_WIN32) && (WINVER == 0x0501) // WinXP
if (AesSwitch && force) { // only if forced
#else
if ((info[2] & bit_AES && AesSwitch) || (AesSwitch && force)) {
#endif
aesni = true;
}
#if defined (_WIN32) && (WINVER == 0x0501) // WinXP
if (AvxSwitch && force) { // only if forced
#else
if ((info[2] & bit_AVX && AvxSwitch) || (AvxSwitch && force)) {
#endif
avx = true;
}
}
#endif // defined(__x86_64__) || defined(__i386__)
#ifdef __AES__
if(aesni)
{
LogPrint(eLogInfo, "AESNI enabled");
}
#endif // __AES__
#ifdef __AVX__
if(avx)
{
LogPrint(eLogInfo, "AVX enabled");
}
#endif // __AVX__
#endif // defined(__AES__) || defined(__AVX__)
LogPrint(eLogInfo, "AESNI ", (aesni ? "enabled" : "disabled"));
LogPrint(eLogInfo, "AVX ", (avx ? "enabled" : "disabled"));
}
}
}

View File

@ -16,7 +16,7 @@ namespace cpu
extern bool aesni;
extern bool avx;
void Detect();
void Detect(bool AesSwitch, bool AvxSwitch, bool force);
}
}

View File

@ -47,11 +47,11 @@ namespace config {
("ifname", value<std::string>()->default_value(""), "Network interface to bind to")
("ifname4", value<std::string>()->default_value(""), "Network interface to bind to for ipv4")
("ifname6", value<std::string>()->default_value(""), "Network interface to bind to for ipv6")
("nat", value<bool>()->default_value(true), "Should we assume we are behind NAT? (default: enabled)")
("nat", bool_switch()->default_value(true), "Should we assume we are behind NAT? (default: enabled)")
("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)")
("ipv4", value<bool>()->default_value(true), "Enable communication through ipv4 (default: enabled)")
("ipv4", bool_switch()->default_value(true), "Enable communication through ipv4 (default: enabled)")
("ipv6", bool_switch()->default_value(false), "Enable communication through ipv6 (default: disabled)")
("reservedrange", value<bool>()->default_value(true), "Check remote RI for being in blacklist of reserved IP ranges (default: enabled)")
("reservedrange", bool_switch()->default_value(true), "Check remote RI for being in blacklist of reserved IP ranges (default: enabled)")
("netid", value<int>()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2")
("daemon", bool_switch()->default_value(false), "Router will go to background after start (default: disabled)")
("service", bool_switch()->default_value(false), "Router will use system folders like '/var/lib/i2pd' (default: disabled)")
@ -59,8 +59,8 @@ namespace config {
("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)")
("bandwidth", value<std::string>()->default_value(""), "Bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)")
("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)")
("ntcp", value<bool>()->default_value(false), "Ignored. Always false")
("ssu", value<bool>()->default_value(true), "Enable SSU transport (default: enabled)")
("ntcp", bool_switch()->default_value(false), "Ignored. Always false")
("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)")
("ntcpproxy", value<std::string>()->default_value(""), "Ignored")
#ifdef _WIN32
("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')")
@ -198,7 +198,6 @@ namespace config {
("reseed.urls", value<std::string>()->default_value(
"https://reseed.i2p-projekt.de/,"
"https://reseed.diva.exchange/,"
"https://reseed.i2p2.no/,"
"https://reseed-fr.i2pd.xyz/,"
"https://reseed.memcpy.io/,"
"https://reseed.onion.im/,"
@ -266,6 +265,13 @@ namespace config {
("persist.addressbook", value<bool>()->default_value(true), "Persist full addresses (default: true)")
;
options_description cpuext("CPU encryption extensions options");
cpuext.add_options()
("cpuext.aesni", bool_switch()->default_value(true), "Use auto detection for AESNI CPU extensions. If false, AESNI will be not used")
("cpuext.avx", bool_switch()->default_value(true), "Use auto detection for AVX CPU extensions. If false, AVX will be not used")
("cpuext.force", bool_switch()->default_value(false), "Force usage of CPU extensions. Useful when cpuinfo is not available on virtual machines")
;
m_OptionsDesc
.add(general)
.add(limits)
@ -286,6 +292,7 @@ namespace config {
.add(ntcp2)
.add(nettime)
.add(persist)
.add(cpuext)
;
}

View File

@ -522,7 +522,7 @@ namespace crypto
bn2buf (y, encrypted + len, len);
RAND_bytes (encrypted + 2*len, 256 - 2*len);
}
// ecryption key and iv
// encryption key and iv
EC_POINT_mul (curve, p, nullptr, key, k, ctx);
EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, nullptr);
uint8_t keyBuf[64], iv[64], shared[32];
@ -638,7 +638,7 @@ namespace crypto
{
uint64_t buf[256];
uint64_t hash[12]; // 96 bytes
#ifdef __AVX__
#if defined(__x86_64__) || defined(__i386__)
if(i2p::cpu::avx)
{
__asm__
@ -696,13 +696,6 @@ namespace crypto
// AES
#ifdef __AES__
#ifdef ARM64AES
void init_aesenc(void){
// TODO: Implementation
}
#endif
#define KeyExpansion256(round0,round1) \
"pshufd $0xff, %%xmm2, %%xmm2 \n" \
"movaps %%xmm1, %%xmm4 \n" \
@ -1323,6 +1316,21 @@ namespace crypto
#endif
}
void NoiseSymmetricState::MixHash (const uint8_t * buf, size_t len)
{
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, m_H, 32);
SHA256_Update (&ctx, buf, len);
SHA256_Final (m_H, &ctx);
}
void NoiseSymmetricState::MixKey (const uint8_t * sharedSecret)
{
HKDF (m_CK, sharedSecret, 32, "", m_CK);
// new ck is m_CK[0:31], key is m_CK[32:63]
}
// init and terminate
/* std::vector <std::unique_ptr<std::mutex> > m_OpenSSLMutexes;
@ -1337,10 +1345,9 @@ namespace crypto
}
}*/
void InitCrypto (bool precomputation)
void InitCrypto (bool precomputation, bool aesni, bool avx, bool force)
{
i2p::cpu::Detect ();
i2p::cpu::Detect (aesni, avx, force);
#if LEGACY_OPENSSL
SSL_library_init ();
#endif

View File

@ -169,9 +169,6 @@ namespace crypto
#ifdef __AES__
#ifdef ARM64AES
void init_aesenc(void) __attribute__((constructor));
#endif
class ECBCryptoAESNI
{
public:
@ -311,8 +308,18 @@ namespace crypto
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out, size_t outLen = 64); // salt - 32, out - 32 or 64, info <= 32
// Noise
struct NoiseSymmetricState
{
uint8_t m_H[32] /*h*/, m_CK[64] /*[ck, k]*/;
void MixHash (const uint8_t * buf, size_t len);
void MixKey (const uint8_t * sharedSecret);
};
// init and terminate
void InitCrypto (bool precomputation);
void InitCrypto (bool precomputation, bool aesni, bool avx, bool force);
void TerminateCrypto ();
}
}

View File

@ -560,8 +560,8 @@ namespace client
LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4);
auto msg = i2p::CreateDatabaseStoreMsg (leaseSet, m_PublishReplyToken, inbound);
if (floodfill->GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) // TODO: remove when implemented
msg = WrapMessage (floodfill, msg);
if (floodfill->GetIdentity ()->GetCryptoKeyType () != i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // TODO: remove whan implemented
msg = WrapMessageForRouter (floodfill, msg);
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
shared_from_this (), std::placeholders::_1));
@ -746,7 +746,7 @@ namespace client
request->excluded.insert (nextFloodfill->GetIdentHash ());
request->requestTimeoutTimer.cancel ();
bool isECIES = SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET) &&
bool isECIES = SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) &&
nextFloodfill->GetVersion () >= MAKE_VERSION_NUMBER(0, 9, 46); // >= 0.9.46;
uint8_t replyKey[32], replyTag[32];
RAND_bytes (replyKey, 32); // random session key
@ -756,10 +756,9 @@ namespace client
else
AddSessionKey (replyKey, replyTag);
auto msg = CreateLeaseSetDatabaseLookupMsg (dest, request->excluded,
request->replyTunnel, replyKey, replyTag, isECIES);
if (nextFloodfill->GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) // TODO: remove when implemented
msg = WrapMessage (nextFloodfill, msg);
auto msg = CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, request->replyTunnel, replyKey, replyTag, isECIES);
if (nextFloodfill->GetIdentity ()->GetCryptoKeyType () != i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // TODO: remove whan implemented
msg = WrapMessageForRouter (nextFloodfill, msg);
request->outboundTunnel->SendTunnelDataMsg (
{
i2p::tunnel::TunnelMessageBlock
@ -846,8 +845,8 @@ namespace client
i2p::data::CryptoKeyType LeaseSetDestination::GetPreferredCryptoType () const
{
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET))
return i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET;
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
return i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD;
return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL;
}
@ -902,7 +901,7 @@ namespace client
else
encryptionKey->GenerateKeys ();
encryptionKey->CreateDecryptor ();
if (it == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET)
if (it == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
{
m_ECIESx25519EncryptionKey.reset (encryptionKey);
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
@ -1219,7 +1218,7 @@ namespace client
bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const
{
if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET)
if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
if (m_ECIESx25519EncryptionKey && m_ECIESx25519EncryptionKey->decryptor)
return m_ECIESx25519EncryptionKey->decryptor->Decrypt (encrypted, data, ctx, true);
if (m_StandardEncryptionKey && m_StandardEncryptionKey->decryptor)
@ -1231,12 +1230,12 @@ namespace client
bool ClientDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const
{
return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET ? (bool)m_ECIESx25519EncryptionKey : (bool)m_StandardEncryptionKey;
return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool)m_ECIESx25519EncryptionKey : (bool)m_StandardEncryptionKey;
}
const uint8_t * ClientDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const
{
if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET)
if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
return m_ECIESx25519EncryptionKey ? m_ECIESx25519EncryptionKey->pub : nullptr;
return m_StandardEncryptionKey ? m_StandardEncryptionKey->pub : nullptr;
}

View File

@ -176,15 +176,6 @@ namespace garlic
memcpy (m_H, hh, 32);
}
void ECIESX25519AEADRatchetSession::MixHash (const uint8_t * buf, size_t len)
{
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, m_H, 32);
SHA256_Update (&ctx, buf, len);
SHA256_Final (m_H, &ctx);
}
void ECIESX25519AEADRatchetSession::CreateNonce (uint64_t seqn, uint8_t * nonce)
{
memset (nonce, 0, 4);
@ -245,7 +236,7 @@ namespace garlic
if (!GetOwner ()) return false;
// we are Bob
// KDF1
MixHash (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET), 32); // h = SHA256(h || bpk)
MixHash (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD), 32); // h = SHA256(h || bpk)
if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk))
{
@ -256,8 +247,8 @@ namespace garlic
MixHash (m_Aepk, 32); // h = SHA256(h || aepk)
uint8_t sharedSecret[32];
GetOwner ()->Decrypt (m_Aepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519(bsk, aepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
GetOwner ()->Decrypt (m_Aepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519(bsk, aepk)
MixKey (sharedSecret);
// decrypt flags/static
uint8_t nonce[12], fs[32];
@ -276,8 +267,8 @@ namespace garlic
{
// static key, fs is apk
memcpy (m_RemoteStaticKey, fs, 32);
GetOwner ()->Decrypt (fs, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519(bsk, apk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
GetOwner ()->Decrypt (fs, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519(bsk, apk)
MixKey (sharedSecret);
}
else // all zeros flags
CreateNonce (1, nonce);
@ -289,10 +280,13 @@ namespace garlic
LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed");
return false;
}
if (isStatic) MixHash (buf, len); // h = SHA256(h || ciphertext)
m_State = eSessionStateNewSessionReceived;
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
m_State = eSessionStateNewSessionReceived;
if (isStatic)
{
MixHash (buf, len); // h = SHA256(h || ciphertext)
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
}
HandlePayload (payload.data (), len - 16, nullptr, 0);
return true;
@ -452,7 +446,7 @@ namespace garlic
LogPrint (eLogDebug, "Garlic: new send ratchet ", m_NextSendRatchet->newKey ? "new" : "old", " key ", m_NextSendRatchet->keyID, " created");
}
bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic)
{
ResetKeys ();
// we are Alice, bpk is m_RemoteStaticKey
@ -469,32 +463,48 @@ namespace garlic
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk)
uint8_t sharedSecret[32];
m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
// encrypt static key section
MixKey (sharedSecret);
// encrypt flags/static key section
uint8_t nonce[12];
CreateNonce (0, nonce);
if (!i2p::crypto::AEADChaCha20Poly1305 (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET), 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt
const uint8_t * fs;
if (isStatic)
fs = GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD);
else
{
LogPrint (eLogWarning, "Garlic: Static section AEAD encryption failed ");
memset (out + offset, 0, 32); // all zeros flags section
fs = out + offset;
}
if (!i2p::crypto::AEADChaCha20Poly1305 (fs, 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt
{
LogPrint (eLogWarning, "Garlic: Flags/static section AEAD encryption failed ");
return false;
}
MixHash (out + offset, 48); // h = SHA256(h || ciphertext)
offset += 48;
// KDF2
GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519 (ask, bpk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
if (isStatic)
{
GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bpk)
MixKey (sharedSecret);
}
else
CreateNonce (1, nonce);
// encrypt payload
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt
{
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
return false;
}
MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext)
m_State = eSessionStateNewSessionSent;
if (isStatic)
{
MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext)
if (GetOwner ())
GenerateMoreReceiveTags (CreateNewSessionTagset (), ECIESX25519_NSR_NUM_GENERATED_TAGS);
}
return true;
}
@ -520,9 +530,9 @@ namespace garlic
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk)
uint8_t sharedSecret[32];
m_EphemeralKeys->Agree (m_Aepk, sharedSecret); // sharedSecret = x25519(besk, aepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32)
MixKey (sharedSecret);
m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret); // sharedSecret = x25519(besk, apk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
MixKey (sharedSecret);
uint8_t nonce[12];
CreateNonce (0, nonce);
// calculate hash for zero length
@ -607,9 +617,9 @@ namespace garlic
{
// only fist time, we assume ephemeral keys the same
m_EphemeralKeys->Agree (bepk, sharedSecret); // sharedSecret = x25519(aesk, bepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32)
GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519 (ask, bepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
MixKey (sharedSecret);
GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bepk)
MixKey (sharedSecret);
}
uint8_t nonce[12];
CreateNonce (0, nonce);
@ -784,6 +794,11 @@ namespace garlic
return nullptr;
len += 72;
break;
case eSessionStateOneTime:
if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen, false))
return nullptr;
len += 96;
break;
default:
return nullptr;
}
@ -794,6 +809,12 @@ namespace garlic
return m;
}
std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg)
{
m_State = eSessionStateOneTime;
return WrapSingleMessage (msg);
}
std::vector<uint8_t> ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first)
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
@ -878,7 +899,7 @@ namespace garlic
}
}
// msg
if (msg && m_Destination)
if (msg)
offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset);
// ack
if (m_AckRequests.size () > 0)
@ -989,10 +1010,13 @@ namespace garlic
}
void ECIESX25519AEADRatchetSession::GenerateMoreReceiveTags (std::shared_ptr<RatchetTagSet> receiveTagset, int numTags)
{
if (GetOwner ())
{
for (int i = 0; i < numTags; i++)
GetOwner ()->AddECIESx25519SessionNextTag (receiveTagset);
}
}
bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts)
{

View File

@ -31,7 +31,7 @@ namespace garlic
const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after
const int ECIESX25519_INCOMING_TAGS_EXPIRATION_TIMEOUT = 600; // in seconds
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180
const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 4096; // number of tags we request new tagset after
const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after
const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24;
const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 160;
const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12;
@ -129,7 +129,9 @@ namespace garlic
const uint8_t ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG = 0x02;
const uint8_t ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG = 0x04;
class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, public std::enable_shared_from_this<ECIESX25519AEADRatchetSession>
class ECIESX25519AEADRatchetSession: public GarlicRoutingSession,
private i2p::crypto::NoiseSymmetricState,
public std::enable_shared_from_this<ECIESX25519AEADRatchetSession>
{
enum SessionState
{
@ -137,7 +139,8 @@ namespace garlic
eSessionStateNewSessionReceived,
eSessionStateNewSessionSent,
eSessionStateNewSessionReplySent,
eSessionStateEstablished
eSessionStateEstablished,
eSessionStateOneTime
};
struct DHRatchet
@ -155,6 +158,7 @@ namespace garlic
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<RatchetTagSet> receiveTagset, int index = 0);
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg);
const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; }
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
@ -169,12 +173,12 @@ namespace garlic
bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); }
bool IsRatchets () const { return true; };
bool IsReadyToSend () const { return m_State != eSessionStateNewSessionSent; };
uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; };
private:
void ResetKeys ();
void MixHash (const uint8_t * buf, size_t len);
void CreateNonce (uint64_t seqn, uint8_t * nonce);
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes
std::shared_ptr<RatchetTagSet> CreateNewSessionTagset ();
@ -185,7 +189,7 @@ namespace garlic
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<RatchetTagSet>& receiveTagset, int index);
void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<RatchetTagSet>& receiveTagset);
bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic = true);
bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
bool NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
@ -199,7 +203,7 @@ namespace garlic
private:
uint8_t m_H[32], m_CK[64] /* [chainkey, key] */, m_RemoteStaticKey[32];
uint8_t m_RemoteStaticKey[32];
uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only
uint8_t m_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;

View File

@ -46,13 +46,13 @@ namespace fs {
dataDir = cmdline_param;
return;
}
#if defined(WIN32) || defined(_WIN32)
#ifdef _WIN32
char localAppData[MAX_PATH];
// check executable directory first
if(!GetModuleFileName(NULL, localAppData, MAX_PATH))
{
#if defined(WIN32_APP)
#ifdef WIN32_APP
MessageBox(NULL, TEXT("Unable to get application path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK);
#else
fprintf(stderr, "Error: Unable to get application path!");
@ -70,7 +70,7 @@ namespace fs {
{
if(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, localAppData) != S_OK)
{
#if defined(WIN32_APP)
#ifdef WIN32_APP
MessageBox(NULL, TEXT("Unable to get AppData path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK);
#else
fprintf(stderr, "Error: Unable to get AppData path!");

View File

@ -506,7 +506,7 @@ namespace garlic
else
{
bool found = false;
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET))
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
{
// try ECIESx25519 tag
uint64_t tag;
@ -536,7 +536,7 @@ namespace garlic
decryption->Decrypt(buf + 514, length - 514, buf + 514);
HandleAESBlock (buf + 514, length - 514, decryption, msg->from);
}
else if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET))
else if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
{
// otherwise ECIESx25519
auto session = std::make_shared<ECIESX25519AEADRatchetSession> (this, false); // incoming
@ -709,18 +709,27 @@ namespace garlic
}
}
std::shared_ptr<I2NPMessage> GarlicDestination::WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet)
std::shared_ptr<I2NPMessage> GarlicDestination::WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<I2NPMessage> msg)
{
auto session = GetRoutingSession (destination, attachLeaseSet);
if (router->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
{
auto session = std::make_shared<ECIESX25519AEADRatchetSession>(this, false);
session->SetRemoteStaticKey (router->GetIdentity ()->GetEncryptionPublicKey ());
return session->WrapOneTimeMessage (msg);
}
else
{
auto session = GetRoutingSession (router, false);
return session->WrapSingleMessage (msg);
}
}
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet)
{
if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET &&
SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET))
if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD &&
SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
{
ECIESX25519AEADRatchetSessionPtr session;
uint8_t staticKey[32];
@ -762,6 +771,7 @@ namespace garlic
}
return session;
}
return nullptr;
}
void GarlicDestination::CleanupExpiredTags ()

View File

@ -114,6 +114,7 @@ namespace garlic
virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession
virtual bool MessageConfirmed (uint32_t msgID);
virtual bool IsRatchets () const { return false; };
virtual bool IsReadyToSend () const { return true; };
virtual uint64_t GetLastActivityTimestamp () const { return 0; }; // non-zero for rathets only
void SetLeaseSetUpdated ()
@ -237,8 +238,8 @@ namespace garlic
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
void CleanupExpiredTags ();
void RemoveDeliveryStatusSession (uint32_t msgID);
std::shared_ptr<I2NPMessage> WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet = false);
std::shared_ptr<I2NPMessage> WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<I2NPMessage> msg);
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag

View File

@ -361,15 +361,27 @@ namespace i2p
{
LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours");
BN_CTX * ctx = BN_CTX_new ();
i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText, ctx);
bool success = i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText, ctx);
BN_CTX_free (ctx);
if(!success) return false;
uint8_t retCode = 0;
bool isECIES = i2p::context.IsECIES ();
// replace record to reply
if (i2p::context.AcceptsTunnels () &&
i2p::tunnel::tunnels.GetTransitTunnels ().size () <= g_MaxNumTransitTunnels &&
!i2p::transport::transports.IsBandwidthExceeded () &&
!i2p::transport::transports.IsTransitBandwidthExceeded ())
{
auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
auto transitTunnel = isECIES ?
i2p::tunnel::CreateTransitTunnel (
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80,
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) :
i2p::tunnel::CreateTransitTunnel (
bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
@ -378,23 +390,54 @@ namespace i2p
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80,
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40);
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 0;
}
else
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 30; // always reject with bandwidth reason (30)
retCode = 30; // always reject with bandwidth reason (30)
//TODO: fill filler
if (isECIES)
{
memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode;
}
else
{
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode;
SHA256 (record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1, // + 1 byte of ret
record + BUILD_RESPONSE_RECORD_HASH_OFFSET);
}
// encrypt reply
i2p::crypto::CBCEncryption encryption;
for (int j = 0; j < num; j++)
{
uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE;
if (isECIES)
{
if (j == i)
{
uint8_t nonce[12];
memset (nonce, 0, 12);
auto noiseState = std::move (i2p::context.GetCurrentNoiseState ());
if (!noiseState || !i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16,
noiseState->m_H, 32, noiseState->m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
{
LogPrint (eLogWarning, "I2NP: Reply AEAD encryption failed");
return false;
}
}
else
{
encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
encryption.SetIV (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
}
}
else
{
encryption.SetKey (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
encryption.SetIV (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE;
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
}
}
return true;
}
}
@ -405,7 +448,7 @@ namespace i2p
{
int num = buf[0];
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records");
if (len < num*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 1)
if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1)
{
LogPrint (eLogError, "VaribleTunnelBuild message of ", num, " records is too short ", len);
return;
@ -429,6 +472,27 @@ namespace i2p
}
}
else
{
if (i2p::context.IsECIES ())
{
uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (num, buf + 1, clearText))
{
if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel
{
// so we send it to reply tunnel
transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
eI2NPVariableTunnelBuildReply, buf, len,
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
else
transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
}
else
{
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (num, buf + 1, clearText))
@ -448,10 +512,16 @@ namespace i2p
}
}
}
}
void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
{
if (len < NUM_TUNNEL_BUILD_RECORDS*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE)
if (i2p::context.IsECIES ())
{
LogPrint (eLogWarning, "TunnelBuild is too old for ECIES router");
return;
}
if (len < NUM_TUNNEL_BUILD_RECORDS*TUNNEL_BUILD_RECORD_SIZE)
{
LogPrint (eLogError, "TunnelBuild message is too short ", len);
return;

View File

@ -98,6 +98,7 @@ namespace i2p
const size_t ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 464;
// ECIES BuildResponseRecord
const size_t ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET = 0;
const size_t ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET = 511;
enum I2NPMessageType

View File

@ -48,10 +48,10 @@ namespace data
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type, CryptoKeyType cryptoType)
{
if (cryptoType == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET)
if (cryptoType == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
{
memcpy (m_StandardIdentity.publicKey, publicKey, 32);
RAND_bytes (m_StandardIdentity.publicKey, 224);
RAND_bytes (m_StandardIdentity.publicKey + 32, 224);
}
else
memcpy (m_StandardIdentity.publicKey, publicKey, 256);
@ -426,7 +426,7 @@ namespace data
case CRYPTO_KEY_TYPE_ELGAMAL:
return std::make_shared<i2p::crypto::ElGamalEncryptor>(key);
break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET:
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD:
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetEncryptor>(key);
break;
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
@ -476,7 +476,7 @@ namespace data
size_t PrivateKeys::GetFullLen () const
{
size_t ret = m_Public->GetFullLen () + 256 + m_Public->GetSigningPrivateKeyLen ();
size_t ret = m_Public->GetFullLen () + GetPrivateKeyLen () + m_Public->GetSigningPrivateKeyLen ();
if (IsOfflineSignature ())
ret += m_OfflineSignature.size () + m_TransientSigningPrivateKeyLen;
return ret;
@ -486,9 +486,10 @@ namespace data
{
m_Public = std::make_shared<IdentityEx>();
size_t ret = m_Public->FromBuffer (buf, len);
if (!ret || ret + 256 > len) return 0; // overflow
memcpy (m_PrivateKey, buf + ret, 256); // private key always 256
ret += 256;
auto cryptoKeyLen = GetPrivateKeyLen ();
if (!ret || ret + cryptoKeyLen > len) return 0; // overflow
memcpy (m_PrivateKey, buf + ret, cryptoKeyLen);
ret += cryptoKeyLen;
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
if(signingPrivateKeySize + ret > len || signingPrivateKeySize > 128) return 0; // overflow
memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize);
@ -540,8 +541,9 @@ namespace data
size_t PrivateKeys::ToBuffer (uint8_t * buf, size_t len) const
{
size_t ret = m_Public->ToBuffer (buf, len);
memcpy (buf + ret, m_PrivateKey, 256); // private key always 256
ret += 256;
auto cryptoKeyLen = GetPrivateKeyLen ();
memcpy (buf + ret, m_PrivateKey, cryptoKeyLen);
ret += cryptoKeyLen;
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
if(ret + signingPrivateKeySize > len) return 0; // overflow
if (IsOfflineSignature ())
@ -657,6 +659,12 @@ namespace data
return IsOfflineSignature () ? m_TransientSignatureLen : m_Public->GetSignatureLen ();
}
size_t PrivateKeys::GetPrivateKeyLen () const
{
// private key length always 256, but type 4
return (m_Public->GetCryptoKeyType () == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) ? 32 : 256;
}
uint8_t * PrivateKeys::GetPadding()
{
if(m_Public->GetSigningKeyType () == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519)
@ -679,6 +687,9 @@ namespace data
case CRYPTO_KEY_TYPE_ELGAMAL:
return std::make_shared<i2p::crypto::ElGamalDecryptor>(key);
break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD:
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetDecryptor>(key);
break;
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST:
return std::make_shared<i2p::crypto::ECIESP256Decryptor>(key);
@ -686,9 +697,6 @@ namespace data
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
return std::make_shared<i2p::crypto::ECIESGOSTR3410Decryptor>(key);
break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET:
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetDecryptor>(key);
break;
default:
LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)cryptoType);
};
@ -768,7 +776,7 @@ namespace data
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
i2p::crypto::CreateECIESGOSTR3410RandomKeys (priv, pub);
break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET:
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD:
i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys (priv, pub);
break;
default:
@ -820,7 +828,7 @@ namespace data
XORMetric operator^(const IdentHash& key1, const IdentHash& key2)
{
XORMetric m;
#ifdef __AVX__
#if defined(__x86_64__) || defined(__i386__)
if(i2p::cpu::avx)
{
__asm__

View File

@ -64,7 +64,7 @@ namespace data
const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0;
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC = 1;
const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET = 4;
const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD = 4;
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST = 65280; // TODO: remove later
const uint16_t CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC = 65281; // TODO: use GOST R 34.11 instead SHA256 and GOST 28147-89 instead AES
@ -183,6 +183,7 @@ namespace data
void CreateSigner () const;
void CreateSigner (SigningKeyType keyType) const;
size_t GetPrivateKeyLen () const;
private:

View File

@ -39,21 +39,6 @@ namespace transport
delete[] m_SessionConfirmedBuffer;
}
void NTCP2Establisher::MixKey (const uint8_t * inputKeyMaterial)
{
i2p::crypto::HKDF (m_CK, inputKeyMaterial, 32, "", m_CK);
// ck is m_CK[0:31], k is m_CK[32:63]
}
void NTCP2Establisher::MixHash (const uint8_t * buf, size_t len)
{
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, m_H, 32);
SHA256_Update (&ctx, buf, len);
SHA256_Final (m_H, &ctx);
}
void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub)
{
static const uint8_t protocolNameHash[] =
@ -757,6 +742,10 @@ namespace transport
void NTCP2Session::ReceiveLength ()
{
if (IsTerminated ()) return;
#ifdef __linux__
const int one = 1;
setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));
#endif
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_NextReceivedLen, 2), boost::asio::transfer_all (),
std::bind(&NTCP2Session::HandleReceivedLength, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
}
@ -808,6 +797,10 @@ namespace transport
void NTCP2Session::Receive ()
{
if (IsTerminated ()) return;
#ifdef __linux__
const int one = 1;
setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));
#endif
boost::asio::async_read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (),
std::bind(&NTCP2Session::HandleReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
}

View File

@ -74,7 +74,7 @@ namespace transport
// RouterInfo flags
const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01;
struct NTCP2Establisher
struct NTCP2Establisher: private i2p::crypto::NoiseSymmetricState
{
NTCP2Establisher ();
~NTCP2Establisher ();
@ -94,8 +94,6 @@ namespace transport
void KDF3Alice (); // for SessionConfirmed part 2
void KDF3Bob ();
void MixKey (const uint8_t * inputKeyMaterial);
void MixHash (const uint8_t * buf, size_t len);
void KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH
void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate
void CreateEphemeralKey ();
@ -112,7 +110,7 @@ namespace transport
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
uint8_t m_RemoteEphemeralPublicKey[32]; // x25519
uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[64] /* [ck, k]*/;
uint8_t m_RemoteStaticKey[32], m_IV[16];
i2p::data::IdentHash m_RemoteIdentHash;
uint16_t m3p2Len;

View File

@ -111,6 +111,9 @@ namespace data
case eI2NPDatabaseLookup:
HandleDatabaseLookupMsg (msg);
break;
case eI2NPDeliveryStatus:
HandleDeliveryStatusMsg (msg);
break;
case eI2NPDummyMsg:
// plain RouterInfo from NTCP2 with flags for now
HandleNTCP2RouterInfoMsg (msg);
@ -148,12 +151,23 @@ namespace data
lastDestinationCleanup = ts;
}
if (ts - lastPublish >= NETDB_PUBLISH_INTERVAL) // update timestamp and publish
// publish
if (!m_HiddenMode && i2p::transport::transports.IsOnline ())
{
bool publish = false;
if (m_PublishReplyToken)
{
if (ts - lastPublish >= NETDB_PUBLISH_CONFIRMATION_TIMEOUT) publish = true;
}
else if (i2p::context.GetLastUpdateTime () > lastPublish ||
ts - lastPublish >= NETDB_PUBLISH_INTERVAL) publish = true;
if (publish) // update timestamp and publish
{
i2p::context.UpdateTimestamp (ts);
if (!m_HiddenMode) Publish ();
Publish ();
lastPublish = ts;
}
}
if (ts - lastExploratory >= 30) // exploratory every 30 seconds
{
auto numRouters = m_RouterInfos.size ();
@ -226,7 +240,7 @@ namespace data
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
if (wasFloodfill)
m_Floodfills.remove (r);
else
else if (r->IsEligibleFloodfill ())
m_Floodfills.push_back (r);
}
}
@ -249,7 +263,7 @@ namespace data
if (inserted)
{
LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64());
if (r->IsFloodfill () && r->IsReachable ()) // floodfill must be reachable
if (r->IsFloodfill () && r->IsEligibleFloodfill ())
{
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
m_Floodfills.push_back (r);
@ -992,6 +1006,16 @@ namespace data
}
}
void NetDb::HandleDeliveryStatusMsg (std::shared_ptr<const I2NPMessage> msg)
{
if (m_PublishReplyToken == bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET))
{
LogPrint (eLogInfo, "NetDb: Publishing confirmed. reply token=", m_PublishReplyToken);
m_PublishExcluded.clear ();
m_PublishReplyToken = 0;
}
}
void NetDb::Explore (int numDestinations)
{
// new requests
@ -1045,18 +1069,22 @@ namespace data
void NetDb::Publish ()
{
i2p::context.UpdateStats (); // for floodfill
std::set<IdentHash> excluded; // TODO: fill up later
for (int i = 0; i < 2; i++)
if (m_PublishExcluded.size () > NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS)
{
auto floodfill = GetClosestFloodfill (i2p::context.GetRouterInfo ().GetIdentHash (), excluded);
LogPrint (eLogError, "NetDb: Couldn't publish our RouterInfo to ", NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS, " closest routers. Try again");
m_PublishExcluded.clear ();
}
auto floodfill = GetClosestFloodfill (i2p::context.GetIdentHash (), m_PublishExcluded);
if (floodfill)
{
uint32_t replyToken;
RAND_bytes ((uint8_t *)&replyToken, 4);
LogPrint (eLogInfo, "NetDb: Publishing our RouterInfo to ", i2p::data::GetIdentHashAbbreviation(floodfill->GetIdentHash ()), ". reply token=", replyToken);
m_PublishExcluded.insert (floodfill->GetIdentHash ());
m_PublishReplyToken = replyToken;
transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken));
excluded.insert (floodfill->GetIdentHash ());
}
}
}

View File

@ -41,7 +41,10 @@ namespace data
const int NETDB_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours
const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours
const int NETDB_PUBLISH_INTERVAL = 60 * 40;
const int NETDB_PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
const int NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15;
const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 36); // 0.9.36
const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 28); // 0.9.28
/** function for visiting a leaseset stored in a floodfill */
typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;
@ -77,6 +80,7 @@ namespace data
void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleNTCP2RouterInfoMsg (std::shared_ptr<const I2NPMessage> m);
void HandleDeliveryStatusMsg (std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<const RouterInfo> GetRandomRouter () const;
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
@ -115,6 +119,8 @@ namespace data
void ClearRouterInfos () { m_RouterInfos.clear (); };
uint32_t GetPublishReplyToken () const { return m_PublishReplyToken; };
private:
void Load ();
@ -162,9 +168,11 @@ namespace data
/** router info we are bootstrapping from or nullptr if we are not currently doing that*/
std::shared_ptr<RouterInfo> m_FloodfillBootstrap;
/** true if in hidden mode */
bool m_HiddenMode;
std::set<IdentHash> m_PublishExcluded;
uint32_t m_PublishReplyToken = 0;
};
extern NetDb netdb;

View File

@ -19,6 +19,7 @@
#include "version.h"
#include "Log.h"
#include "Family.h"
#include "TunnelConfig.h"
#include "RouterContext.h"
namespace i2p
@ -41,6 +42,13 @@ namespace i2p
CreateNewRouter ();
m_Decryptor = m_Keys.CreateDecryptor (nullptr);
UpdateRouterInfo ();
if (IsECIES ())
{
auto initState = new i2p::crypto::NoiseSymmetricState ();
i2p::tunnel::InitBuildRequestRecordNoiseState (*initState);
initState->MixHash (GetIdentity ()->GetEncryptionPublicKey (), 32); // h = SHA256(h || hepk)
m_InitialNoiseState.reset (initState);
}
}
void RouterContext::CreateNewRouter ()
@ -81,7 +89,7 @@ namespace i2p
host = i2p::util::net::GetInterfaceAddress(ifname4, false).to_string();
if (ssu)
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
}
if (ipv6)
{
@ -95,7 +103,7 @@ namespace i2p
host = i2p::util::net::GetInterfaceAddress(ifname6, true).to_string();
if (ssu)
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
}
routerInfo.SetCaps (i2p::data::RouterInfo::eReachable |
@ -478,7 +486,7 @@ namespace i2p
if (ssu)
{
std::string host = "::1"; // TODO: read host
m_RouterInfo.AddSSUAddress (host.c_str (), port, GetIdentHash ());
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
}
}
// NTCP2
@ -555,6 +563,7 @@ namespace i2p
}
bool RouterContext::Load ()
{
{
std::ifstream fk (i2p::fs::DataDirPath (ROUTER_KEYS), std::ifstream::in | std::ifstream::binary);
if (!fk.is_open ()) return false;
@ -575,12 +584,22 @@ namespace i2p
m_Keys.FromBuffer (buf, len);
delete[] buf;
}
}
std::shared_ptr<const i2p::data::IdentityEx> oldIdentity;
if (m_Keys.GetPublic ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{
// update keys
LogPrint (eLogInfo, "Router: router keys are obsolete. Creating new");
oldIdentity = m_Keys.GetPublic ();
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519);
SaveKeys ();
}
// read NTCP2 keys if available
std::ifstream n2k (i2p::fs::DataDirPath (NTCP2_KEYS), std::ifstream::in | std::ifstream::binary);
if (n2k)
{
n2k.seekg (0, std::ios::end);
len = n2k.tellg();
size_t len = n2k.tellg();
n2k.seekg (0, std::ios::beg);
if (len == sizeof (NTCP2PrivateKeys))
{
@ -590,17 +609,15 @@ namespace i2p
n2k.close ();
}
// read RouterInfo
m_RouterInfo.SetRouterIdentity (GetIdentity ());
m_RouterInfo.SetRouterIdentity (oldIdentity ? oldIdentity : GetIdentity ());
i2p::data::RouterInfo routerInfo(i2p::fs::DataDirPath (ROUTER_INFO));
if (!routerInfo.IsUnreachable ()) // router.info looks good
{
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
if (oldIdentity)
m_RouterInfo.SetRouterIdentity (GetIdentity ()); // from new keys
m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION);
m_RouterInfo.SetProperty ("router.version", I2P_VERSION);
// Migration to 0.9.24. TODO: remove later
m_RouterInfo.DeleteProperty ("coreVersion");
m_RouterInfo.DeleteProperty ("stat_uptime");
}
else
{
@ -645,6 +662,15 @@ namespace i2p
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len)));
}
bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len)
{
auto msg = CreateI2NPMessage (typeID, payload, len);
if (!msg) return false;
i2p::HandleI2NPMessage (msg);
return true;
}
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
std::unique_lock<std::mutex> l(m_GarlicMutex);
@ -652,10 +678,15 @@ namespace i2p
}
void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
if (i2p::data::netdb.GetPublishReplyToken () == bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET))
i2p::data::netdb.PostI2NPMsg (msg);
else
{
std::unique_lock<std::mutex> l(m_GarlicMutex);
i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg);
}
}
void RouterContext::CleanupDestination ()
{
@ -673,9 +704,32 @@ namespace i2p
return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, true) : false;
}
bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const
bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx)
{
return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, false) : false;
if (!m_Decryptor) return false;
if (IsECIES ())
{
if (!m_InitialNoiseState) return false;
// m_InitialNoiseState is h = SHA256(h || hepk)
m_CurrentNoiseState.reset (new i2p::crypto::NoiseSymmetricState (*m_InitialNoiseState));
m_CurrentNoiseState->MixHash (encrypted, 32); // h = SHA256(h || sepk)
uint8_t sharedSecret[32];
m_Decryptor->Decrypt (encrypted, sharedSecret, ctx, false);
m_CurrentNoiseState->MixKey (sharedSecret);
encrypted += 32;
uint8_t nonce[12];
memset (nonce, 0, 12);
if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE,
m_CurrentNoiseState->m_H, 32, m_CurrentNoiseState->m_CK + 32, nonce, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, false)) // decrypt
{
LogPrint (eLogWarning, "Router: Tunnel record AEAD decryption failed");
return false;
}
m_CurrentNoiseState->MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext)
return true;
}
else
return m_Decryptor->Decrypt (encrypted, data, ctx, false);
}
i2p::crypto::X25519Keys& RouterContext::GetStaticKeys ()

View File

@ -84,7 +84,7 @@ namespace i2p
void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; };
int GetNetID () const { return m_NetID; };
void SetNetID (int netID) { m_NetID = netID; };
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const;
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx);
void UpdatePort (int port); // called from Daemon
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
@ -109,6 +109,8 @@ namespace i2p
bool SupportsV4 () const { return m_RouterInfo.IsV4 (); };
void SetSupportsV6 (bool supportsV6);
void SetSupportsV4 (bool supportsV4);
bool IsECIES () const { return GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
std::unique_ptr<i2p::crypto::NoiseSymmetricState>& GetCurrentNoiseState () { return m_CurrentNoiseState; };
void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove
void UpdateStats ();
@ -133,7 +135,7 @@ namespace i2p
// implements GarlicDestination
void HandleI2NPMessage (const uint8_t * buf, size_t len);
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) { return false; }; // not implemented
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len);
private:
@ -160,6 +162,8 @@ namespace i2p
std::mutex m_GarlicMutex;
std::unique_ptr<NTCP2PrivateKeys> m_NTCP2Keys;
std::unique_ptr<i2p::crypto::X25519Keys> m_StaticKeys;
// for ECIESx25519
std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_InitialNoiseState, m_CurrentNoiseState;
};
extern RouterContext context;

View File

@ -719,7 +719,10 @@ namespace data
addr->date = 0;
addr->ssu.reset (new SSUExt ());
addr->ssu->mtu = mtu;
if (key)
memcpy (addr->ssu->key, key, 32);
else
RAND_bytes (addr->ssu->key, 32);
for (const auto& it: *m_Addresses) // don't insert same address twice
if (*it == *addr) return;
m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
@ -945,5 +948,12 @@ namespace data
if (encryptor)
encryptor->Encrypt (data, encrypted, ctx, true);
}
bool RouterInfo::IsEligibleFloodfill () const
{
// floodfill must be reachable, >= 0.9.28 and not DSA
return IsReachable () && m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1;
}
}
}

View File

@ -184,6 +184,7 @@ namespace data
bool IsHidden () const { return m_Caps & eHidden; };
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
bool IsEligibleFloodfill () const;
uint8_t GetCaps () const { return m_Caps; };
void SetCaps (uint8_t caps);

View File

@ -13,6 +13,10 @@
#include "NetDb.hpp"
#include "SSU.h"
#ifdef _WIN32
#include <boost/winapi/error_codes.hpp>
#endif
namespace i2p
{
namespace transport
@ -247,11 +251,17 @@ namespace transport
void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
{
if (!ecode ||
ecode == boost::asio::error::connection_refused ||
ecode == boost::asio::error::connection_reset ||
ecode == boost::asio::error::network_unreachable ||
ecode == boost::asio::error::host_unreachable)
if (!ecode
|| ecode == boost::asio::error::connection_refused
|| ecode == boost::asio::error::connection_reset
|| ecode == boost::asio::error::network_unreachable
|| ecode == boost::asio::error::host_unreachable
#ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO
|| ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_
|| ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_
|| ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_
#endif
)
// just try continue reading when received ICMP response otherwise socket can crash,
// but better to find out which host were sent it and mark that router as unreachable
{
@ -300,11 +310,17 @@ namespace transport
void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
{
if (!ecode ||
ecode == boost::asio::error::connection_refused ||
ecode == boost::asio::error::connection_reset ||
ecode == boost::asio::error::network_unreachable ||
ecode == boost::asio::error::host_unreachable)
if (!ecode
|| ecode == boost::asio::error::connection_refused
|| ecode == boost::asio::error::connection_reset
|| ecode == boost::asio::error::network_unreachable
|| ecode == boost::asio::error::host_unreachable
#ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO
|| ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_
|| ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_
|| ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_
#endif
)
// just try continue reading when received ICMP response otherwise socket can crash,
// but better to find out which host were sent it and mark that router as unreachable
{

View File

@ -30,14 +30,15 @@ namespace transport
if (router)
{
// we are client
auto address = router->GetSSUAddress (false);
auto address = IsV6 () ? router->GetSSUV6Address () : router->GetSSUAddress (true);
if (address) m_IntroKey = address->ssu->key;
m_Data.AdjustPacketSize (router); // mtu
}
else
{
// we are server
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () :
i2p::context.GetRouterInfo ().GetSSUAddress (true);
if (address) m_IntroKey = address->ssu->key;
}
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();

View File

@ -21,8 +21,16 @@ namespace stream
{
void SendBufferQueue::Add (const uint8_t * buf, size_t len, SendHandler handler)
{
m_Buffers.push_back (std::make_shared<SendBuffer>(buf, len, handler));
m_Size += len;
Add (std::make_shared<SendBuffer>(buf, len, handler));
}
void SendBufferQueue::Add (std::shared_ptr<SendBuffer> buf)
{
if (buf)
{
m_Buffers.push_back (buf);
m_Size += buf->len;
}
}
size_t SendBufferQueue::Get (uint8_t * buf, size_t len)
@ -325,7 +333,7 @@ namespace stream
if (flags & PACKET_FLAG_SIGNATURE_INCLUDED)
{
uint8_t signature[256];
auto signatureLen = m_RemoteIdentity->GetSignatureLen ();
auto signatureLen = m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : m_RemoteIdentity->GetSignatureLen ();
if(signatureLen <= sizeof(signature))
{
memcpy (signature, optionData, signatureLen);
@ -756,7 +764,7 @@ namespace stream
return;
}
}
if (!m_RoutingSession || !m_RoutingSession->GetOwner ()) // expired and detached
if (!m_RoutingSession || !m_RoutingSession->GetOwner () || !m_RoutingSession->IsReadyToSend ()) // expired and detached or new session sent
m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true);
if (!m_CurrentOutboundTunnel && m_RoutingSession) // first message to send
{

View File

@ -111,6 +111,11 @@ namespace stream
buf = new uint8_t[len];
memcpy (buf, b, len);
}
SendBuffer (size_t l): // creat empty buffer
len(l), offset (0)
{
buf = new uint8_t[len];
}
~SendBuffer ()
{
delete[] buf;
@ -129,6 +134,7 @@ namespace stream
~SendBufferQueue () { CleanUp (); };
void Add (const uint8_t * buf, size_t len, SendHandler handler);
void Add (std::shared_ptr<SendBuffer> buf);
size_t Get (uint8_t * buf, size_t len);
size_t GetSize () const { return m_Size; };
bool IsEmpty () const { return m_Buffers.empty (); };

View File

@ -19,7 +19,7 @@
#include "I2PEndian.h"
#include "Timestamp.h"
#ifdef WIN32
#ifdef _WIN32
#ifndef _WIN64
#define _USE_32BIT_TIME_T
#endif

View File

@ -124,7 +124,7 @@ namespace tunnel
uint8_t nonce[12];
memset (nonce, 0, 12);
if (!i2p::crypto::AEADChaCha20Poly1305 (record, TUNNEL_BUILD_RECORD_SIZE - 16,
hop->h, 32, hop->ck, nonce, record, TUNNEL_BUILD_RECORD_SIZE - 16, false)) // decrypt
hop->m_H, 32, hop->m_CK, nonce, record, TUNNEL_BUILD_RECORD_SIZE - 16, false)) // decrypt
{
LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed");
return false;
@ -474,7 +474,7 @@ namespace tunnel
{
std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready
uint64_t lastTs = 0;
uint64_t lastTs = 0, lastPoolsTs = 0;
while (m_IsRunning)
{
try
@ -535,12 +535,20 @@ namespace tunnel
while (msg);
}
if (i2p::transport::transports.IsOnline())
{
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts - lastTs >= 15 && i2p::transport::transports.IsOnline()) // manage tunnels every 15 seconds
if (ts - lastTs >= 15) // manage tunnels every 15 seconds
{
ManageTunnels ();
lastTs = ts;
}
if (ts - lastPoolsTs >= 5) // manage pools every 5 seconds
{
ManageTunnelPools (ts);
lastPoolsTs = ts;
}
}
}
catch (std::exception& ex)
{
@ -582,7 +590,6 @@ namespace tunnel
ManageInboundTunnels ();
ManageOutboundTunnels ();
ManageTransitTunnels ();
ManageTunnelPools ();
}
void Tunnels::ManagePendingTunnels ()
@ -794,16 +801,13 @@ namespace tunnel
}
}
void Tunnels::ManageTunnelPools ()
void Tunnels::ManageTunnelPools (uint64_t ts)
{
std::unique_lock<std::mutex> l(m_PoolsMutex);
for (auto& pool : m_Pools)
{
if (pool && pool->IsActive ())
{
pool->CreateTunnels ();
pool->TestTunnels ();
}
pool->ManageTunnels (ts);
}
}

View File

@ -230,7 +230,7 @@ namespace tunnel
void ManagePendingTunnels ();
template<class PendingTunnels>
void ManagePendingTunnels (PendingTunnels& pendingTunnels);
void ManageTunnelPools ();
void ManageTunnelPools (uint64_t ts);
std::shared_ptr<ZeroHopsInboundTunnel> CreateZeroHopsInboundTunnel ();
std::shared_ptr<ZeroHopsOutboundTunnel> CreateZeroHopsOutboundTunnel ();

View File

@ -10,7 +10,6 @@
#include <memory>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "Crypto.h"
#include "Log.h"
#include "Transports.h"
#include "Timestamp.h"
@ -128,9 +127,7 @@ namespace tunnel
void TunnelHopConfig::EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor,
const uint8_t * plainText, uint8_t * encrypted, BN_CTX * ctx)
{
static const char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars
memcpy (ck, protocolName, 32); // ck = h = protocol_name || 0
SHA256 (ck, 32, h); // h = SHA256(h);
InitBuildRequestRecordNoiseState (*this);
uint8_t hepk[32];
encryptor->Encrypt (nullptr, hepk, nullptr, false);
MixHash (hepk, 32); // h = SHA256(h || hepk)
@ -140,13 +137,11 @@ namespace tunnel
encrypted += 32;
uint8_t sharedSecret[32];
ephemeralKeys->Agree (hepk, sharedSecret); // x25519(sesk, hepk)
uint8_t keydata[64];
i2p::crypto::HKDF (ck, sharedSecret, 32, "", keydata);
memcpy (ck, keydata, 32);
MixKey (sharedSecret);
uint8_t nonce[12];
memset (nonce, 0, 12);
if (!i2p::crypto::AEADChaCha20Poly1305 (plainText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, h, 32,
keydata + 32, nonce, encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16, true)) // encrypt
if (!i2p::crypto::AEADChaCha20Poly1305 (plainText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, m_H, 32,
m_CK + 32, nonce, encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16, true)) // encrypt
{
LogPrint (eLogWarning, "Tunnel: Plaintext AEAD encryption failed");
return;
@ -154,13 +149,16 @@ namespace tunnel
MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext)
}
void TunnelHopConfig::MixHash (const uint8_t * buf, size_t len)
void InitBuildRequestRecordNoiseState (i2p::crypto::NoiseSymmetricState& state)
{
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, h, 32);
SHA256_Update (&ctx, buf, len);
SHA256_Final (h, &ctx);
static const char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars
static const uint8_t hh[32] =
{
0x69, 0x4d, 0x52, 0x44, 0x5a, 0x27, 0xd9, 0xad, 0xfa, 0xd2, 0x9c, 0x76, 0x32, 0x39, 0x5d, 0xc1,
0xe4, 0x35, 0x4c, 0x69, 0xb4, 0xf9, 0x2e, 0xac, 0x8a, 0x1e, 0xe4, 0x6a, 0x9e, 0xd2, 0x15, 0x54
}; // SHA256 (protocol_name || 0)
memcpy (state.m_CK, protocolName, 32); // ck = h = protocol_name || 0
memcpy (state.m_H, hh, 32); // h = SHA256(h)
}
}
}

View File

@ -12,12 +12,13 @@
#include <vector>
#include "Identity.h"
#include "RouterContext.h"
#include "Crypto.h"
namespace i2p
{
namespace tunnel
{
struct TunnelHopConfig
struct TunnelHopConfig: public i2p::crypto::NoiseSymmetricState
{
std::shared_ptr<const i2p::data::IdentityEx> ident;
i2p::data::IdentHash nextIdent;
@ -30,7 +31,6 @@ namespace tunnel
TunnelHopConfig * next, * prev;
int recordIndex; // record # in tunnel build message
uint8_t ck[32], h[32]; // for ECIES
TunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r);
@ -39,13 +39,14 @@ namespace tunnel
void SetNext (TunnelHopConfig * n);
void SetPrev (TunnelHopConfig * p);
bool IsECIES () const { return ident->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET; };
bool IsECIES () const { return ident->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx);
void EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor,
const uint8_t * clearText, uint8_t * encrypted, BN_CTX * ctx);
void MixHash (const uint8_t * buf, size_t len);
};
void InitBuildRequestRecordNoiseState (i2p::crypto::NoiseSymmetricState& state);
class TunnelConfig
{
public:

View File

@ -26,9 +26,10 @@ namespace tunnel
{
TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels):
m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true),
m_CustomPeerSelector(nullptr)
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels),
m_IsActive (true), m_CustomPeerSelector(nullptr)
{
m_NextManageTime = i2p::util::GetSecondsSinceEpoch () + rand () % TUNNEL_POOL_MANAGE_INTERVAL;
}
TunnelPool::~TunnelPool ()
@ -118,7 +119,6 @@ namespace tunnel
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
m_OutboundTunnels.insert (createdTunnel);
}
//CreatePairedInboundTunnel (createdTunnel);
}
void TunnelPool::TunnelExpired (std::shared_ptr<OutboundTunnel> expiredTunnel)
@ -235,6 +235,15 @@ namespace tunnel
for (const auto& it : m_InboundTunnels)
if (it->IsEstablished ()) num++;
}
if (!num && !m_OutboundTunnels.empty ())
{
for (auto it: m_OutboundTunnels)
{
CreatePairedInboundTunnel (it);
num++;
if (num >= m_NumInboundTunnels) break;
}
}
for (int i = num; i < m_NumInboundTunnels; i++)
CreateInboundTunnel ();
@ -313,6 +322,16 @@ namespace tunnel
}
}
void TunnelPool::ManageTunnels (uint64_t ts)
{
if (ts > m_NextManageTime)
{
CreateTunnels ();
TestTunnels ();
m_NextManageTime = ts + TUNNEL_POOL_MANAGE_INTERVAL + (rand () % TUNNEL_POOL_MANAGE_INTERVAL)/2;
}
}
void TunnelPool::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
if (m_LocalDestination)
@ -342,18 +361,25 @@ namespace tunnel
}
if (found)
{
// restore from test failed state if any
if (test.first->GetState () == eTunnelStateTestFailed)
test.first->SetState (eTunnelStateEstablished);
if (test.second->GetState () == eTunnelStateTestFailed)
test.second->SetState (eTunnelStateEstablished);
uint64_t dlt = i2p::util::GetMillisecondsSinceEpoch () - timestamp;
LogPrint (eLogDebug, "Tunnels: test of ", msgID, " successful. ", dlt, " milliseconds");
// update latency
uint64_t latency = dlt / 2;
// restore from test failed state if any
if (test.first)
{
if (test.first->GetState () == eTunnelStateTestFailed)
test.first->SetState (eTunnelStateEstablished);
// update latency
test.first->AddLatencySample(latency);
}
if (test.second)
{
if (test.second->GetState () == eTunnelStateTestFailed)
test.second->SetState (eTunnelStateEstablished);
// update latency
test.second->AddLatencySample(latency);
}
}
else
{
if (m_LocalDestination)

View File

@ -27,6 +27,8 @@ namespace i2p
{
namespace tunnel
{
const int TUNNEL_POOL_MANAGE_INTERVAL = 10; // in seconds
class Tunnel;
class InboundTunnel;
class OutboundTunnel;
@ -69,6 +71,7 @@ namespace tunnel
std::shared_ptr<InboundTunnel> GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded = nullptr) const;
std::shared_ptr<OutboundTunnel> GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) const;
void TestTunnels ();
void ManageTunnels (uint64_t ts);
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg);
@ -123,6 +126,7 @@ namespace tunnel
mutable std::mutex m_TestsMutex;
std::map<uint32_t, std::pair<std::shared_ptr<OutboundTunnel>, std::shared_ptr<InboundTunnel> > > m_Tests;
bool m_IsActive;
uint64_t m_NextManageTime; // in seconds
std::mutex m_CustomPeerSelectorMutex;
ITunnelPeerSelector * m_CustomPeerSelector;

View File

@ -37,7 +37,10 @@ namespace api
i2p::fs::Init();
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
i2p::crypto::InitCrypto (precomputation);
bool aesni; i2p::config::GetOption("cpuext.aesni", aesni);
bool avx; i2p::config::GetOption("cpuext.avx", avx);
bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt);
i2p::crypto::InitCrypto (precomputation, aesni, avx, forceCpuExt);
int netID; i2p::config::GetOption("netid", netID);
i2p::context.SetNetID (netID);

View File

@ -13,7 +13,7 @@
#include "util.h"
#include "Log.h"
#ifdef WIN32
#ifdef _WIN32
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
@ -56,7 +56,7 @@ int inet_pton_xp(int af, const char *src, void *dst)
}
return 0;
}
#else /* !WIN32 => UNIX */
#else /* !_WIN32 => UNIX */
#include <sys/types.h>
#include <ifaddrs.h>
#endif
@ -109,7 +109,7 @@ namespace util
namespace net
{
#ifdef WIN32
#ifdef _WIN32
bool IsWindowsXPorLater()
{
static bool isRequested = false;
@ -333,13 +333,13 @@ namespace net
return mtu;
}
#endif // WIN32
#endif // _WIN32
int GetMTU(const boost::asio::ip::address& localAddress)
{
int fallback = localAddress.is_v6 () ? 1280 : 620; // fallback MTU
#ifdef WIN32
#ifdef _WIN32
return GetMTUWindows(localAddress, fallback);
#else
return GetMTUUnix(localAddress, fallback);
@ -349,7 +349,7 @@ namespace net
const boost::asio::ip::address GetInterfaceAddress(const std::string & ifname, bool ipv6)
{
#ifdef WIN32
#ifdef _WIN32
LogPrint(eLogError, "NetIface: cannot get address by interface name, not implemented on WIN32");
if(ipv6)
return boost::asio::ip::address::from_string("::1");

View File

@ -16,7 +16,7 @@
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
#define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 33
#define I2PD_VERSION_MINOR 35
#define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_PATCH 0
#define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO)
@ -30,7 +30,7 @@
#define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 47
#define I2P_VERSION_MICRO 48
#define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

View File

@ -454,17 +454,18 @@ namespace client
auto it = m_Addresses.find (name);
if (it != m_Addresses.end ()) // already exists ?
{
if (it->second->IsIdentHash () && it->second->identHash != ident->GetIdentHash ()) // address changed?
if (it->second->IsIdentHash () && it->second->identHash != ident->GetIdentHash () && // address changed?
ident->GetSigningKeyType () != i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) // don't replace by DSA
{
it->second->identHash = ident->GetIdentHash ();
m_Storage->AddAddress (ident);
m_Storage->RemoveAddress (it->second->identHash);
LogPrint (eLogInfo, "Addressbook: updated host: ", name);
}
}
else
{
//m_Addresses.emplace (name, std::make_shared<Address>(ident->GetIdentHash ()));
m_Addresses[name] = std::make_shared<Address>(ident->GetIdentHash ()); // for gcc 4.7
m_Addresses.emplace (name, std::make_shared<Address>(ident->GetIdentHash ()));
m_Storage->AddAddress (ident);
if (is_update)
LogPrint (eLogInfo, "Addressbook: added new host: ", name);

View File

@ -590,6 +590,7 @@ namespace client
localDestination = CreateNewMatchedTunnelDestination(k, dest, &options);
else
localDestination = CreateNewLocalDestination (k, type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT, &options);
if (keys != "transient")
destinations[keys] = localDestination;
}
}

View File

@ -30,6 +30,12 @@ namespace client
{
}
void I2CPDestination::Stop ()
{
LeaseSetDestination::Stop ();
m_Owner = nullptr;
}
void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key)
{
m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_Identity->GetCryptoKeyType (), key);
@ -46,7 +52,7 @@ namespace client
bool I2CPDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const
{
if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET && m_ECIESx25519Decryptor)
if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && m_ECIESx25519Decryptor)
return m_ECIESx25519Decryptor->Decrypt (encrypted, data, ctx, true);
if (m_Decryptor)
return m_Decryptor->Decrypt (encrypted, data, ctx, true);
@ -57,14 +63,14 @@ namespace client
const uint8_t * I2CPDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const
{
if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET && m_ECIESx25519Decryptor)
if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && m_ECIESx25519Decryptor)
return m_ECIESx25519Decryptor->GetPubicKey ();
return nullptr;
}
bool I2CPDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const
{
return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET ? (bool)m_ECIESx25519Decryptor : m_EncryptionKeyType == keyType;
return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool)m_ECIESx25519Decryptor : m_EncryptionKeyType == keyType;
}
@ -72,6 +78,7 @@ namespace client
{
uint32_t length = bufbe32toh (buf);
if (length > len - 4) length = len - 4;
if (m_Owner)
m_Owner->SendMessagePayloadMessage (buf + 4, length);
}
@ -82,10 +89,17 @@ namespace client
m_LeaseSetExpirationTime = ls.GetExpirationTime ();
uint8_t * leases = ls.GetLeases ();
leases[-1] = tunnels.size ();
htobe16buf (leases - 3, m_Owner->GetSessionID ());
if (m_Owner)
{
uint16_t sessionID = m_Owner->GetSessionID ();
if (sessionID != 0xFFFF)
{
htobe16buf (leases - 3, sessionID);
size_t l = 2/*sessionID*/ + 1/*num leases*/ + i2p::data::LEASE_SIZE*tunnels.size ();
m_Owner->SendI2CPMessage (I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE, leases - 3, l);
}
}
}
void I2CPDestination::LeaseSetCreated (const uint8_t * buf, size_t len)
{
@ -119,6 +133,7 @@ namespace client
[s, msg, remote, nonce]()
{
bool sent = s->SendMsg (msg, remote);
if (s->m_Owner)
s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
});
}
@ -130,9 +145,10 @@ namespace client
if (ls)
{
bool sent = s->SendMsg (msg, ls);
if (s->m_Owner)
s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
}
else
else if (s->m_Owner)
s->m_Owner->SendMessageStatusMessage (nonce, eI2CPMessageStatusNoLeaseSet);
});
}
@ -227,12 +243,13 @@ namespace client
I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<proto::socket> socket):
m_Owner (owner), m_Socket (socket), m_SessionID (0xFFFF),
m_MessageID (0), m_IsSendAccepted (true)
m_MessageID (0), m_IsSendAccepted (true), m_IsSending (false)
{
}
I2CPSession::~I2CPSession ()
{
Terminate ();
}
void I2CPSession::Start ()
@ -343,39 +360,75 @@ namespace client
m_Socket->close ();
m_Socket = nullptr;
}
if (!m_SendQueue.IsEmpty ())
m_SendQueue.CleanUp ();
if (m_SessionID != 0xFFFF)
{
m_Owner.RemoveSession (GetSessionID ());
LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " terminated");
m_SessionID = 0xFFFF;
}
}
void I2CPSession::SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len)
{
if (len > I2CP_MAX_MESSAGE_LENGTH)
auto l = len + I2CP_HEADER_SIZE;
if (l > I2CP_MAX_MESSAGE_LENGTH)
{
LogPrint (eLogError, "I2CP: Message to send is too long ", len);
LogPrint (eLogError, "I2CP: Message to send is too long ", l);
return;
}
auto socket = m_Socket;
if (socket)
{
auto l = len + I2CP_HEADER_SIZE;
uint8_t * buf = new uint8_t[l];
auto sendBuf = m_IsSending ? std::make_shared<i2p::stream::SendBuffer> (l) : nullptr;
uint8_t * buf = sendBuf ? sendBuf->buf : m_SendBuffer;
htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len);
buf[I2CP_HEADER_TYPE_OFFSET] = type;
memcpy (buf + I2CP_HEADER_SIZE, payload, len);
boost::asio::async_write (*socket, boost::asio::buffer (buf, l), boost::asio::transfer_all (),
std::bind(&I2CPSession::HandleI2CPMessageSent, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, buf));
if (sendBuf)
{
if (m_SendQueue.GetSize () < I2CP_MAX_SEND_QUEUE_SIZE)
m_SendQueue.Add (sendBuf);
else
{
LogPrint (eLogWarning, "I2CP: send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE);
return;
}
}
else
LogPrint (eLogError, "I2CP: Can't write to the socket");
{
auto socket = m_Socket;
if (socket)
{
m_IsSending = true;
boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, l),
boost::asio::transfer_all (), std::bind(&I2CPSession::HandleI2CPMessageSent,
shared_from_this (), std::placeholders::_1, std::placeholders::_2));
}
}
}
void I2CPSession::HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, const uint8_t * buf)
void I2CPSession::HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
delete[] buf;
if (ecode && ecode != boost::asio::error::operation_aborted)
if (ecode)
{
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else if (!m_SendQueue.IsEmpty ())
{
auto socket = m_Socket;
if (socket)
{
auto len = m_SendQueue.Get (m_SendBuffer, I2CP_MAX_MESSAGE_LENGTH);
boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, len),
boost::asio::transfer_all (),std::bind(&I2CPSession::HandleI2CPMessageSent,
shared_from_this (), std::placeholders::_1, std::placeholders::_2));
}
else
m_IsSending = false;
}
else
m_IsSending = false;
}
std::string I2CPSession::ExtractString (const uint8_t * buf, size_t len)
{
@ -492,11 +545,7 @@ namespace client
{
SendSessionStatusMessage (0); // destroy
LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " destroyed");
if (m_Destination)
{
m_Destination->Stop ();
m_Destination = 0;
}
Terminate ();
}
void I2CPSession::ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len)
@ -621,7 +670,7 @@ namespace client
uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption type
uint16_t keyLen = bufbe16toh (buf + offset); offset += 2; // private key length
if (offset + keyLen > len) return;
if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET)
if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
m_Destination->SetECIESx25519EncryptionPrivateKey (buf + offset);
else
{
@ -807,16 +856,40 @@ namespace client
{
// we don't use SendI2CPMessage to eliminate additional copy
auto l = len + 10 + I2CP_HEADER_SIZE;
uint8_t * buf = new uint8_t[l];
if (l > I2CP_MAX_MESSAGE_LENGTH)
{
LogPrint (eLogError, "I2CP: Message to send is too long ", l);
return;
}
auto sendBuf = m_IsSending ? std::make_shared<i2p::stream::SendBuffer> (l) : nullptr;
uint8_t * buf = sendBuf ? sendBuf->buf : m_SendBuffer;
htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len + 10);
buf[I2CP_HEADER_TYPE_OFFSET] = I2CP_MESSAGE_PAYLOAD_MESSAGE;
htobe16buf (buf + I2CP_HEADER_SIZE, m_SessionID);
htobe32buf (buf + I2CP_HEADER_SIZE + 2, m_MessageID++);
htobe32buf (buf + I2CP_HEADER_SIZE + 6, len);
memcpy (buf + I2CP_HEADER_SIZE + 10, payload, len);
boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, l), boost::asio::transfer_all (),
std::bind(&I2CPSession::HandleI2CPMessageSent, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, buf));
if (sendBuf)
{
if (m_SendQueue.GetSize () < I2CP_MAX_SEND_QUEUE_SIZE)
m_SendQueue.Add (sendBuf);
else
{
LogPrint (eLogWarning, "I2CP: send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE);
return;
}
}
else
{
auto socket = m_Socket;
if (socket)
{
m_IsSending = true;
boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, l),
boost::asio::transfer_all (), std::bind(&I2CPSession::HandleI2CPMessageSent,
shared_from_this (), std::placeholders::_1, std::placeholders::_2));
}
}
}
I2CPServer::I2CPServer (const std::string& interface, int port, bool isSingleThread):

View File

@ -17,6 +17,7 @@
#include <boost/asio.hpp>
#include "util.h"
#include "Destination.h"
#include "Streaming.h"
namespace i2p
{
@ -25,6 +26,7 @@ namespace client
const uint8_t I2CP_PROTOCOL_BYTE = 0x2A;
const size_t I2CP_SESSION_BUFFER_SIZE = 4096;
const size_t I2CP_MAX_MESSAGE_LENGTH = 65535;
const size_t I2CP_MAX_SEND_QUEUE_SIZE = 1024*1024; // in bytes, 1M
const size_t I2CP_HEADER_LENGTH_OFFSET = 0;
const size_t I2CP_HEADER_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4;
@ -71,6 +73,8 @@ namespace client
std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params);
~I2CPDestination () {};
void Stop ();
void SetEncryptionPrivateKey (const uint8_t * key);
void SetEncryptionType (i2p::data::CryptoKeyType keyType) { m_EncryptionKeyType = keyType; };
void SetECIESx25519EncryptionPrivateKey (const uint8_t * key);
@ -167,11 +171,11 @@ namespace client
void HandleMessage ();
void Terminate ();
void HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, const uint8_t * buf);
void HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
std::string ExtractString (const uint8_t * buf, size_t len);
size_t PutString (uint8_t * buf, size_t len, const std::string& str);
void ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping);
void SendSessionStatusMessage (uint8_t status);
void SendHostReplyMessage (uint32_t requestID, std::shared_ptr<const i2p::data::IdentityEx> identity);
@ -186,6 +190,11 @@ namespace client
uint16_t m_SessionID;
uint32_t m_MessageID;
bool m_IsSendAccepted;
// to client
bool m_IsSending;
uint8_t m_SendBuffer[I2CP_MAX_MESSAGE_LENGTH];
i2p::stream::SendBufferQueue m_SendQueue;
};
typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len);

View File

@ -54,6 +54,7 @@ namespace client
break;
}
case eSAMSocketTypeAcceptor:
case eSAMSocketTypeForward:
{
if (Session)
{
@ -198,7 +199,7 @@ namespace client
{
LogPrint (eLogDebug, "SAMSocket::SendMessageReply, close=",close?"true":"false", " reason: ", msg);
if (!m_IsSilent)
if (!m_IsSilent || m_SocketType == eSAMSocketTypeForward)
boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (),
std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, close));
@ -263,6 +264,8 @@ namespace client
ProcessStreamConnect (separator + 1, bytes_transferred - (separator - m_Buffer) - 1, bytes_transferred - (eol - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_STREAM_ACCEPT))
ProcessStreamAccept (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_STREAM_FORWARD))
ProcessStreamForward (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_DEST_GENERATE))
ProcessDestGenerate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP))
@ -358,12 +361,12 @@ namespace client
std::shared_ptr<boost::asio::ip::udp::endpoint> forward = nullptr;
if ((type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw) &&
params.find(SAM_VALUE_HOST) != params.end() && params.find(SAM_VALUE_PORT) != params.end())
params.find(SAM_PARAM_HOST) != params.end() && params.find(SAM_PARAM_PORT) != params.end())
{
// udp forward selected
boost::system::error_code e;
// TODO: support hostnames in udp forward
auto addr = boost::asio::ip::address::from_string(params[SAM_VALUE_HOST], e);
auto addr = boost::asio::ip::address::from_string(params[SAM_PARAM_HOST], e);
if (e)
{
// not an ip address
@ -371,7 +374,7 @@ namespace client
return;
}
auto port = std::stoi(params[SAM_VALUE_PORT]);
auto port = std::stoi(params[SAM_PARAM_PORT]);
if (port == -1)
{
SendI2PError("Invalid port");
@ -469,6 +472,11 @@ namespace client
void SAMSocket::ProcessStreamConnect (char * buf, size_t len, size_t rem)
{
LogPrint (eLogDebug, "SAM: stream connect: ", buf);
if ( m_SocketType != eSAMSocketTypeUnknown)
{
SendI2PError ("Socket already in use");
return;
}
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID];
@ -544,6 +552,11 @@ namespace client
void SAMSocket::ProcessStreamAccept (char * buf, size_t len)
{
LogPrint (eLogDebug, "SAM: stream accept: ", buf);
if ( m_SocketType != eSAMSocketTypeUnknown)
{
SendI2PError ("Socket already in use");
return;
}
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID];
@ -565,6 +578,53 @@ namespace client
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
}
void SAMSocket::ProcessStreamForward (char * buf, size_t len)
{
LogPrint (eLogDebug, "SAM: stream forward: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID];
auto session = m_Owner.FindSession (id);
if (!session)
{
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
return;
}
if (session->localDestination->IsAcceptingStreams ())
{
SendI2PError ("Already accepting");
return;
}
auto it = params.find (SAM_PARAM_PORT);
if (it == params.end ())
{
SendI2PError ("PORT is missing");
return;
}
auto port = std::stoi (it->second);
if (port <= 0 || port >= 0xFFFF)
{
SendI2PError ("Invalid PORT");
return;
}
boost::system::error_code ec;
auto ep = m_Socket.remote_endpoint (ec);
if (ec)
{
SendI2PError ("Socket error");
return;
}
ep.port (port);
m_SocketType = eSAMSocketTypeForward;
m_ID = id;
m_IsAccepting = true;
std::string& silent = params[SAM_PARAM_SILENT];
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PForward,
shared_from_this (), std::placeholders::_1, ep));
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data)
{
LogPrint (eLogDebug, "SAM: datagram send: ", buf, " ", len);
@ -917,6 +977,43 @@ namespace client
LogPrint (eLogWarning, "SAM: I2P acceptor has been reset");
}
void SAMSocket::HandleI2PForward (std::shared_ptr<i2p::stream::Stream> stream,
boost::asio::ip::tcp::endpoint ep)
{
if (stream)
{
LogPrint (eLogDebug, "SAM: incoming forward I2P connection for session ", m_ID);
auto newSocket = std::make_shared<SAMSocket>(m_Owner);
newSocket->SetSocketType (eSAMSocketTypeStream);
auto s = shared_from_this ();
newSocket->GetSocket ().async_connect (ep,
[s, newSocket, stream](const boost::system::error_code& ecode)
{
if (!ecode)
{
s->m_Owner.AddSocket (newSocket);
newSocket->Receive ();
newSocket->m_Stream = stream;
newSocket->m_ID = s->m_ID;
if (!s->m_IsSilent)
{
// get remote peer address
auto dest = stream->GetRemoteIdentity()->ToBase64 ();
memcpy (newSocket->m_StreamBuffer, dest.c_str (), dest.length ());
newSocket->m_StreamBuffer[dest.length ()] = '\n';
newSocket->HandleI2PReceive (boost::system::error_code (),dest.length () + 1); // we send identity like it has been received from stream
}
else
newSocket->I2PReceive ();
}
else
stream->AsyncClose ();
});
}
else
LogPrint (eLogWarning, "SAM: I2P forward acceptor has been reset");
}
void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
LogPrint (eLogDebug, "SAM: datagram received ", len);
@ -1072,6 +1169,12 @@ namespace client
std::placeholders::_1, newSocket));
}
void SAMBridge::AddSocket(std::shared_ptr<SAMSocket> socket)
{
std::unique_lock<std::mutex> lock(m_OpenSocketsMutex);
m_OpenSockets.push_back(socket);
}
void SAMBridge::RemoveSocket(const std::shared_ptr<SAMSocket> & socket)
{
std::unique_lock<std::mutex> lock(m_OpenSocketsMutex);
@ -1087,10 +1190,7 @@ namespace client
if (!ec)
{
LogPrint (eLogDebug, "SAM: new connection from ", ep);
{
std::unique_lock<std::mutex> l(m_OpenSocketsMutex);
m_OpenSockets.push_back(socket);
}
AddSocket (socket);
socket->ReceiveHandshake ();
}
else

View File

@ -48,6 +48,7 @@ namespace client
const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n";
const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n";
const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT";
const char SAM_STREAM_FORWARD[] = "STREAM FORWARD";
const char SAM_DATAGRAM_SEND[] = "DATAGRAM SEND";
const char SAM_RAW_SEND[] = "RAW SEND";
const char SAM_DEST_GENERATE[] = "DEST GENERATE";
@ -69,14 +70,14 @@ namespace client
const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE";
const char SAM_PARAM_CRYPTO_TYPE[] = "CRYPTO_TYPE";
const char SAM_PARAM_SIZE[] = "SIZE";
const char SAM_PARAM_HOST[] = "HOST";
const char SAM_PARAM_PORT[] = "PORT";
const char SAM_VALUE_TRANSIENT[] = "TRANSIENT";
const char SAM_VALUE_STREAM[] = "STREAM";
const char SAM_VALUE_DATAGRAM[] = "DATAGRAM";
const char SAM_VALUE_RAW[] = "RAW";
const char SAM_VALUE_TRUE[] = "true";
const char SAM_VALUE_FALSE[] = "false";
const char SAM_VALUE_HOST[] = "HOST";
const char SAM_VALUE_PORT[] = "PORT";
enum SAMSocketType
{
@ -84,6 +85,7 @@ namespace client
eSAMSocketTypeSession,
eSAMSocketTypeStream,
eSAMSocketTypeAcceptor,
eSAMSocketTypeForward,
eSAMSocketTypeTerminated
};
@ -121,6 +123,7 @@ namespace client
void I2PReceive ();
void HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleI2PAccept (std::shared_ptr<i2p::stream::Stream> stream);
void HandleI2PForward (std::shared_ptr<i2p::stream::Stream> stream, boost::asio::ip::tcp::endpoint ep);
void HandleWriteI2PData (const boost::system::error_code& ecode, size_t sz);
void HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void HandleI2PRawDatagramReceive (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
@ -128,6 +131,7 @@ namespace client
void ProcessSessionCreate (char * buf, size_t len);
void ProcessStreamConnect (char * buf, size_t len, size_t rem);
void ProcessStreamAccept (char * buf, size_t len);
void ProcessStreamForward (char * buf, size_t len);
void ProcessDestGenerate (char * buf, size_t len);
void ProcessNamingLookup (char * buf, size_t len);
void SendI2PError(const std::string & msg);
@ -205,6 +209,7 @@ namespace client
/** send raw data to remote endpoint from our UDP Socket */
void SendTo(const uint8_t * buf, size_t len, std::shared_ptr<boost::asio::ip::udp::endpoint> remote);
void AddSocket(std::shared_ptr<SAMSocket> socket);
void RemoveSocket(const std::shared_ptr<SAMSocket> & socket);
bool ResolveSignatureType (const std::string& name, i2p::data::SigningKeyType& type) const;

View File

@ -35,6 +35,8 @@
<translation type="qt" />
<releases>
<release version="2.35.0" date="2020-11-30" />
<release version="2.34.0" date="2020-10-27" />
<release version="2.33.0" date="2020-08-24" />
<release version="2.32.1" date="2020-06-02" />
<release version="2.32.0" date="2020-05-25" />

View File

@ -17,7 +17,7 @@ BEGIN
VALUE "FileDescription", "I2Pd Qt"
VALUE "FileVersion", I2PD_VERSION
VALUE "InternalName", "i2pd-qt"
VALUE "LegalCopyright", "Copyright (C) 2013-2018, The PurpleI2P Project"
VALUE "LegalCopyright", "Copyright (C) 2013-2020, The PurpleI2P Project"
VALUE "LegalTrademarks1", "Distributed under the BSD 3-Clause software license, see the accompanying file COPYING or https://opensource.org/licenses/BSD-3-Clause."
VALUE "OriginalFilename", "i2pd_qt.exe"
VALUE "ProductName", "i2pd-qt"

View File

@ -4,81 +4,19 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = i2pd_qt
TEMPLATE = app
QMAKE_CXXFLAGS *= -Wno-unused-parameter -Wno-maybe-uninitialized
QMAKE_CXXFLAGS *= -Wno-unused-parameter -Wno-maybe-uninitialized -Wno-deprecated-copy
CONFIG += strict_c++ c++11
DEFINES += USE_UPNP
CONFIG(debug, debug|release) {
message(Debug build)
DEFINES += DEBUG_WITH_DEFAULT_LOGGING
I2PDMAKE += DEBUG=yes
} else {
message(Release build)
I2PDMAKE += DEBUG=no
}
SOURCES += DaemonQT.cpp mainwindow.cpp \
../../libi2pd/api.cpp \
../../libi2pd/Base.cpp \
../../libi2pd/Blinding.cpp \
../../libi2pd/BloomFilter.cpp \
../../libi2pd/ChaCha20.cpp \
../../libi2pd/Config.cpp \
../../libi2pd/CPU.cpp \
../../libi2pd/Crypto.cpp \
../../libi2pd/CryptoKey.cpp \
../../libi2pd/Datagram.cpp \
../../libi2pd/Destination.cpp \
../../libi2pd/Ed25519.cpp \
../../libi2pd/Family.cpp \
../../libi2pd/FS.cpp \
../../libi2pd/Garlic.cpp \
../../libi2pd/Gost.cpp \
../../libi2pd/Gzip.cpp \
../../libi2pd/HTTP.cpp \
../../libi2pd/I2NPProtocol.cpp \
../../libi2pd/I2PEndian.cpp \
../../libi2pd/Identity.cpp \
../../libi2pd/LeaseSet.cpp \
../../libi2pd/Log.cpp \
../../libi2pd/NetDb.cpp \
../../libi2pd/NetDbRequests.cpp \
../../libi2pd/NTCP2.cpp \
../../libi2pd/Poly1305.cpp \
../../libi2pd/Profiling.cpp \
../../libi2pd/Reseed.cpp \
../../libi2pd/RouterContext.cpp \
../../libi2pd/RouterInfo.cpp \
../../libi2pd/Signature.cpp \
../../libi2pd/SSU.cpp \
../../libi2pd/SSUData.cpp \
../../libi2pd/SSUSession.cpp \
../../libi2pd/Streaming.cpp \
../../libi2pd/Timestamp.cpp \
../../libi2pd/TransitTunnel.cpp \
../../libi2pd/Transports.cpp \
../../libi2pd/Tunnel.cpp \
../../libi2pd/TunnelEndpoint.cpp \
../../libi2pd/TunnelGateway.cpp \
../../libi2pd/TunnelPool.cpp \
../../libi2pd/TunnelConfig.cpp \
../../libi2pd/util.cpp \
../../libi2pd/Elligator.cpp \
../../libi2pd/ECIESX25519AEADRatchetSession.cpp \
../../libi2pd_client/AddressBook.cpp \
../../libi2pd_client/BOB.cpp \
../../libi2pd_client/ClientContext.cpp \
../../libi2pd_client/HTTPProxy.cpp \
../../libi2pd_client/I2CP.cpp \
../../libi2pd_client/I2PService.cpp \
../../libi2pd_client/I2PTunnel.cpp \
../../libi2pd_client/MatchedDestination.cpp \
../../libi2pd_client/SAM.cpp \
../../libi2pd_client/SOCKS.cpp \
../../daemon/Daemon.cpp \
../../daemon/HTTPServer.cpp \
../../daemon/I2PControl.cpp \
../../daemon/i2pd.cpp \
../../daemon/UPnP.cpp \
ClientTunnelPane.cpp \
MainWindowItems.cpp \
ServerTunnelPane.cpp \
@ -93,77 +31,14 @@ SOURCES += DaemonQT.cpp mainwindow.cpp \
DelayedSaveManager.cpp \
Saver.cpp \
DelayedSaveManagerImpl.cpp \
SaverImpl.cpp
SaverImpl.cpp \
../../daemon/Daemon.cpp \
../../daemon/HTTPServer.cpp \
../../daemon/I2PControl.cpp \
../../daemon/i2pd.cpp \
../../daemon/UPnP.cpp
HEADERS += DaemonQT.h mainwindow.h \
../../libi2pd/api.h \
../../libi2pd/Base.h \
../../libi2pd/Blinding.h \
../../libi2pd/BloomFilter.h \
../../libi2pd/ChaCha20.h \
../../libi2pd/Config.h \
../../libi2pd/CPU.h \
../../libi2pd/Crypto.h \
../../libi2pd/CryptoKey.h \
../../libi2pd/Datagram.h \
../../libi2pd/Destination.h \
../../libi2pd/Ed25519.h \
../../libi2pd/Family.h \
../../libi2pd/FS.h \
../../libi2pd/Garlic.h \
../../libi2pd/Gost.h \
../../libi2pd/Gzip.h \
../../libi2pd/HTTP.h \
../../libi2pd/I2NPProtocol.h \
../../libi2pd/I2PEndian.h \
../../libi2pd/Identity.h \
../../libi2pd/LeaseSet.h \
../../libi2pd/LittleBigEndian.h \
../../libi2pd/Log.h \
../../libi2pd/NetDb.hpp \
../../libi2pd/NetDbRequests.h \
../../libi2pd/NTCP2.h \
../../libi2pd/Poly1305.h \
../../libi2pd/Profiling.h \
../../libi2pd/Queue.h \
../../libi2pd/Reseed.h \
../../libi2pd/RouterContext.h \
../../libi2pd/RouterInfo.h \
../../libi2pd/Signature.h \
../../libi2pd/Siphash.h \
../../libi2pd/SSU.h \
../../libi2pd/SSUData.h \
../../libi2pd/SSUSession.h \
../../libi2pd/Streaming.h \
../../libi2pd/Tag.h \
../../libi2pd/Timestamp.h \
../../libi2pd/TransitTunnel.h \
../../libi2pd/Transports.h \
../../libi2pd/TransportSession.h \
../../libi2pd/Tunnel.h \
../../libi2pd/TunnelBase.h \
../../libi2pd/TunnelConfig.h \
../../libi2pd/TunnelEndpoint.h \
../../libi2pd/TunnelGateway.h \
../../libi2pd/TunnelPool.h \
../../libi2pd/util.h \
../../libi2pd/version.h \
../../libi2pd/Elligator.h \
../../libi2pd/ECIESX25519AEADRatchetSession.h \
../../libi2pd_client/AddressBook.h \
../../libi2pd_client/BOB.h \
../../libi2pd_client/ClientContext.h \
../../libi2pd_client/HTTPProxy.h \
../../libi2pd_client/I2CP.h \
../../libi2pd_client/I2PService.h \
../../libi2pd_client/I2PTunnel.h \
../../libi2pd_client/MatchedDestination.h \
../../libi2pd_client/SAM.h \
../../libi2pd_client/SOCKS.h \
../../daemon/Daemon.h \
../../daemon/HTTPServer.h \
../../daemon/I2PControl.h \
../../daemon/UPnP.h \
ClientTunnelPane.h \
MainWindowItems.h \
ServerTunnelPane.h \
@ -180,7 +55,12 @@ HEADERS += DaemonQT.h mainwindow.h \
DelayedSaveManager.h \
Saver.h \
DelayedSaveManagerImpl.h \
SaverImpl.h
SaverImpl.h \
../../daemon/Daemon.h \
../../daemon/HTTPServer.h \
../../daemon/I2PControl.h \
../../daemon/UPnP.h
INCLUDEPATH += ../../libi2pd
INCLUDEPATH += ../../libi2pd_client
@ -193,7 +73,23 @@ FORMS += mainwindow.ui \
routercommandswidget.ui \
generalsettingswidget.ui
LIBS += -lz
LIBS += $$PWD/../../libi2pd.a $$PWD/../../libi2pdclient.a -lz
libi2pd.commands = @echo Building i2pd libraries
libi2pd.target = $$PWD/../../libi2pd.a
libi2pd.depends = i2pd FORCE
i2pd.commands = cd $$PWD/../../ && mkdir -p obj/libi2pd_client && CC=$$QMAKE_CC CXX=$$QMAKE_CXX $(MAKE) USE_UPNP=yes $$I2PDMAKE api_client
i2pd.target += $$PWD/../../libi2pdclient.a
i2pd.depends = FORCE
cleani2pd.commands = cd $$PWD/../../ && CC=$$QMAKE_CC CXX=$$QMAKE_CXX $(MAKE) clean
cleani2pd.depends = clean
PRE_TARGETDEPS += $$PWD/../../libi2pd.a $$PWD/../../libi2pdclient.a
QMAKE_EXTRA_TARGETS += cleani2pd i2pd libi2pd
CLEAN_DEPS += cleani2pd
macx {
message("using mac os x target")
@ -233,7 +129,9 @@ windows {
# linker's -s means "strip"
QMAKE_LFLAGS_RELEASE += -s
LIBS = -lminiupnpc \
LIBS = \
$$PWD/../../libi2pd.a $$PWD/../../libi2pdclient.a \
-lminiupnpc \
-lboost_system$$BOOST_SUFFIX \
-lboost_date_time$$BOOST_SUFFIX \
-lboost_filesystem$$BOOST_SUFFIX \