diff --git a/.gitignore b/.gitignore index 353d839f..c2db70e0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ netDb /i2pd /libi2pd.a /libi2pdclient.a -i2pd.exe +*.exe # Autotools diff --git a/ChangeLog b/ChangeLog index 54989d44..59a19a3c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -68,7 +68,7 @@ - NTCP soft and hard descriptors limits - Support full timestamps in logs ### Changed -- Faster implmentation of GOST R 34.11 hash +- Faster implementation of GOST R 34.11 hash - Reject routers with RSA signtures - Reload config and shudown from Windows GUI - Update tunnels address(destination) without restart @@ -168,7 +168,7 @@ - Initial iOS support ### Changed -- Reduced file descriptiors usage +- Reduced file descriptors usage - Strict reseed checks enabled by default ## Fixed diff --git a/Makefile b/Makefile index 6d56ec7d..c51018f9 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,14 @@ USE_AVX := yes USE_STATIC := no USE_MESHNET := no USE_UPNP := no +DEBUG := yes + +ifeq ($(DEBUG),yes) + CXX_DEBUG = -g +else + CXX_DEBUG = -Os + LD_DEBUG = -s +endif ifeq ($(WEBSOCKETS),1) NEEDED_CXXFLAGS += -DWITH_EVENTS diff --git a/Makefile.bsd b/Makefile.bsd index f2293540..39e5651a 100644 --- a/Makefile.bsd +++ b/Makefile.bsd @@ -1,5 +1,5 @@ CXX = clang++ -CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation +CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation ## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time ## **without** overwriting the CXXFLAGS which we need in order to build. ## For example, when adding 'hardening flags' to the build @@ -8,5 +8,5 @@ CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-ind ## custom FLAGS to work at build-time. NEEDED_CXXFLAGS = -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1 INCFLAGS = -I/usr/include/ -I/usr/local/include/ -LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib +LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread diff --git a/Makefile.homebrew b/Makefile.homebrew index a6e645ee..688fdaea 100644 --- a/Makefile.homebrew +++ b/Makefile.homebrew @@ -3,8 +3,9 @@ BREWROOT = /usr/local BOOSTROOT = ${BREWROOT}/opt/boost SSLROOT = ${BREWROOT}/opt/libressl UPNPROOT = ${BREWROOT}/opt/miniupnpc -CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX -Wno-overloaded-virtual +CXXFLAGS = ${CXX_DEBUG} -Wall -std=c++11 -DMAC_OSX -Wno-overloaded-virtual INCFLAGS = -I${SSLROOT}/include -I${BOOSTROOT}/include +LDFLAGS = ${LD_DEBUG} ifndef TRAVIS CXX = clang++ @@ -13,7 +14,7 @@ endif ifeq ($(USE_STATIC),yes) LDLIBS = -lz ${SSLROOT}/lib/libcrypto.a ${SSLROOT}/lib/libssl.a ${BOOSTROOT}/lib/libboost_system.a ${BOOSTROOT}/lib/libboost_date_time.a ${BOOSTROOT}/lib/libboost_filesystem.a ${BOOSTROOT}/lib/libboost_program_options.a -lpthread else - LDFLAGS = -L${SSLROOT}/lib -L${BOOSTROOT}/lib + LDFLAGS += -L${SSLROOT}/lib -L${BOOSTROOT}/lib LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread endif @@ -34,7 +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 -DAESNI + CXXFLAGS += -maes endif ifeq ($(USE_AVX),1) CXXFLAGS += -mavx diff --git a/Makefile.linux b/Makefile.linux index cf045eb4..b9a740ad 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -1,13 +1,13 @@ # set defaults instead redefine -CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation -INCFLAGS ?= +CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation +LDFLAGS ?= ${LD_DEBUG} ## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time ## **without** overwriting the CXXFLAGS which we need in order to build. ## For example, when adding 'hardening flags' to the build ## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove ## -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. +## custom FDLAGS to work at build-time. # detect proper flag for c++11 support by compilers CXXVER := $(shell $(CXX) -dumpversion) @@ -64,7 +64,7 @@ ifneq ($(shell $(GREP) -c aes /proc/cpuinfo),0) ifeq ($(machine), aarch64) CXXFLAGS += -DARM64AES else - CPU_FLAGS += -maes -DAESNI + CPU_FLAGS += -maes endif endif endif diff --git a/Makefile.mingw b/Makefile.mingw index b40d0ada..5f3292d1 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -1,54 +1,54 @@ -USE_WIN32_APP=yes -CXX = g++ -WINDRES = windres -CXXFLAGS = -Os -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN -NEEDED_CXXFLAGS = -std=c++11 -BOOST_SUFFIX = -mt -INCFLAGS = -Idaemon -I. -LDFLAGS = -s -Wl,-rpath,/usr/local/lib -Wl,-Bstatic -static-libgcc -static-libstdc++ - -# UPNP Support -ifeq ($(USE_UPNP),yes) - CXXFLAGS += -DUSE_UPNP -DMINIUPNP_STATICLIB - LDLIBS = -lminiupnpc -endif - -LDLIBS += \ - -lboost_system$(BOOST_SUFFIX) \ - -lboost_date_time$(BOOST_SUFFIX) \ - -lboost_filesystem$(BOOST_SUFFIX) \ - -lboost_program_options$(BOOST_SUFFIX) \ - -lssl \ - -lcrypto \ - -lz \ - -lwsock32 \ - -lws2_32 \ - -lgdi32 \ - -liphlpapi \ - -lstdc++ \ - -lpthread - -ifeq ($(USE_WIN32_APP), yes) - CXXFLAGS += -DWIN32_APP - LDFLAGS += -mwindows - DAEMON_RC += Win32/Resource.rc - DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC)) -endif - -# don't change following line to ifeq ($(USE_AESNI),yes) !!! -ifeq ($(USE_AESNI),1) - CPU_FLAGS += -maes -DAESNI -else - CPU_FLAGS += -msse -endif - -ifeq ($(USE_AVX),1) - CPU_FLAGS += -mavx -endif - -ifeq ($(USE_ASLR),yes) - LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols -endif - -obj/%.o : %.rc - $(WINDRES) -i $< -o $@ +USE_WIN32_APP=yes +CXX = g++ +WINDRES = windres +CXXFLAGS := ${CXX_DEBUG} -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN +NEEDED_CXXFLAGS = -std=c++11 +BOOST_SUFFIX = -mt +INCFLAGS = -Idaemon -I. +LDFLAGS := ${LD_DEBUG} -Wl,-Bstatic -static-libgcc -static-libstdc++ + +# UPNP Support +ifeq ($(USE_UPNP),yes) + CXXFLAGS += -DUSE_UPNP -DMINIUPNP_STATICLIB + LDLIBS = -lminiupnpc +endif + +LDLIBS += \ + -lboost_system$(BOOST_SUFFIX) \ + -lboost_date_time$(BOOST_SUFFIX) \ + -lboost_filesystem$(BOOST_SUFFIX) \ + -lboost_program_options$(BOOST_SUFFIX) \ + -lssl \ + -lcrypto \ + -lz \ + -lwsock32 \ + -lws2_32 \ + -lgdi32 \ + -liphlpapi \ + -lstdc++ \ + -lpthread + +ifeq ($(USE_WIN32_APP), yes) + CXXFLAGS += -DWIN32_APP + LDFLAGS += -mwindows + DAEMON_RC += Win32/Resource.rc + DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC)) +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 +endif + +ifeq ($(USE_ASLR),yes) + LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols +endif + +obj/%.o : %.rc + $(WINDRES) -i $< -o $@ diff --git a/Makefile.osx b/Makefile.osx index 8bbf37f0..3358cb10 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -1,8 +1,7 @@ CXX = clang++ -CXXFLAGS = -Os -Wall -std=c++11 -DMAC_OSX -#CXXFLAGS = -g -O2 -Wall -std=c++11 +CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++11 -DMAC_OSX INCFLAGS = -I/usr/local/include -LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib +LDFLAGS := ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib ifeq ($(USE_STATIC),yes) LDLIBS = -lz /usr/local/lib/libcrypto.a /usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread @@ -21,7 +20,7 @@ ifeq ($(USE_UPNP),yes) endif ifeq ($(USE_AESNI),1) - CXXFLAGS += -maes -DAESNI + CXXFLAGS += -maes else CXXFLAGS += -msse endif diff --git a/android/.gitignore b/android/.gitignore index 90cd315e..6e42311a 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -1,12 +1,15 @@ gen tests +bin +libs +log* +obj +.gradle .idea +.externalNativeBuild ant.properties local.properties build.sh -bin -log* -.gradle android.iml build gradle diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index ca66c17d..705cce4e 100755 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -7,7 +7,7 @@ + android:targetSdkVersion="28" /> diff --git a/android/assets/certificates b/android/assets/certificates new file mode 120000 index 00000000..01b21e5d --- /dev/null +++ b/android/assets/certificates @@ -0,0 +1 @@ +../../contrib/certificates \ No newline at end of file diff --git a/android/assets/i2pd.conf b/android/assets/i2pd.conf new file mode 100644 index 00000000..8022b3d9 --- /dev/null +++ b/android/assets/i2pd.conf @@ -0,0 +1,78 @@ +## Configuration file for a typical i2pd user +## See https://i2pd.readthedocs.org/en/latest/configuration.html +## for more options you can use in this file. + +#logfile = /sdcard/i2pd/i2pd.log +loglevel = none + +# host = 1.2.3.4 +# port = 4567 + +ipv4 = true +ipv6 = false + +# ntcp = true +# ntcpproxy = http://127.0.0.1:8118 +# ssu = true + +bandwidth = O +# share = 100 + +# notransit = true +# floodfill = true + +[http] +enabled = true +address = 127.0.0.1 +port = 7070 +# auth = true +# user = i2pd +# pass = changeme + +[httpproxy] +enabled = true +address = 127.0.0.1 +port = 4444 +# keys = http-proxy-keys.dat +# addresshelper = true +# outproxy = http://false.i2p +## httpproxy section also accepts I2CP parameters, like "inbound.length" etc. + +[socksproxy] +enabled = true +address = 127.0.0.1 +port = 4447 +# keys = socks-proxy-keys.dat +# outproxy.enabled = false +# outproxy = 127.0.0.1 +# outproxyport = 9050 +## socksproxy section also accepts I2CP parameters, like "inbound.length" etc. + +[sam] +enabled = false +# address = 127.0.0.1 +# port = 7656 + +[precomputation] +elgamal = true + +[upnp] +enabled = true +# name = I2Pd + +[reseed] +verify = true +## Path to local reseed data file (.su3) for manual reseeding +# file = /path/to/i2pseeds.su3 +## or HTTPS URL to reseed from +# file = https://legit-website.com/i2pseeds.su3 +## Path to local ZIP file or HTTPS URL to reseed from +# zipfile = /path/to/netDb.zip +## If you run i2pd behind a proxy server, set proxy server for reseeding here +## Should be http://address:port or socks://address:port +# proxy = http://127.0.0.1:8118 +## Minimum number of known routers, below which i2pd triggers reseeding. 25 by default +# threshold = 25 + +[limits] +transittunnels = 50 diff --git a/android/assets/subscriptions.txt b/android/assets/subscriptions.txt new file mode 100644 index 00000000..8f4afb03 --- /dev/null +++ b/android/assets/subscriptions.txt @@ -0,0 +1,3 @@ +http://inr.i2p/export/alive-hosts.txt +http://stats.i2p/cgi-bin/newhosts.txt +http://i2p-projekt.i2p/hosts.txt diff --git a/android/assets/tunnels.conf b/android/assets/tunnels.conf new file mode 100644 index 00000000..e95fdf2e --- /dev/null +++ b/android/assets/tunnels.conf @@ -0,0 +1,33 @@ +[IRC-IRC2P] +#type = client +#address = 127.0.0.1 +#port = 6668 +#destination = irc.postman.i2p +#destinationport = 6667 +#keys = irc-keys.dat + +#[IRC-ILITA] +#type = client +#address = 127.0.0.1 +#port = 6669 +#destination = irc.ilita.i2p +#destinationport = 6667 +#keys = irc-keys.dat + +#[SMTP] +#type = client +#address = 127.0.0.1 +#port = 7659 +#destination = smtp.postman.i2p +#destinationport = 25 +#keys = smtp-keys.dat + +#[POP3] +#type = client +#address = 127.0.0.1 +#port = 7660 +#destination = pop.postman.i2p +#destinationport = 110 +#keys = pop3-keys.dat + +# see more examples at https://i2pd.readthedocs.io/en/latest/user-guide/tunnels/ diff --git a/android/build.gradle b/android/build.gradle index c5834e19..2a8ad6f4 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -18,17 +18,22 @@ repositories { } android { - compileSdkVersion 25 - buildToolsVersion "25.0.3" + compileSdkVersion 28 + buildToolsVersion "28.0.1" defaultConfig { applicationId "org.purplei2p.i2pd" - targetSdkVersion 25 + targetSdkVersion 28 minSdkVersion 14 versionCode 1 versionName "2.19.0" ndk { abiFilters 'armeabi-v7a' - //abiFilters 'x86' + abiFilters 'x86' + } + externalNativeBuild { + ndkBuild { + arguments "-j4" + } } } sourceSets { @@ -37,6 +42,7 @@ android { java.srcDirs = ['src'] res.srcDirs = ['res'] jniLibs.srcDirs = ['libs'] + assets.srcDirs = ['assets'] } } signingConfigs { diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 00000000..af82e006 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1 @@ +org.gradle.parallel=true \ No newline at end of file diff --git a/android/project.properties b/android/project.properties index f72b9716..919ca9c3 100644 --- a/android/project.properties +++ b/android/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-25 +target=android-28 diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 00000000..2a620ef9 --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "i2pd" diff --git a/android/src/org/purplei2p/i2pd/I2PDActivity.java b/android/src/org/purplei2p/i2pd/I2PDActivity.java index 99672eb7..28e30251 100755 --- a/android/src/org/purplei2p/i2pd/I2PDActivity.java +++ b/android/src/org/purplei2p/i2pd/I2PDActivity.java @@ -1,5 +1,10 @@ package org.purplei2p.i2pd; +import java.io.File; +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; @@ -10,7 +15,9 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.res.AssetManager; import android.os.Bundle; +import android.os.Environment; import android.os.IBinder; import android.util.Log; import android.view.Menu; @@ -19,24 +26,24 @@ import android.widget.TextView; import android.widget.Toast; public class I2PDActivity extends Activity { - private static final String TAG = "i2pdActvt"; - public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000; - - private TextView textView; - + private static final String TAG = "i2pdActvt"; + public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000; + + private TextView textView; + private static final DaemonSingleton daemon = DaemonSingleton.getInstance(); - + private final DaemonSingleton.StateUpdateListener daemonStateUpdatedListener = - new DaemonSingleton.StateUpdateListener() { - + new DaemonSingleton.StateUpdateListener() { + @Override public void daemonStateUpdate() { runOnUiThread(new Runnable(){ - + @Override public void run() { try { - if(textView==null)return; + if(textView==null) return; Throwable tr = daemon.getLastThrowable(); if(tr!=null) { textView.setText(throwableToString(tr)); @@ -44,242 +51,309 @@ public class I2PDActivity extends Activity { } DaemonSingleton.State state = daemon.getState(); textView.setText( - String.valueOf(state)+ - (DaemonSingleton.State.startFailed.equals(state)?": "+daemon.getDaemonStartResult():"")+ - (DaemonSingleton.State.gracefulShutdownInProgress.equals(state)?": "+formatGraceTimeRemaining()+" "+getText(R.string.remaining):"") - ); - } catch (Throwable tr) { + String.valueOf(state)+ + (DaemonSingleton.State.startFailed.equals(state)?": "+daemon.getDaemonStartResult():"")+ + (DaemonSingleton.State.gracefulShutdownInProgress.equals(state)?": "+formatGraceTimeRemaining()+" "+getText(R.string.remaining):"") + ); + } catch (Throwable tr) { Log.e(TAG,"error ignored",tr); } } }); } }; - private static volatile long graceStartedMillis; - private static final Object graceStartedMillis_LOCK=new Object(); - - private static String formatGraceTimeRemaining() { - long remainingSeconds; - synchronized (graceStartedMillis_LOCK){ - remainingSeconds=Math.round(Math.max(0,graceStartedMillis+GRACEFUL_DELAY_MILLIS-System.currentTimeMillis())/1000.0D); - } - long remainingMinutes=(long)Math.floor(remainingSeconds/60.0D); - long remSec=remainingSeconds-remainingMinutes*60; - return remainingMinutes+":"+(remSec/10)+remSec%10; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - textView = new TextView(this); - setContentView(textView); - daemon.addStateChangeListener(daemonStateUpdatedListener); - daemonStateUpdatedListener.daemonStateUpdate(); - - //set the app be foreground - doBindService(); - - final Timer gracefulQuitTimer = getGracefulQuitTimer(); - if(gracefulQuitTimer!=null){ - long gracefulStopAtMillis; - synchronized (graceStartedMillis_LOCK) { - gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS; - } - rescheduleGraceStop(gracefulQuitTimer, gracefulStopAtMillis); - } - } - - @Override + private static volatile long graceStartedMillis; + private static final Object graceStartedMillis_LOCK=new Object(); + + private static String formatGraceTimeRemaining() { + long remainingSeconds; + synchronized (graceStartedMillis_LOCK){ + remainingSeconds=Math.round(Math.max(0,graceStartedMillis+GRACEFUL_DELAY_MILLIS-System.currentTimeMillis())/1000.0D); + } + long remainingMinutes=(long)Math.floor(remainingSeconds/60.0D); + long remSec=remainingSeconds-remainingMinutes*60; + return remainingMinutes+":"+(remSec/10)+remSec%10; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // copy assets + copyAsset("certificates"); + copyAsset("i2pd.conf"); + copyAsset("subsciptions.txt"); + copyAsset("tunnels.conf"); + + textView = new TextView(this); + setContentView(textView); + daemon.addStateChangeListener(daemonStateUpdatedListener); + daemonStateUpdatedListener.daemonStateUpdate(); + + // set the app be foreground + doBindService(); + + final Timer gracefulQuitTimer = getGracefulQuitTimer(); + if(gracefulQuitTimer!=null){ + long gracefulStopAtMillis; + synchronized (graceStartedMillis_LOCK) { + gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS; + } + rescheduleGraceStop(gracefulQuitTimer, gracefulStopAtMillis); + } + } + + @Override protected void onDestroy() { super.onDestroy(); - textView = null; - daemon.removeStateChangeListener(daemonStateUpdatedListener); - //cancelGracefulStop(); + textView = null; + daemon.removeStateChangeListener(daemonStateUpdatedListener); + //cancelGracefulStop(); try{ - doUnbindService(); - }catch(Throwable tr){ + doUnbindService(); + }catch(Throwable tr){ Log.e(TAG, "", tr); } } - - private static void cancelGracefulStop() { - Timer gracefulQuitTimer = getGracefulQuitTimer(); - if(gracefulQuitTimer!=null) { - gracefulQuitTimer.cancel(); - setGracefulQuitTimer(null); - } - } - - private CharSequence throwableToString(Throwable tr) { - StringWriter sw = new StringWriter(8192); - PrintWriter pw = new PrintWriter(sw); - tr.printStackTrace(pw); - pw.close(); - return sw.toString(); + + private static void cancelGracefulStop() { + Timer gracefulQuitTimer = getGracefulQuitTimer(); + if(gracefulQuitTimer!=null) { + gracefulQuitTimer.cancel(); + setGracefulQuitTimer(null); + } } - -// private LocalService mBoundService; - - private ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - // This is called when the connection with the service has been - // established, giving us the service object we can use to - // interact with the service. Because we have bound to a explicit - // service that we know is running in our own process, we can - // cast its IBinder to a concrete class and directly access it. -// mBoundService = ((LocalService.LocalBinder)service).getService(); - - // Tell the user about this for our demo. -// Toast.makeText(Binding.this, R.string.local_service_connected, -// Toast.LENGTH_SHORT).show(); - } - - public void onServiceDisconnected(ComponentName className) { - // This is called when the connection with the service has been - // unexpectedly disconnected -- that is, its process crashed. - // Because it is running in our same process, we should never - // see this happen. -// mBoundService = null; -// Toast.makeText(Binding.this, R.string.local_service_disconnected, -// Toast.LENGTH_SHORT).show(); - } - }; - - - private static volatile boolean mIsBound; - - private void doBindService() { - synchronized (I2PDActivity.class) { - if (mIsBound) return; - // Establish a connection with the service. We use an explicit - // class name because we want a specific service implementation that - // we know will be running in our own process (and thus won't be - // supporting component replacement by other applications). - bindService(new Intent(this, ForegroundService.class), mConnection, Context.BIND_AUTO_CREATE); - mIsBound = true; - } - } - - private void doUnbindService() { - synchronized (I2PDActivity.class) { - if (mIsBound) { - // Detach our existing connection. - unbindService(mConnection); - mIsBound = false; - } - } - } - + + private CharSequence throwableToString(Throwable tr) { + StringWriter sw = new StringWriter(8192); + PrintWriter pw = new PrintWriter(sw); + tr.printStackTrace(pw); + pw.close(); + return sw.toString(); + } + + // private LocalService mBoundService; + + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + // This is called when the connection with the service has been + // established, giving us the service object we can use to + // interact with the service. Because we have bound to a explicit + // service that we know is running in our own process, we can + // cast its IBinder to a concrete class and directly access it. + // mBoundService = ((LocalService.LocalBinder)service).getService(); + + // Tell the user about this for our demo. + // Toast.makeText(Binding.this, R.string.local_service_connected, + // Toast.LENGTH_SHORT).show(); + } + + public void onServiceDisconnected(ComponentName className) { + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + // Because it is running in our same process, we should never + // see this happen. + // mBoundService = null; + // Toast.makeText(Binding.this, R.string.local_service_disconnected, + // Toast.LENGTH_SHORT).show(); + } + }; + + + private static volatile boolean mIsBound; + + private void doBindService() { + synchronized (I2PDActivity.class) { + if (mIsBound) return; + // Establish a connection with the service. We use an explicit + // class name because we want a specific service implementation that + // we know will be running in our own process (and thus won't be + // supporting component replacement by other applications). + bindService(new Intent(this, ForegroundService.class), mConnection, Context.BIND_AUTO_CREATE); + mIsBound = true; + } + } + + private void doUnbindService() { + synchronized (I2PDActivity.class) { + if (mIsBound) { + // Detach our existing connection. + unbindService(mConnection); + mIsBound = false; + } + } + } + @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.options_main, menu); return true; } - + @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); - + switch(id){ - case R.id.action_stop: - i2pdStop(); - return true; - case R.id.action_graceful_stop: - i2pdGracefulStop(); - return true; - } - + case R.id.action_stop: + i2pdStop(); + return true; + case R.id.action_graceful_stop: + i2pdGracefulStop(); + return true; + } + return super.onOptionsItemSelected(item); } - + private void i2pdStop() { - cancelGracefulStop(); - new Thread(new Runnable(){ - - @Override - public void run() { - Log.d(TAG, "stopping"); - try{ - daemon.stopDaemon(); - }catch (Throwable tr) { - Log.e(TAG, "", tr); - } - } - - },"stop").start(); - } - - private static volatile Timer gracefulQuitTimer; - - private void i2pdGracefulStop() { - if(daemon.getState()==DaemonSingleton.State.stopped){ - Toast.makeText(this, R.string.already_stopped, - Toast.LENGTH_SHORT).show(); - return; - } - if(getGracefulQuitTimer()!=null){ - Toast.makeText(this, R.string.graceful_stop_is_already_in_progress, - Toast.LENGTH_SHORT).show(); - return; - } - Toast.makeText(this, R.string.graceful_stop_is_in_progress, - Toast.LENGTH_SHORT).show(); - new Thread(new Runnable(){ - + cancelGracefulStop(); + new Thread(new Runnable(){ + + @Override + public void run() { + Log.d(TAG, "stopping"); + try{ + daemon.stopDaemon(); + }catch (Throwable tr) { + Log.e(TAG, "", tr); + } + } + + },"stop").start(); + } + + private static volatile Timer gracefulQuitTimer; + + private void i2pdGracefulStop() { + if(daemon.getState()==DaemonSingleton.State.stopped){ + Toast.makeText(this, R.string.already_stopped, + Toast.LENGTH_SHORT).show(); + return; + } + if(getGracefulQuitTimer()!=null){ + Toast.makeText(this, R.string.graceful_stop_is_already_in_progress, + Toast.LENGTH_SHORT).show(); + return; + } + Toast.makeText(this, R.string.graceful_stop_is_in_progress, + Toast.LENGTH_SHORT).show(); + new Thread(new Runnable(){ + @Override public void run() { try{ Log.d(TAG, "grac stopping"); - if(daemon.isStartedOkay()) { - daemon.stopAcceptingTunnels(); - long gracefulStopAtMillis; - synchronized (graceStartedMillis_LOCK) { - graceStartedMillis = System.currentTimeMillis(); - gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS; - } - rescheduleGraceStop(null,gracefulStopAtMillis); - }else{ - i2pdStop(); - } - } catch(Throwable tr) { + if(daemon.isStartedOkay()) { + daemon.stopAcceptingTunnels(); + long gracefulStopAtMillis; + synchronized (graceStartedMillis_LOCK) { + graceStartedMillis = System.currentTimeMillis(); + gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS; + } + rescheduleGraceStop(null,gracefulStopAtMillis); + }else{ + i2pdStop(); + } + } catch(Throwable tr) { Log.e(TAG,"",tr); } } - - },"gracInit").start(); - } - - private void rescheduleGraceStop(Timer gracefulQuitTimerOld, long gracefulStopAtMillis) { - if(gracefulQuitTimerOld!=null)gracefulQuitTimerOld.cancel(); - final Timer gracefulQuitTimer = new Timer(true); - setGracefulQuitTimer(gracefulQuitTimer); - gracefulQuitTimer.schedule(new TimerTask(){ - - @Override - public void run() { - i2pdStop(); - } - - }, Math.max(0,gracefulStopAtMillis-System.currentTimeMillis())); - final TimerTask tickerTask = new TimerTask() { - @Override - public void run() { - daemonStateUpdatedListener.daemonStateUpdate(); - } - }; - gracefulQuitTimer.scheduleAtFixedRate(tickerTask,0/*start delay*/,1000/*millis period*/); - } - - private static Timer getGracefulQuitTimer() { - return gracefulQuitTimer; + + },"gracInit").start(); } - + + private void rescheduleGraceStop(Timer gracefulQuitTimerOld, long gracefulStopAtMillis) { + if(gracefulQuitTimerOld!=null)gracefulQuitTimerOld.cancel(); + final Timer gracefulQuitTimer = new Timer(true); + setGracefulQuitTimer(gracefulQuitTimer); + gracefulQuitTimer.schedule(new TimerTask(){ + + @Override + public void run() { + i2pdStop(); + } + + }, Math.max(0,gracefulStopAtMillis-System.currentTimeMillis())); + final TimerTask tickerTask = new TimerTask() { + @Override + public void run() { + daemonStateUpdatedListener.daemonStateUpdate(); + } + }; + gracefulQuitTimer.scheduleAtFixedRate(tickerTask,0/*start delay*/,1000/*millis period*/); + } + + private static Timer getGracefulQuitTimer() { + return gracefulQuitTimer; + } + private static void setGracefulQuitTimer(Timer gracefulQuitTimer) { - I2PDActivity.gracefulQuitTimer = gracefulQuitTimer; + I2PDActivity.gracefulQuitTimer = gracefulQuitTimer; + } + + /** + * 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) + throw new IOException(); + + // Make the directory. + File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd/", path); + dir.mkdirs(); + + // Recurse on the contents. + for (String entry : contents) { + copyAsset(path + "/" + entry); + } + } catch (IOException e) { + copyFileAsset(path); + } + } + + /** + * 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(Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd/", path); + try { + InputStream in = getAssets().open(path); + 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); + } + out.close(); + in.close(); + } catch (IOException e) { + Log.e(TAG, "", e); + } } } diff --git a/android_binary_only/.gitignore b/android_binary_only/.gitignore new file mode 100644 index 00000000..6e42311a --- /dev/null +++ b/android_binary_only/.gitignore @@ -0,0 +1,18 @@ +gen +tests +bin +libs +log* +obj +.gradle +.idea +.externalNativeBuild +ant.properties +local.properties +build.sh +android.iml +build +gradle +gradlew +gradlew.bat + diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 632edc03..5876a0f7 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -39,7 +39,7 @@ include_directories(${LIBI2PD_CLIENT_SRC_DIR}) set (LIBI2PD_SRC "${LIBI2PD_SRC_DIR}/BloomFilter.cpp" "${LIBI2PD_SRC_DIR}/Config.cpp" - "${LIBI2PD_SRC_DIR}/CPU.cpp" + "${LIBI2PD_SRC_DIR}/CPU.cpp" "${LIBI2PD_SRC_DIR}/Crypto.cpp" "${LIBI2PD_SRC_DIR}/CryptoKey.cpp" "${LIBI2PD_SRC_DIR}/Garlic.cpp" @@ -77,10 +77,10 @@ set (LIBI2PD_SRC "${LIBI2PD_SRC_DIR}/api.cpp" "${LIBI2PD_SRC_DIR}/Event.cpp" "${LIBI2PD_SRC_DIR}/Gost.cpp" - "${LIBI2PD_SRC_DIR}/ChaCha20.cpp" + "${LIBI2PD_SRC_DIR}/ChaCha20.cpp" "${LIBI2PD_SRC_DIR}/Poly1305.cpp" "${LIBI2PD_SRC_DIR}/Ed25519.cpp" - "${LIBI2PD_SRC_DIR}/NTCP2.cpp" + "${LIBI2PD_SRC_DIR}/NTCP2.cpp" ) if (WITH_WEBSOCKETS) @@ -190,7 +190,7 @@ if (CXX11_SUPPORTED) elseif (CXX0X_SUPPORTED) # gcc 4.6 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x" ) elseif (NOT MSVC) - message(SEND_ERROR "C++11 standart not seems to be supported by compiler. Too old version?") + message(SEND_ERROR "C++11 standard not seems to be supported by compiler. Too old version?") endif () if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") @@ -202,9 +202,11 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") endif () elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") # more tweaks - if (NOT (MSVC OR MSYS OR APPLE)) + if (LINUX) set (CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -stdlib=libstdc++" ) # required for list(APPEND CMAKE_REQUIRED_LIBRARIES "stdc++") # required to link with -stdlib=libstdc++ + endif() + if (NOT (MSVC OR MSYS OR APPLE)) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-const-variable -Wno-overloaded-virtual -Wno-c99-extensions" ) endif() endif () @@ -234,7 +236,6 @@ endif () if (WITH_AESNI) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes" ) - add_definitions ( -DAESNI ) endif() if (WITH_AVX) @@ -339,7 +340,7 @@ target_link_libraries(libi2pdclient libi2pd) find_package ( Boost COMPONENTS system filesystem program_options date_time REQUIRED ) if(NOT DEFINED Boost_INCLUDE_DIRS) - message(SEND_ERROR "Boost is not found, or your boost version was bellow 1.46. Please download Boost!") + message(SEND_ERROR "Boost is not found, or your boost version was below 1.46. Please download Boost!") endif() find_package ( OpenSSL REQUIRED ) diff --git a/build/build_mingw.cmd b/build/build_mingw.cmd index e7811b0b..7f163878 100644 --- a/build/build_mingw.cmd +++ b/build/build_mingw.cmd @@ -62,12 +62,12 @@ exit /b 0 %xSH% "make clean" >> nul echo Building i2pd %tag% for win%bitness%: echo Build AVX+AESNI... -%xSH% "make USE_UPNP=yes USE_AVX=1 USE_AESNI=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_avx_aesni.zip i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates && make clean" > build/build_win%bitness%_avx_aesni.log 2>&1 +%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 i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates && make clean" > build/build_win%bitness%_avx_aesni.log 2>&1 echo Build AVX... -%xSH% "make USE_UPNP=yes USE_AVX=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_avx.zip i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates && make clean" > build/build_win%bitness%_avx.log 2>&1 +%xSH% "make DEBUG=no USE_UPNP=yes USE_AVX=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_avx.zip i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates && make clean" > build/build_win%bitness%_avx.log 2>&1 echo Build AESNI... -%xSH% "make USE_UPNP=yes USE_AESNI=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_aesni.zip i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates && make clean" > build/build_win%bitness%_aesni.log 2>&1 +%xSH% "make DEBUG=no USE_UPNP=yes USE_AESNI=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_aesni.zip i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates && make clean" > build/build_win%bitness%_aesni.log 2>&1 echo Build without extensions... -%xSH% "make USE_UPNP=yes -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw.zip i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates && make clean" > build/build_win%bitness%.log 2>&1 +%xSH% "make DEBUG=no USE_UPNP=yes -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw.zip i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates && make clean" > build/build_win%bitness%.log 2>&1 :EOF \ No newline at end of file diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index ee56a1c7..0b88e983 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -152,6 +152,19 @@ namespace i2p i2p::context.SetSupportsV6 (ipv6); i2p::context.SetSupportsV4 (ipv4); + bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); + if (ntcp2) + { + bool published; i2p::config::GetOption("ntcp2.published", published); + if (published) + { + uint16_t port; i2p::config::GetOption("ntcp2.port", port); + i2p::context.PublishNTCP2Address (port, true); // publish + } + else + i2p::context.PublishNTCP2Address (port, false); // unpublish + } + bool transit; i2p::config::GetOption("notransit", transit); i2p::context.SetAcceptsTunnels (!transit); uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels); @@ -276,9 +289,10 @@ namespace i2p if(!ntcp) LogPrint(eLogInfo, "Daemon: ntcp disabled"); i2p::transport::transports.Start(ntcp, ssu); - if (i2p::transport::transports.IsBoundNTCP() || i2p::transport::transports.IsBoundSSU()) { + if (i2p::transport::transports.IsBoundNTCP() || i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundNTCP2()) LogPrint(eLogInfo, "Daemon: Transports started"); - } else { + else + { LogPrint(eLogError, "Daemon: failed to start Transports"); /** shut down netdb right away */ i2p::transport::transports.Stop(); diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index faf386d8..cda3f2ec 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -5,6 +5,7 @@ #include #include +#include #include "Base.h" #include "FS.h" @@ -259,14 +260,21 @@ namespace http { s << "Our external address:" << "
\r\n" ; for (const auto& address : i2p::context.GetRouterInfo().GetAddresses()) { + if (address->IsNTCP2 () && !address->IsPublishedNTCP2 ()) + { + s << "NTCP2   supported
\r\n"; + continue; + } switch (address->transportStyle) { case i2p::data::RouterInfo::eTransportNTCP: - if (address->host.is_v6 ()) - s << "NTCP6  "; - else - s << "NTCP  "; - break; + { + s << "NTCP"; + if (address->IsPublishedNTCP2 ()) s << "2"; + if (address->host.is_v6 ()) s << "6"; + s << "  "; + break; + } case i2p::data::RouterInfo::eTransportSSU: if (address->host.is_v6 ()) s << "SSU6     "; @@ -540,6 +548,46 @@ namespace http { } } + template + static void ShowNTCPTransports (std::stringstream& s, const Sessions& sessions, const std::string name) + { + std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0; + for (const auto& it: sessions ) + { + if (it.second && it.second->IsEstablished () && !it.second->GetSocket ().remote_endpoint ().address ().is_v6 ()) + { + // incoming connection doesn't have remote RI + if (it.second->IsOutgoing ()) tmp_s << " ⇒ "; + tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " + << it.second->GetSocket ().remote_endpoint().address ().to_string (); + if (!it.second->IsOutgoing ()) tmp_s << " ⇒ "; + tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; + tmp_s << "
\r\n" << std::endl; + cnt++; + } + if (it.second && it.second->IsEstablished () && it.second->GetSocket ().remote_endpoint ().address ().is_v6 ()) + { + if (it.second->IsOutgoing ()) tmp_s6 << " ⇒ "; + tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " + << "[" << it.second->GetSocket ().remote_endpoint().address ().to_string () << "]"; + if (!it.second->IsOutgoing ()) tmp_s6 << " ⇒ "; + tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; + tmp_s6 << "
\r\n" << std::endl; + cnt6++; + } + } + if (!tmp_s.str ().empty ()) + { + s << "
\r\n\r\n

"; + s << tmp_s.str () << "

\r\n
\r\n"; + } + if (!tmp_s6.str ().empty ()) + { + s << "
\r\n\r\n

"; + s << tmp_s6.str () << "

\r\n
\r\n"; + } + } + void ShowTransports (std::stringstream& s) { s << "Transports:
\r\n
\r\n"; @@ -548,43 +596,14 @@ namespace http { { auto sessions = ntcpServer->GetNTCPSessions (); if (!sessions.empty ()) - { - std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0; - for (const auto& it: sessions ) - { - if (it.second && it.second->IsEstablished () && !it.second->GetSocket ().remote_endpoint ().address ().is_v6 ()) - { - // incoming connection doesn't have remote RI - if (it.second->IsOutgoing ()) tmp_s << " ⇒ "; - tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " - << it.second->GetSocket ().remote_endpoint().address ().to_string (); - if (!it.second->IsOutgoing ()) tmp_s << " ⇒ "; - tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; - tmp_s << "
\r\n" << std::endl; - cnt++; - } - if (it.second && it.second->IsEstablished () && it.second->GetSocket ().remote_endpoint ().address ().is_v6 ()) - { - if (it.second->IsOutgoing ()) tmp_s6 << " ⇒ "; - tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " - << "[" << it.second->GetSocket ().remote_endpoint().address ().to_string () << "]"; - if (!it.second->IsOutgoing ()) tmp_s6 << " ⇒ "; - tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; - tmp_s6 << "
\r\n" << std::endl; - cnt6++; - } - } - if (!tmp_s.str ().empty ()) - { - s << "
\r\n\r\n

"; - s << tmp_s.str () << "

\r\n
\r\n"; - } - if (!tmp_s6.str ().empty ()) - { - s << "
\r\n\r\n

"; - s << tmp_s6.str () << "

\r\n
\r\n"; - } - } + ShowNTCPTransports (s, sessions, "NTCP"); + } + auto ntcp2Server = i2p::transport::transports.GetNTCP2Server (); + if (ntcp2Server) + { + auto sessions = ntcp2Server->GetNTCP2Sessions (); + if (!sessions.empty ()) + ShowNTCPTransports (s, sessions, "NTCP2"); } auto ssuServer = i2p::transport::transports.GetSSUServer (); if (ssuServer) @@ -862,7 +881,7 @@ namespace http { { /* deny request as it's from a non whitelisted hostname */ res.code = 403; - content = "host missmatch"; + content = "host mismatch"; SendReply(res, content); return; } diff --git a/debian/i2pd.1 b/debian/i2pd.1 index 91e3b60f..9dd44093 100644 --- a/debian/i2pd.1 +++ b/debian/i2pd.1 @@ -96,7 +96,7 @@ Router will use system folders like \fI/var/lib/i2pd\fR (\fIdisabled\fR by defau \fB\-\-family=\fR Name of a family, router belongs to. .PP -Switchs, which enabled by default (like \fB\-\-ssu\fR, \fB\-\-ntcp\fR, etc.), can be disabled in config file. +Switches, which enabled by default (like \fB\-\-ssu\fR, \fB\-\-ntcp\fR, etc.), can be disabled in config file. .RE See service-specific parameters in example config file \fI/usr/share/doc/i2pd/i2pd.conf.gz\fR .SH "FILES" diff --git a/debian/rules b/debian/rules index 8e537049..77ecd7b7 100755 --- a/debian/rules +++ b/debian/rules @@ -17,6 +17,6 @@ DEB_BUILD_MAINT_OPTIONS=hardening=+bindnow override_dh_strip: dh_strip --dbg-package=i2pd-dbg -## uncoment this if you have "missing info" problem when building package +## uncomment this if you have "missing info" problem when building package #override_dh_shlibdeps: # dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info diff --git a/docs/Doxyfile b/docs/Doxyfile index c434d2dc..275d668e 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1094,7 +1094,7 @@ HTML_STYLESHEET = # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. +# standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra stylesheet files is of importance (e.g. the last # stylesheet in the list overrules the setting of the previous ones in the @@ -1637,7 +1637,7 @@ EXTRA_PACKAGES = # Note: Only use a user-defined header if you know what you are doing! The # following commands have a special meaning inside the header: $title, # $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty string, # for the replacement values of the other commands the user is referred to # HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. diff --git a/libi2pd/CPU.cpp b/libi2pd/CPU.cpp index d2868a20..a707c3dc 100644 --- a/libi2pd/CPU.cpp +++ b/libi2pd/CPU.cpp @@ -21,23 +21,35 @@ namespace cpu void Detect() { +#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__ } -#endif +#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__) } } } diff --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp index 1ff55dd6..098a7baa 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -59,7 +59,6 @@ namespace config { ("ntcp", value()->default_value(true), "Enable NTCP transport (default: enabled)") ("ssu", value()->default_value(true), "Enable SSU transport (default: enabled)") ("ntcpproxy", value()->default_value(""), "Proxy URL for NTCP transport") - ("ntcp2", value()->default_value(false), "Enable NTCP2 (experimental, default: disabled)") #ifdef _WIN32 ("svcctl", value()->default_value(""), "Windows service management ('install' or 'remove')") ("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)") @@ -232,6 +231,13 @@ namespace config { ("exploratory.outbound.quantity", value()->default_value(3), "Exploratory outbound tunnels quantity") ; + options_description ntcp2("NTCP2 Options"); + ntcp2.add_options() + ("ntcp2.enabled", value()->default_value(false), "Enable NTCP2 (default: disabled)") + ("ntcp2.published", value()->default_value(false), "Publish NTCP2 (default: disabled)") + ("ntcp2.port", value()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)") + ; + m_OptionsDesc .add(general) .add(limits) @@ -249,6 +255,7 @@ namespace config { .add(trust) .add(websocket) .add(exploratory) + .add(ntcp2) ; } diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index 6d859342..7a947d34 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -399,7 +399,7 @@ namespace crypto bn2buf (x, encrypted + 1, len); bn2buf (y, encrypted + 1 + len, len); RAND_bytes (encrypted + 1 + 2*len, 256 - 2*len); - } + } else { bn2buf (x, encrypted, len); @@ -468,10 +468,10 @@ namespace crypto CBCDecryption decryption; decryption.SetKey (shared); decryption.SetIV (iv); - if (zeroPadding) + if (zeroPadding) decryption.Decrypt (encrypted + 258, 256, m); else - decryption.Decrypt (encrypted + 256, 256, m); + decryption.Decrypt (encrypted + 256, 256, m); // verify and copy uint8_t hash[32]; SHA256 (m + 33, 222, hash); @@ -522,9 +522,9 @@ namespace crypto { uint64_t buf[256]; uint64_t hash[12]; // 96 bytes +#ifdef __AVX__ if(i2p::cpu::avx) { -#ifdef AVX __asm__ ( "vmovups %[key], %%ymm0 \n" @@ -543,30 +543,9 @@ namespace crypto [buf]"r"(buf), [hash]"r"(hash) : "memory", "%xmm0" // TODO: change to %ymm0 later ); -#else - // ikeypad - buf[0] = key.GetLL ()[0] ^ IPAD; - buf[1] = key.GetLL ()[1] ^ IPAD; - buf[2] = key.GetLL ()[2] ^ IPAD; - buf[3] = key.GetLL ()[3] ^ IPAD; - buf[4] = IPAD; - buf[5] = IPAD; - buf[6] = IPAD; - buf[7] = IPAD; - // okeypad - hash[0] = key.GetLL ()[0] ^ OPAD; - hash[1] = key.GetLL ()[1] ^ OPAD; - hash[2] = key.GetLL ()[2] ^ OPAD; - hash[3] = key.GetLL ()[3] ^ OPAD; - hash[4] = OPAD; - hash[5] = OPAD; - hash[6] = OPAD; - hash[7] = OPAD; - // fill last 16 bytes with zeros (first hash size assumed 32 bytes in I2P) - memset (hash + 10, 0, 16); -#endif } else +#endif { // ikeypad buf[0] = key.GetLL ()[0] ^ IPAD; @@ -600,12 +579,12 @@ namespace crypto } // AES -#ifdef AESNI +#ifdef __AES__ #ifdef ARM64AES void init_aesenc(void){ // TODO: Implementation } - + #endif #define KeyExpansion256(round0,round1) \ @@ -632,7 +611,7 @@ namespace crypto "movaps %%xmm3, "#round1"(%[sched]) \n" #endif -#ifdef AESNI +#ifdef __AES__ void ECBCryptoAESNI::ExpandKey (const AESKey& key) { __asm__ @@ -669,11 +648,11 @@ namespace crypto : [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input : "%xmm1", "%xmm2", "%xmm3", "%xmm4", "memory" // clogged ); - } + } #endif -#if AESNI +#ifdef __AES__ #define EncryptAES256(sched) \ "pxor (%["#sched"]), %%xmm0 \n" \ "aesenc 16(%["#sched"]), %%xmm0 \n" \ @@ -691,12 +670,12 @@ namespace crypto "aesenc 208(%["#sched"]), %%xmm0 \n" \ "aesenclast 224(%["#sched"]), %%xmm0 \n" #endif - + void ECBEncryption::Encrypt (const ChipherBlock * in, ChipherBlock * out) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI __asm__ ( "movups (%[in]), %%xmm0 \n" @@ -704,17 +683,15 @@ namespace crypto "movups %%xmm0, (%[out]) \n" : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" ); -#else - AES_encrypt (in->buf, out->buf, &m_Key); -#endif } else +#endif { AES_encrypt (in->buf, out->buf, &m_Key); - } + } } -#ifdef AESNI +#ifdef __AES__ #define DecryptAES256(sched) \ "pxor 224(%["#sched"]), %%xmm0 \n" \ "aesdec 208(%["#sched"]), %%xmm0 \n" \ @@ -732,12 +709,12 @@ namespace crypto "aesdec 16(%["#sched"]), %%xmm0 \n" \ "aesdeclast (%["#sched"]), %%xmm0 \n" #endif - + void ECBDecryption::Decrypt (const ChipherBlock * in, ChipherBlock * out) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI __asm__ ( "movups (%[in]), %%xmm0 \n" @@ -745,17 +722,15 @@ namespace crypto "movups %%xmm0, (%[out]) \n" : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" ); -#else - AES_decrypt (in->buf, out->buf, &m_Key); -#endif } else +#endif { AES_decrypt (in->buf, out->buf, &m_Key); } } -#ifdef AESNI +#ifdef __AES__ #define CallAESIMC(offset) \ "movaps "#offset"(%[shed]), %%xmm0 \n" \ "aesimc %%xmm0, %%xmm0 \n" \ @@ -764,25 +739,23 @@ namespace crypto void ECBEncryption::SetKey (const AESKey& key) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI - ExpandKey (key); -#else - AES_set_encrypt_key (key, 256, &m_Key); -#endif + ExpandKey (key); } else +#endif { AES_set_encrypt_key (key, 256, &m_Key); } } - + void ECBDecryption::SetKey (const AESKey& key) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI ExpandKey (key); // expand encryption key first // then invert it using aesimc __asm__ @@ -802,11 +775,9 @@ namespace crypto CallAESIMC(208) : : [shed]"r"(GetKeySchedule ()) : "%xmm0", "memory" ); -#else - AES_set_decrypt_key (key, 256, &m_Key); -#endif } else +#endif { AES_set_decrypt_key (key, 256, &m_Key); } @@ -815,9 +786,9 @@ namespace crypto void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI __asm__ ( "movups (%[iv]), %%xmm1 \n" @@ -837,16 +808,9 @@ namespace crypto [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) : "%xmm0", "%xmm1", "cc", "memory" ); -#else - for (int i = 0; i < numBlocks; i++) - { - *m_LastBlock.GetChipherBlock () ^= in[i]; - m_ECBEncryption.Encrypt (m_LastBlock.GetChipherBlock (), m_LastBlock.GetChipherBlock ()); - out[i] = *m_LastBlock.GetChipherBlock (); - } -#endif } else +#endif { for (int i = 0; i < numBlocks; i++) { @@ -867,9 +831,9 @@ namespace crypto void CBCEncryption::Encrypt (const uint8_t * in, uint8_t * out) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI __asm__ ( "movups (%[iv]), %%xmm1 \n" @@ -883,19 +847,17 @@ namespace crypto [in]"r"(in), [out]"r"(out) : "%xmm0", "%xmm1", "memory" ); -#else - Encrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); -#endif } else +#endif Encrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); } void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI __asm__ ( "movups (%[iv]), %%xmm1 \n" @@ -916,17 +878,9 @@ namespace crypto [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" ); -#else - for (int i = 0; i < numBlocks; i++) - { - ChipherBlock tmp = in[i]; - m_ECBDecryption.Decrypt (in + i, out + i); - out[i] ^= *m_IV.GetChipherBlock (); - *m_IV.GetChipherBlock () = tmp; - } -#endif } else +#endif { for (int i = 0; i < numBlocks; i++) { @@ -947,9 +901,9 @@ namespace crypto void CBCDecryption::Decrypt (const uint8_t * in, uint8_t * out) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI __asm__ ( "movups (%[iv]), %%xmm1 \n" @@ -963,19 +917,17 @@ namespace crypto [in]"r"(in), [out]"r"(out) : "%xmm0", "%xmm1", "memory" ); -#else - Decrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); -#endif } else +#endif Decrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); } void TunnelEncryption::Encrypt (const uint8_t * in, uint8_t * out) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI __asm__ ( // encrypt IV @@ -1001,14 +953,9 @@ namespace crypto [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes : "%xmm0", "%xmm1", "cc", "memory" ); -#else - m_IVEncryption.Encrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv - m_LayerEncryption.SetIV (out); - m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data - m_IVEncryption.Encrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv -#endif } else +#endif { m_IVEncryption.Encrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv m_LayerEncryption.SetIV (out); @@ -1019,9 +966,9 @@ namespace crypto void TunnelDecryption::Decrypt (const uint8_t * in, uint8_t * out) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI __asm__ ( // decrypt IV @@ -1048,14 +995,9 @@ namespace crypto [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" ); -#else - m_IVDecryption.Decrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv - m_LayerDecryption.SetIV (out); - m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data - m_IVDecryption.Decrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv -#endif } else +#endif { m_IVDecryption.Decrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv m_LayerDecryption.SetIV (out); @@ -1068,7 +1010,7 @@ namespace crypto bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt) { - if (len < msgLen) return false; + if (len < msgLen) return false; if (encrypt && len < msgLen + 16) return false; bool ret = true; #if LEGACY_OPENSSL @@ -1076,40 +1018,51 @@ namespace crypto uint8_t polyKey[64]; memset(polyKey, 0, sizeof(polyKey)); chacha20 (polyKey, 64, nonce, key, 0); - // encrypt data - memcpy (buf, msg, msgLen); - chacha20 (buf, msgLen, nonce, key, 1); - + // create Poly1305 message - if (!ad) adLen = 0; + if (!ad) adLen = 0; std::vector polyMsg(adLen + msgLen + 3*16); size_t offset = 0; uint8_t padding[16]; memset (padding, 0, 16); if (ad) - { + { memcpy (polyMsg.data (), ad, adLen); offset += adLen; // additional authenticated data auto rem = adLen & 0x0F; // %16 - if (rem) + if (rem) { // padding1 rem = 16 - rem; - memcpy (polyMsg.data () + offset, padding, rem); offset += rem; + memcpy (polyMsg.data () + offset, padding, rem); offset += rem; } } - memcpy (polyMsg.data () + offset, encrypt ? buf : msg, msgLen); offset += msgLen; // encrypted data + // encrypt/decrypt data and add to hash + if (buf != msg) + memcpy (buf, msg, msgLen); + if (encrypt) + { + chacha20 (buf, msgLen, nonce, key, 1); // encrypt + memcpy (polyMsg.data () + offset, buf, msgLen); // after encryption + } + else + { + memcpy (polyMsg.data () + offset, buf, msgLen); // before decryption + chacha20 (buf, msgLen, nonce, key, 1); // decrypt + } + offset += msgLen; // encrypted data + auto rem = msgLen & 0x0F; // %16 - if (rem) + if (rem) { // padding2 rem = 16 - rem; - memcpy (polyMsg.data () + offset, padding, rem); offset += rem; + memcpy (polyMsg.data () + offset, padding, rem); offset += rem; } - htole64buf (polyMsg.data () + offset, adLen); offset += 8; + htole64buf (polyMsg.data () + offset, adLen); offset += 8; htole64buf (polyMsg.data () + offset, msgLen); offset += 8; if (encrypt) { - // calculate Poly1305 tag and write in after encrypted data + // calculate Poly1305 tag and write in after encrypted data Poly1305HMAC ((uint32_t *)(buf + msgLen), (uint32_t *)polyKey, polyMsg.data (), offset); } else @@ -1118,9 +1071,9 @@ namespace crypto // calculate Poly1305 tag Poly1305HMAC (tag, (uint32_t *)polyKey, polyMsg.data (), offset); if (memcmp (tag, msg + msgLen, 16)) ret = false; // compare with provided - } + } #else - int outlen = 0; + int outlen = 0; EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new (); if (encrypt) { @@ -1139,10 +1092,11 @@ namespace crypto EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, (uint8_t *)(msg + msgLen)); EVP_DecryptInit_ex(ctx, NULL, NULL, key, nonce); EVP_DecryptUpdate(ctx, NULL, &outlen, ad, adLen); - ret = EVP_DecryptUpdate(ctx, buf, &outlen, msg, msgLen) > 0; + EVP_DecryptUpdate(ctx, buf, &outlen, msg, msgLen); + ret = EVP_DecryptFinal_ex(ctx, buf + outlen, &outlen) > 0; } - - EVP_CIPHER_CTX_free (ctx); + + EVP_CIPHER_CTX_free (ctx); #endif return ret; } diff --git a/libi2pd/Crypto.h b/libi2pd/Crypto.h index 25646dbb..43f1def9 100644 --- a/libi2pd/Crypto.h +++ b/libi2pd/Crypto.h @@ -69,9 +69,9 @@ namespace crypto void operator^=(const ChipherBlock& other) // XOR { +#ifdef __AVX__ if (i2p::cpu::avx) { -#ifdef AVX __asm__ ( "vmovups (%[buf]), %%xmm0 \n" @@ -82,12 +82,9 @@ namespace crypto : [buf]"r"(buf), [other]"r"(other.buf) : "%xmm0", "%xmm1", "memory" ); -#else - for (int i = 0; i < 16; i++) - buf[i] ^= other.buf[i]; -#endif } else +#endif { // TODO: implement it better for (int i = 0; i < 16; i++) @@ -123,7 +120,7 @@ namespace crypto }; -#ifdef AESNI +#ifdef __AES__ #ifdef ARM64AES void init_aesenc(void) __attribute__((constructor)); #endif @@ -143,7 +140,7 @@ namespace crypto }; #endif -#ifdef AESNI +#ifdef __AES__ class ECBEncryption: public ECBCryptoAESNI #else class ECBEncryption @@ -152,14 +149,14 @@ namespace crypto public: void SetKey (const AESKey& key); - + void Encrypt(const ChipherBlock * in, ChipherBlock * out); private: AES_KEY m_Key; }; -#ifdef AESNI +#ifdef __AES__ class ECBDecryption: public ECBCryptoAESNI #else class ECBDecryption @@ -188,7 +185,7 @@ namespace crypto void Encrypt (const uint8_t * in, uint8_t * out); // one block ECBEncryption & ECB() { return m_ECBEncryption; } - + private: AESAlignedBuffer<16> m_LastBlock; @@ -211,7 +208,7 @@ namespace crypto void Decrypt (const uint8_t * in, uint8_t * out); // one block ECBDecryption & ECB() { return m_ECBDecryption; } - + private: AESAlignedBuffer<16> m_IV; @@ -255,8 +252,8 @@ namespace crypto }; // AEAD/ChaCha20/Poly1305 - bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt); // msgLen is len without tag - + bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt); // msgLen is len without tag + // init and terminate void InitCrypto (bool precomputation); void TerminateCrypto (); @@ -265,7 +262,12 @@ namespace crypto // take care about openssl version #include -#define LEGACY_OPENSSL ((OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER)) // 1.0.2 and below or LibreSSL +#if ((OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER)) // 1.0.2 and below or LibreSSL +# define LEGACY_OPENSSL 1 +#else +# define LEGACY_OPENSSL 0 +#endif + #if LEGACY_OPENSSL // define getters and setters introduced in 1.1.0 inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index d3632881..ad7725fc 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -64,7 +64,7 @@ namespace client { it = params->find (I2CP_PARAM_OUTBOUND_NICKNAME); if (it != params->end ()) m_Nickname = it->second; - // otherwise we set deafult nickname in Start when we know local address + // otherwise we set default nickname in Start when we know local address } } } diff --git a/libi2pd/I2NPProtocol.h b/libi2pd/I2NPProtocol.h index f394d284..deaa2292 100644 --- a/libi2pd/I2NPProtocol.h +++ b/libi2pd/I2NPProtocol.h @@ -26,6 +26,9 @@ namespace i2p const size_t I2NP_SHORT_HEADER_EXPIRATION_OFFSET = I2NP_SHORT_HEADER_TYPEID_OFFSET + 1; const size_t I2NP_SHORT_HEADER_SIZE = I2NP_SHORT_HEADER_EXPIRATION_OFFSET + 4; + // I2NP NTCP2 header + const size_t I2NP_NTCP2_HEADER_SIZE = I2NP_HEADER_EXPIRATION_OFFSET + 4; + // Tunnel Gateway header const size_t TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET = 0; const size_t TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET = TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET + 4; @@ -194,6 +197,24 @@ namespace tunnel len = offset + I2NP_SHORT_HEADER_SIZE + bufbe16toh (header + I2NP_HEADER_SIZE_OFFSET); return bufbe32toh (header + I2NP_HEADER_MSGID_OFFSET); } + // for NTCP2 only + uint8_t * GetNTCP2Header () { return GetPayload () - I2NP_NTCP2_HEADER_SIZE; }; + size_t GetNTCP2Length () const { return GetPayloadLength () + I2NP_NTCP2_HEADER_SIZE; }; + void FromNTCP2 () + { + const uint8_t * ntcp2 = GetNTCP2Header (); + memcpy (GetHeader () + I2NP_HEADER_TYPEID_OFFSET, ntcp2 + I2NP_HEADER_TYPEID_OFFSET, 5); // typeid + msgid + SetExpiration (bufbe32toh (ntcp2 + I2NP_HEADER_EXPIRATION_OFFSET)*1000LL); + SetSize (len - offset - I2NP_HEADER_SIZE); + SetChks (0); + } + + void ToNTCP2 () + { + uint8_t * ntcp2 = GetNTCP2Header (); + htobe32buf (ntcp2 + I2NP_HEADER_EXPIRATION_OFFSET, bufbe64toh (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET)/1000LL); + memcpy (ntcp2 + I2NP_HEADER_TYPEID_OFFSET, GetHeader () + I2NP_HEADER_TYPEID_OFFSET, 5); // typeid + msgid + } void FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID = 0); void RenewI2NPMessageHeader (); diff --git a/libi2pd/Identity.cpp b/libi2pd/Identity.cpp index 3fcd16ad..7f64d931 100644 --- a/libi2pd/Identity.cpp +++ b/libi2pd/Identity.cpp @@ -719,24 +719,29 @@ namespace data XORMetric operator^(const IdentHash& key1, const IdentHash& key2) { XORMetric m; -#if defined(__AVX__) // for AVX - __asm__ - ( - "vmovups %1, %%ymm0 \n" - "vmovups %2, %%ymm1 \n" - "vxorps %%ymm0, %%ymm1, %%ymm1 \n" - "vmovups %%ymm1, %0 \n" - : "=m"(*m.metric) - : "m"(*key1), "m"(*key2) - : "memory", "%xmm0", "%xmm1" // should be replaced by %ymm0/1 once supported by compiler - ); -#else - const uint64_t * hash1 = key1.GetLL (), * hash2 = key2.GetLL (); - m.metric_ll[0] = hash1[0] ^ hash2[0]; - m.metric_ll[1] = hash1[1] ^ hash2[1]; - m.metric_ll[2] = hash1[2] ^ hash2[2]; - m.metric_ll[3] = hash1[3] ^ hash2[3]; +#ifdef __AVX__ + if(i2p::cpu::avx) + { + __asm__ + ( + "vmovups %1, %%ymm0 \n" + "vmovups %2, %%ymm1 \n" + "vxorps %%ymm0, %%ymm1, %%ymm1 \n" + "vmovups %%ymm1, %0 \n" + : "=m"(*m.metric) + : "m"(*key1), "m"(*key2) + : "memory", "%xmm0", "%xmm1" // should be replaced by %ymm0/1 once supported by compiler + ); + } + else #endif + { + const uint64_t * hash1 = key1.GetLL (), * hash2 = key2.GetLL (); + m.metric_ll[0] = hash1[0] ^ hash2[0]; + m.metric_ll[1] = hash1[1] ^ hash2[1]; + m.metric_ll[2] = hash1[2] ^ hash2[2]; + m.metric_ll[3] = hash1[3] ^ hash2[3]; + } return m; } diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index 594f4a93..b54a83ef 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -9,56 +9,30 @@ #include "Ed25519.h" #include "Siphash.h" #include "RouterContext.h" +#include "Transports.h" +#include "NetDb.hpp" #include "NTCP2.h" namespace i2p { namespace transport { - NTCP2Session::NTCP2Session (NTCP2Server& server, std::shared_ptr in_RemoteRouter): - TransportSession (in_RemoteRouter, 30), - m_Server (server), m_Socket (m_Server.GetService ()), - m_IsEstablished (false), m_IsTerminated (false), - m_SessionRequestBuffer (nullptr), m_SessionCreatedBuffer (nullptr), m_SessionConfirmedBuffer (nullptr), - m_NextReceivedBuffer (nullptr), m_NextSendBuffer (nullptr), - m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0) - { - auto addr = in_RemoteRouter->GetNTCPAddress (); - if (addr->ntcp2) - { - memcpy (m_RemoteStaticKey, addr->ntcp2->staticKey, 32); - memcpy (m_IV, addr->ntcp2->iv, 16); - } - else - LogPrint (eLogWarning, "NTCP2: Missing NTCP2 parameters"); + NTCP2Establisher::NTCP2Establisher (): + m_SessionRequestBuffer (nullptr), m_SessionCreatedBuffer (nullptr), m_SessionConfirmedBuffer (nullptr) + { + m_Ctx = BN_CTX_new (); + CreateEphemeralKey (); } - NTCP2Session::~NTCP2Session () - { + NTCP2Establisher::~NTCP2Establisher () + { + BN_CTX_free (m_Ctx); delete[] m_SessionRequestBuffer; delete[] m_SessionCreatedBuffer; delete[] m_SessionConfirmedBuffer; - delete[] m_NextReceivedBuffer; - delete[] m_NextSendBuffer; } - void NTCP2Session::Terminate () - { - if (!m_IsTerminated) - { - m_IsTerminated = true; - m_IsEstablished = false; - m_Socket.close (); - LogPrint (eLogDebug, "NTCP2: session terminated"); - } - } - - void NTCP2Session::Done () - { - m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ())); - } - - void NTCP2Session::MixKey (const uint8_t * inputKeyMaterial, uint8_t * derived) + void NTCP2Establisher::MixKey (const uint8_t * inputKeyMaterial, uint8_t * derived) { // temp_key = HMAC-SHA256(ck, input_key_material) uint8_t tempKey[32]; unsigned int len; @@ -71,77 +45,276 @@ namespace transport HMAC(EVP_sha256(), tempKey, 32, m_CK, 33, derived, &len); } - void NTCP2Session::CreateNonce (uint64_t seqn, uint8_t * nonce) - { - memset (nonce, 0, 4); - htole64buf (nonce + 4, seqn); - } - - void NTCP2Session::KeyDerivationFunction1 (const uint8_t * rs, const uint8_t * priv, const uint8_t * pub, uint8_t * derived) + void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, const uint8_t * priv, const uint8_t * rs, const uint8_t * epub) { static const uint8_t protocolNameHash[] = { 0x72, 0xe8, 0x42, 0xc5, 0x45, 0xe1, 0x80, 0x80, 0xd3, 0x9c, 0x44, 0x93, 0xbb, 0x91, 0xd7, 0xed, 0xf2, 0x28, 0x98, 0x17, 0x71, 0x21, 0x8c, 0x1f, 0x62, 0x4e, 0x20, 0x6f, 0x28, 0xd3, 0x2f, 0x71 }; // SHA256 ("Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256") - static uint8_t h[64] = + static const uint8_t hh[32] = { 0x49, 0xff, 0x48, 0x3f, 0xc4, 0x04, 0xb9, 0xb2, 0x6b, 0x11, 0x94, 0x36, 0x72, 0xff, 0x05, 0xb5, 0x61, 0x27, 0x03, 0x31, 0xba, 0x89, 0xb8, 0xfc, 0x33, 0x15, 0x93, 0x87, 0x57, 0xdd, 0x3d, 0x1e }; // SHA256 (protocolNameHash) memcpy (m_CK, protocolNameHash, 32); - // h = SHA256(h || rs) - memcpy (h + 32, rs, 32); - SHA256 (h, 64, h); - // h = SHA256(h || pub) - memcpy (h + 32, pub, 32); - SHA256 (h, 64, m_H); + // h = SHA256(hh || rs) + SHA256_CTX ctx; + SHA256_Init (&ctx); + SHA256_Update (&ctx, hh, 32); + SHA256_Update (&ctx, rs, 32); + SHA256_Final (m_H, &ctx); + // h = SHA256(h || epub) + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, epub, 32); + SHA256_Final (m_H, &ctx); // x25519 between rs and priv uint8_t inputKeyMaterial[32]; - BN_CTX * ctx = BN_CTX_new (); - i2p::crypto::GetEd25519 ()->ScalarMul (rs, priv, inputKeyMaterial, ctx); // rs*priv - BN_CTX_free (ctx); - MixKey (inputKeyMaterial, derived); + i2p::crypto::GetEd25519 ()->ScalarMul (pub, priv, inputKeyMaterial, m_Ctx); // rs*priv + MixKey (inputKeyMaterial, m_K); } - void NTCP2Session::KeyDerivationFunction2 (const uint8_t * priv, const uint8_t * pub, const uint8_t * sessionRequest, size_t sessionRequestLen, uint8_t * derived) + void NTCP2Establisher::KDF1Alice () { - uint8_t h[64]; - memcpy (h, m_H, 32); - memcpy (h + 32, sessionRequest + 32, 32); // encrypted payload - SHA256 (h, 64, h); + KeyDerivationFunction1 (m_RemoteStaticKey, GetPriv (), m_RemoteStaticKey, GetPub ()); + } + + void NTCP2Establisher::KDF1Bob () + { + KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetNTCP2StaticPrivateKey (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ()); + } + + void NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub) + { + SHA256_CTX ctx; + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, sessionRequest + 32, 32); // encrypted payload + SHA256_Final (m_H, &ctx); + int paddingLength = sessionRequestLen - 64; if (paddingLength > 0) { - std::vector h1(paddingLength + 32); - memcpy (h1.data (), h, 32); - memcpy (h1.data () + 32, sessionRequest + 64, paddingLength); - SHA256 (h1.data (), paddingLength + 32, h); + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, sessionRequest + 64, paddingLength); + SHA256_Final (m_H, &ctx); } - memcpy (h + 32, pub, 32); - SHA256 (h, 64, m_H); + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, epub, 32); + SHA256_Final (m_H, &ctx); // x25519 between remote pub and priv uint8_t inputKeyMaterial[32]; - BN_CTX * ctx = BN_CTX_new (); - i2p::crypto::GetEd25519 ()->ScalarMul (pub, priv, inputKeyMaterial, ctx); - BN_CTX_free (ctx); - MixKey (inputKeyMaterial, derived); + i2p::crypto::GetEd25519 ()->ScalarMul (GetRemotePub (), GetPriv (), inputKeyMaterial, m_Ctx); + MixKey (inputKeyMaterial, m_K); } - void NTCP2Session::KeyDerivationFunction3 (const uint8_t * staticPrivKey, uint8_t * derived) + void NTCP2Establisher::KDF2Alice () + { + KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetRemotePub ()); + } + + void NTCP2Establisher::KDF2Bob () + { + KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetPub ()); + } + + void NTCP2Establisher::KDF3Alice () { uint8_t inputKeyMaterial[32]; - BN_CTX * ctx = BN_CTX_new (); - i2p::crypto::GetEd25519 ()->ScalarMul (m_Y, staticPrivKey, inputKeyMaterial, ctx); - BN_CTX_free (ctx); - MixKey (inputKeyMaterial, derived); + i2p::crypto::GetEd25519 ()->ScalarMul (GetRemotePub (), i2p::context.GetNTCP2StaticPrivateKey (), inputKeyMaterial, m_Ctx); + MixKey (inputKeyMaterial, m_K); } + void NTCP2Establisher::KDF3Bob () + { + uint8_t inputKeyMaterial[32]; + i2p::crypto::GetEd25519 ()->ScalarMul (m_RemoteStaticKey, m_EphemeralPrivateKey, inputKeyMaterial, m_Ctx); + MixKey (inputKeyMaterial, m_K); + } + + void NTCP2Establisher::CreateEphemeralKey () + { + RAND_bytes (m_EphemeralPrivateKey, 32); + i2p::crypto::GetEd25519 ()->ScalarMulB (m_EphemeralPrivateKey, m_EphemeralPublicKey, m_Ctx); + } + + void NTCP2Establisher::CreateSessionRequestMessage () + { + // create buffer and fill padding + auto paddingLength = rand () % (287 - 64); // message length doesn't exceed 287 bytes + m_SessionRequestBufferLen = paddingLength + 64; + m_SessionRequestBuffer = new uint8_t[m_SessionRequestBufferLen]; + RAND_bytes (m_SessionRequestBuffer + 64, paddingLength); + // encrypt X + i2p::crypto::CBCEncryption encryption; + encryption.SetKey (m_RemoteIdentHash); + encryption.SetIV (m_IV); + encryption.Encrypt (GetPub (), 32, m_SessionRequestBuffer); // X + encryption.GetIV (m_IV); // save IV for SessionCreated + // encryption key for next block + KDF1Alice (); + // fill options + uint8_t options[32]; // actual options size is 16 bytes + memset (options, 0, 16); + options[1] = 2; // ver + htobe16buf (options + 2, paddingLength); // padLen + m3p2Len = i2p::context.GetRouterInfo ().GetBufferLen () + 20; // (RI header + RI + MAC for now) TODO: implement options + htobe16buf (options + 4, m3p2Len); + // 2 bytes reserved + htobe32buf (options + 8, i2p::util::GetSecondsSinceEpoch ()); // tsA + // 4 bytes reserved + // sign and encrypt options, use m_H as AD + uint8_t nonce[12]; + memset (nonce, 0, 12); // set nonce to zero + i2p::crypto::AEADChaCha20Poly1305 (options, 16, m_H, 32, m_K, nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt + } + + void NTCP2Establisher::CreateSessionCreatedMessage () + { + auto paddingLen = rand () % (287 - 64); + m_SessionCreatedBufferLen = paddingLen + 64; + m_SessionCreatedBuffer = new uint8_t[m_SessionCreatedBufferLen]; + RAND_bytes (m_SessionCreatedBuffer + 64, paddingLen); + // encrypt Y + i2p::crypto::CBCEncryption encryption; + encryption.SetKey (i2p::context.GetIdentHash ()); + encryption.SetIV (m_IV); + encryption.Encrypt (GetPub (), 32, m_SessionCreatedBuffer); // Y + // encryption key for next block (m_K) + KDF2Bob (); + uint8_t options[16]; + memset (options, 0, 16); + htobe16buf (options + 2, paddingLen); // padLen + htobe32buf (options + 8, i2p::util::GetSecondsSinceEpoch ()); // tsB + // sign and encrypt options, use m_H as AD + uint8_t nonce[12]; + memset (nonce, 0, 12); // set nonce to zero + i2p::crypto::AEADChaCha20Poly1305 (options, 16, m_H, 32, m_K, nonce, m_SessionCreatedBuffer + 32, 32, true); // encrypt + + } + + void NTCP2Establisher::CreateSessionConfirmedMessagePart1 (const uint8_t * nonce) + { + // update AD + SHA256_CTX ctx; + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, m_SessionCreatedBuffer + 32, 32); // encrypted payload + SHA256_Final (m_H, &ctx); + + int paddingLength = m_SessionCreatedBufferLen - 64; + if (paddingLength > 0) + { + SHA256_CTX ctx1; + SHA256_Init (&ctx1); + SHA256_Update (&ctx1, m_H, 32); + SHA256_Update (&ctx1, m_SessionCreatedBuffer + 64, paddingLength); + SHA256_Final (m_H, &ctx1); + } + // part1 48 bytes + m_SessionConfirmedBuffer = new uint8_t[m3p2Len + 48]; + i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetNTCP2StaticPublicKey (), 32, m_H, 32, m_K, nonce, m_SessionConfirmedBuffer, 48, true); // encrypt + } + + void NTCP2Establisher::CreateSessionConfirmedMessagePart2 (const uint8_t * nonce) + { + // part 2 + // update AD again + SHA256_CTX ctx; + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, m_SessionConfirmedBuffer, 48); + SHA256_Final (m_H, &ctx); + // fill and encrypt + uint8_t * buf = m_SessionConfirmedBuffer + 48; + buf[0] = eNTCP2BlkRouterInfo; // block + htobe16buf (buf + 1, i2p::context.GetRouterInfo ().GetBufferLen () + 1); // flag + RI + buf[3] = 0; // flag + memcpy (buf + 4, i2p::context.GetRouterInfo ().GetBuffer (), i2p::context.GetRouterInfo ().GetBufferLen ()); + KDF3Alice (); + i2p::crypto::AEADChaCha20Poly1305 (buf, m3p2Len - 16, m_H, 32, m_K, nonce, buf, m3p2Len, true); // encrypt + // update h again + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, buf, m3p2Len); + SHA256_Final (m_H, &ctx); //h = SHA256(h || ciphertext) + } + + NTCP2Session::NTCP2Session (NTCP2Server& server, std::shared_ptr in_RemoteRouter): + TransportSession (in_RemoteRouter, NTCP2_ESTABLISH_TIMEOUT), + m_Server (server), m_Socket (m_Server.GetService ()), + m_IsEstablished (false), m_IsTerminated (false), + m_NextReceivedLen (0), m_NextReceivedBuffer (nullptr), m_NextSendBuffer (nullptr), + m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0), m_IsSending (false) + { + m_Establisher.reset (new NTCP2Establisher); + if (in_RemoteRouter) // Alice + { + m_Establisher->m_RemoteIdentHash = GetRemoteIdentity ()->GetIdentHash (); + auto addr = in_RemoteRouter->GetNTCP2Address (true); // we need a published address + if (addr) + { + memcpy (m_Establisher->m_RemoteStaticKey, addr->ntcp2->staticKey, 32); + memcpy (m_Establisher->m_IV, addr->ntcp2->iv, 16); + } + else + LogPrint (eLogWarning, "NTCP2: Missing NTCP2 parameters"); + } + } + + NTCP2Session::~NTCP2Session () + { + delete[] m_NextReceivedBuffer; + delete[] m_NextSendBuffer; + } + + void NTCP2Session::Terminate () + { + if (!m_IsTerminated) + { + m_IsTerminated = true; + m_IsEstablished = false; + m_Socket.close (); + transports.PeerDisconnected (shared_from_this ()); + m_Server.RemoveNTCP2Session (shared_from_this ()); + m_SendQueue.clear (); + LogPrint (eLogDebug, "NTCP2: session terminated"); + } + } + + void NTCP2Session::TerminateByTimeout () + { + SendTerminationAndTerminate (eNTCP2IdleTimeout); + } + + void NTCP2Session::Done () + { + m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ())); + } + + void NTCP2Session::Established () + { + m_IsEstablished = true; + m_Establisher.reset (nullptr); + SetTerminationTimeout (NTCP2_TERMINATION_TIMEOUT); + transports.PeerConnected (shared_from_this ()); + } + + void NTCP2Session::CreateNonce (uint64_t seqn, uint8_t * nonce) + { + memset (nonce, 0, 4); + htole64buf (nonce + 4, seqn); + } + + void NTCP2Session::KeyDerivationFunctionDataPhase () { uint8_t tempKey[32]; unsigned int len; - HMAC(EVP_sha256(), m_CK, 32, nullptr, 0, tempKey, &len); // temp_key = HMAC-SHA256(ck, zerolen) + HMAC(EVP_sha256(), m_Establisher->GetCK (), 32, nullptr, 0, tempKey, &len); // temp_key = HMAC-SHA256(ck, zerolen) static uint8_t one[1] = { 1 }; HMAC(EVP_sha256(), tempKey, 32, one, 1, m_Kab, &len); // k_ab = HMAC-SHA256(temp_key, byte(0x01)). m_Kab[32] = 2; @@ -149,7 +322,7 @@ namespace transport static uint8_t ask[4] = { 'a', 's', 'k', 1 }, master[32]; HMAC(EVP_sha256(), tempKey, 32, ask, 4, master, &len); // ask_master = HMAC-SHA256(temp_key, "ask" || byte(0x01)) uint8_t h[39]; - memcpy (h, m_H, 32); + memcpy (h, m_Establisher->GetH (), 32); memcpy (h + 32, "siphash", 7); HMAC(EVP_sha256(), master, 32, h, 39, tempKey, &len); // temp_key = HMAC-SHA256(ask_master, h || "siphash") HMAC(EVP_sha256(), tempKey, 32, one, 1, master, &len); // sip_master = HMAC-SHA256(temp_key, byte(0x01)) @@ -159,48 +332,12 @@ namespace transport HMAC(EVP_sha256(), tempKey, 32, m_Sipkeysab, 33, m_Sipkeysba, &len); // sipkeys_ba = HMAC-SHA256(temp_key, sipkeys_ab || byte(0x02)) } - void NTCP2Session::CreateEphemeralKey (uint8_t * pub) - { - RAND_bytes (m_EphemeralPrivateKey, 32); - BN_CTX * ctx = BN_CTX_new (); - i2p::crypto::GetEd25519 ()->ScalarMulB (m_EphemeralPrivateKey, pub, ctx); - BN_CTX_free (ctx); - } void NTCP2Session::SendSessionRequest () { - // create buffer and fill padding - auto paddingLength = rand () % (287 - 64); // message length doesn't exceed 287 bytes - m_SessionRequestBufferLen = paddingLength + 64; - m_SessionRequestBuffer = new uint8_t[m_SessionRequestBufferLen]; - RAND_bytes (m_SessionRequestBuffer + 64, paddingLength); - // generate key pair (X) - uint8_t x[32]; - CreateEphemeralKey (x); - // encrypt X - i2p::crypto::CBCEncryption encryption; - encryption.SetKey (GetRemoteIdentity ()->GetIdentHash ()); - encryption.SetIV (m_IV); - encryption.Encrypt (x, 32, m_SessionRequestBuffer); - encryption.GetIV (m_IV); // save IV for SessionCreated - // encryption key for next block - uint8_t key[32]; - KeyDerivationFunction1 (m_RemoteStaticKey, m_EphemeralPrivateKey, x, key); - // fill options - uint8_t options[32]; // actual options size is 16 bytes - memset (options, 0, 16); - options[1] = 2; // ver - htobe16buf (options + 2, paddingLength); // padLen - htobe16buf (options + 4, i2p::context.GetRouterInfo ().GetBufferLen () + 20); // m3p2Len (RI header + RI + MAC for now) TODO: implement options - // 2 bytes reserved - htobe32buf (options + 8, i2p::util::GetSecondsSinceEpoch ()); // tsA - // 4 bytes reserved - // sign and encrypt options, use m_H as AD - uint8_t nonce[12]; - memset (nonce, 0, 12); // set nonce to zero - i2p::crypto::AEADChaCha20Poly1305 (options, 16, m_H, 32, key, nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt + m_Establisher->CreateSessionRequestMessage (); // send message - boost::asio::async_write (m_Socket, boost::asio::buffer (m_SessionRequestBuffer, m_SessionRequestBufferLen), boost::asio::transfer_all (), + boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionRequestBuffer, m_Establisher->m_SessionRequestBufferLen), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionRequestSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } @@ -214,9 +351,9 @@ namespace transport } else { - m_SessionCreatedBuffer = new uint8_t[287]; // TODO: determine actual max size + m_Establisher->m_SessionCreatedBuffer = new uint8_t[287]; // TODO: determine actual max size // we receive first 64 bytes (32 Y, and 32 ChaCha/Poly frame) first - boost::asio::async_read (m_Socket, boost::asio::buffer(m_SessionCreatedBuffer, 64), boost::asio::transfer_all (), + boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer, 64), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionCreatedReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } } @@ -235,24 +372,34 @@ namespace transport i2p::crypto::CBCDecryption decryption; decryption.SetKey (i2p::context.GetIdentHash ()); decryption.SetIV (i2p::context.GetNTCP2IV ()); - decryption.Decrypt (m_SessionRequestBuffer, 32, m_Y); - decryption.GetIV (m_IV); // save IV for SessionCreated + decryption.Decrypt (m_Establisher->m_SessionRequestBuffer, 32, m_Establisher->GetRemotePub ()); + decryption.GetIV (m_Establisher->m_IV); // save IV for SessionCreated // decryption key for next block - uint8_t key[32]; - KeyDerivationFunction1 (m_Y, i2p::context.GetNTCP2StaticPrivateKey (), m_Y, key); + m_Establisher->KDF1Bob (); // verify MAC and decrypt options block (32 bytes), use m_H as AD uint8_t nonce[12], options[16]; memset (nonce, 0, 12); // set nonce to zero - if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionRequestBuffer + 32, 16, m_H, 32, key, nonce, options, 16, false)) // decrypt + if (i2p::crypto::AEADChaCha20Poly1305 (m_Establisher->m_SessionRequestBuffer + 32, 16, m_Establisher->GetH (), 32, m_Establisher->GetK (), nonce, options, 16, false)) // decrypt { if (options[1] == 2) { uint16_t paddingLen = bufbe16toh (options + 2); - m_SessionRequestBufferLen = paddingLen + 64; + m_Establisher->m_SessionRequestBufferLen = paddingLen + 64; + m_Establisher->m3p2Len = bufbe16toh (options + 4); // TODO: check tsA if (paddingLen > 0) - boost::asio::async_read (m_Socket, boost::asio::buffer(m_SessionRequestBuffer + 64, paddingLen), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + { + if (paddingLen <= 287 - 64) // session request is 287 bytes max + { + boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer + 64, paddingLen), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionRequest padding length ", (int)paddingLen, " is too long"); + Terminate (); + } + } else SendSessionCreated (); } @@ -283,30 +430,9 @@ namespace transport void NTCP2Session::SendSessionCreated () { - m_SessionCreatedBuffer = new uint8_t[287]; // TODO: determine actual max size - // generate key pair (y) - uint8_t y[32]; - CreateEphemeralKey (y); - // encrypt Y - i2p::crypto::CBCEncryption encryption; - encryption.SetKey (i2p::context.GetIdentHash ()); - encryption.SetIV (m_IV); - encryption.Encrypt (y, 32, m_SessionCreatedBuffer); - // encryption key for next block (m_K) - KeyDerivationFunction2 (m_EphemeralPrivateKey, m_Y, m_SessionRequestBuffer, m_SessionRequestBufferLen, m_K); - auto paddingLen = rand () % (287 - 64); - uint8_t options[16]; - memset (options, 0, 16); - htobe16buf (options + 2, paddingLen); // padLen - htobe32buf (options + 8, i2p::util::GetSecondsSinceEpoch ()); // tsB - // sign and encrypt options, use m_H as AD - uint8_t nonce[12]; - memset (nonce, 0, 12); // set nonce to zero - i2p::crypto::AEADChaCha20Poly1305 (options, 16, m_H, 32, m_K, nonce, m_SessionCreatedBuffer + 32, 32, true); // encrypt - // fill padding - RAND_bytes (m_SessionCreatedBuffer + 56, paddingLen); + m_Establisher->CreateSessionCreatedMessage (); // send message - boost::asio::async_write (m_Socket, boost::asio::buffer (m_SessionCreatedBuffer, paddingLen + 64), boost::asio::transfer_all (), + boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionCreatedBuffer, m_Establisher->m_SessionCreatedBufferLen), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionCreatedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } @@ -320,34 +446,42 @@ namespace transport else { LogPrint (eLogDebug, "NTCP2: SessionCreated received ", bytes_transferred); - m_SessionCreatedBufferLen = 64; + m_Establisher->m_SessionCreatedBufferLen = 64; // decrypt Y i2p::crypto::CBCDecryption decryption; decryption.SetKey (GetRemoteIdentity ()->GetIdentHash ()); - decryption.SetIV (m_IV); - decryption.Decrypt (m_SessionCreatedBuffer, 32, m_Y); + decryption.SetIV (m_Establisher->m_IV); + decryption.Decrypt (m_Establisher->m_SessionCreatedBuffer, 32, m_Establisher->GetRemotePub ()); // decryption key for next block (m_K) - KeyDerivationFunction2 (m_EphemeralPrivateKey, m_Y, m_SessionRequestBuffer, m_SessionRequestBufferLen, m_K); + m_Establisher->KDF2Alice (); // decrypt and verify MAC uint8_t payload[16]; uint8_t nonce[12]; memset (nonce, 0, 12); // set nonce to zero - if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionCreatedBuffer + 32, 16, m_H, 32, m_K, nonce, payload, 16, false)) // decrypt + if (i2p::crypto::AEADChaCha20Poly1305 (m_Establisher->m_SessionCreatedBuffer + 32, 16, m_Establisher->GetH (), 32, m_Establisher->GetK (), nonce, payload, 16, false)) // decrypt { uint16_t paddingLen = bufbe16toh(payload + 2); LogPrint (eLogDebug, "NTCP2: padding length ", paddingLen); // TODO: check tsB if (paddingLen > 0) { - boost::asio::async_read (m_Socket, boost::asio::buffer(m_SessionCreatedBuffer + 64, paddingLen), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleSessionCreatedPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + if (paddingLen <= 287 - 64) // session created is 287 bytes max + { + boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer + 64, paddingLen), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleSessionCreatedPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionCreated padding length ", (int)paddingLen, " is too long"); + Terminate (); + } } else SendSessionConfirmed (); } else { - LogPrint (eLogWarning, "NTCP2: SessionCreated MAC verification failed "); + LogPrint (eLogWarning, "NTCP2: SessionCreated AEAD verification failed "); Terminate (); } } @@ -362,55 +496,20 @@ namespace transport } else { - m_SessionCreatedBufferLen += bytes_transferred; + m_Establisher->m_SessionCreatedBufferLen += bytes_transferred; SendSessionConfirmed (); } } - void NTCP2Session::SendSessionConfirmed () { - // update AD - uint8_t h[80]; - memcpy (h, m_H, 32); - memcpy (h + 32, m_SessionCreatedBuffer + 32, 32); // encrypted payload - SHA256 (h, 64, h); - int paddingLength = m_SessionCreatedBufferLen - 64; - if (paddingLength > 0) - { - std::vector h1(paddingLength + 32); - memcpy (h1.data (), h, 32); - memcpy (h1.data () + 32, m_SessionCreatedBuffer + 64, paddingLength); - SHA256 (h1.data (), paddingLength + 32, h); - } - // part1 48 bytes - m_SessionConfirmedBuffer = new uint8_t[2048]; // TODO: actual size uint8_t nonce[12]; - CreateNonce (1, nonce); - i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetNTCP2StaticPublicKey (), 32, h, 32, m_K, nonce, m_SessionConfirmedBuffer, 48, true); // encrypt - // part 2 - // update AD again - memcpy (h + 32, m_SessionConfirmedBuffer, 48); - SHA256 (h, 80, m_H); - - size_t m3p2Len = i2p::context.GetRouterInfo ().GetBufferLen () + 20; - std::vector buf(m3p2Len - 16); - buf[0] = 2; // block - htobe16buf (buf.data () + 1, i2p::context.GetRouterInfo ().GetBufferLen () + 1); // flag + RI - buf[3] = 0; // flag - memcpy (buf.data () + 4, i2p::context.GetRouterInfo ().GetBuffer (), i2p::context.GetRouterInfo ().GetBufferLen ()); - uint8_t key[32]; - KeyDerivationFunction3 (i2p::context.GetNTCP2StaticPrivateKey (), key); - memset (nonce, 0, 12); // set nonce to 0 again - i2p::crypto::AEADChaCha20Poly1305 (buf.data (), m3p2Len - 16, m_H, 32, key, nonce, m_SessionConfirmedBuffer + 48, m3p2Len, true); // encrypt - uint8_t tmp[48]; - memcpy (tmp, m_SessionConfirmedBuffer, 48); - memcpy (m_SessionConfirmedBuffer + 16, m_H, 32); // h || ciphertext - SHA256 (m_SessionConfirmedBuffer + 16, m3p2Len + 32, m_H); //h = SHA256(h || ciphertext); - memcpy (m_SessionConfirmedBuffer, tmp, 48); - + CreateNonce (1, nonce); // set nonce to 1 + m_Establisher->CreateSessionConfirmedMessagePart1 (nonce); + memset (nonce, 0, 12); // set nonce back to 0 + m_Establisher->CreateSessionConfirmedMessagePart2 (nonce); // send message - boost::asio::async_write (m_Socket, boost::asio::buffer (m_SessionConfirmedBuffer, m3p2Len + 48), boost::asio::transfer_all (), + boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionConfirmedBuffer, m_Establisher->m3p2Len + 48), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionConfirmedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } @@ -418,23 +517,147 @@ namespace transport { LogPrint (eLogDebug, "NTCP2: SessionConfirmed sent"); KeyDerivationFunctionDataPhase (); - memcpy (m_ReceiveIV, m_Sipkeysba + 16, 8); //Alice - memcpy (m_SendIV, m_Sipkeysab + 16, 8); //Alice + // Alice data phase keys + m_SendKey = m_Kab; + m_ReceiveKey = m_Kba; + m_SendSipKey = m_Sipkeysab; + m_ReceiveSipKey = m_Sipkeysba; + memcpy (m_ReceiveIV.buf, m_Sipkeysba + 16, 8); + memcpy (m_SendIV.buf, m_Sipkeysab + 16, 8); + Established (); ReceiveLength (); // TODO: remove - uint8_t pad[1024]; - auto paddingLength = rand () % 1000; - RAND_bytes (pad + 3, paddingLength); - pad[0] = 254; - htobe16buf (pad + 1, paddingLength); - SendNextFrame (pad, paddingLength + 3); + // m_SendQueue.push_back (CreateDeliveryStatusMsg (1)); + // SendQueue (); } void NTCP2Session::HandleSessionCreatedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) { - LogPrint (eLogDebug, "NTCP2: SessionCreated sent"); - Terminate (); // TODO + (void) bytes_transferred; + if (ecode) + { + LogPrint (eLogWarning, "NTCP2: couldn't send SessionCreated message: ", ecode.message ()); + Terminate (); + } + else + { + LogPrint (eLogDebug, "NTCP2: SessionCreated sent"); + m_Establisher->m_SessionConfirmedBuffer = new uint8_t[m_Establisher->m3p2Len + 48]; + boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionConfirmedBuffer, m_Establisher->m3p2Len + 48), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleSessionConfirmedReceived , shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + } + + void NTCP2Session::HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint (eLogWarning, "NTCP2: SessionConfirmed read error: ", ecode.message ()); + Terminate (); + } + else + { + LogPrint (eLogDebug, "NTCP2: SessionConfirmed received"); + // update AD + uint8_t h[80]; + memcpy (h, m_Establisher->GetH (), 32); + memcpy (h + 32, m_Establisher->m_SessionCreatedBuffer + 32, 32); // encrypted payload + SHA256 (h, 64, h); + int paddingLength = m_Establisher->m_SessionCreatedBufferLen - 64; + if (paddingLength > 0) + { + SHA256_CTX ctx; + SHA256_Init (&ctx); + SHA256_Update (&ctx, h, 32); + SHA256_Update (&ctx, m_Establisher->m_SessionCreatedBuffer + 64, paddingLength); + SHA256_Final (h, &ctx); + } + // part 1 + uint8_t nonce[12]; + CreateNonce (1, nonce); + if (i2p::crypto::AEADChaCha20Poly1305 (m_Establisher->m_SessionConfirmedBuffer, 32, h, 32, m_Establisher->GetK (), nonce, m_Establisher->m_RemoteStaticKey, 32, false)) // decrypt S + { + // part 2 + // update AD again + memcpy (h + 32, m_Establisher->m_SessionConfirmedBuffer, 48); + SHA256 (h, 80, m_Establisher->m_H); + + std::vector buf(m_Establisher->m3p2Len - 16); // -MAC + m_Establisher->KDF3Bob (); + memset (nonce, 0, 12); // set nonce to 0 again + if (i2p::crypto::AEADChaCha20Poly1305 (m_Establisher->m_SessionConfirmedBuffer + 48, m_Establisher->m3p2Len - 16, m_Establisher->GetH (), 32, m_Establisher->GetK (), nonce, buf.data (), m_Establisher->m3p2Len - 16, false)) // decrypt + { + // caclulate new h again for KDF data + memcpy (m_Establisher->m_SessionConfirmedBuffer + 16, m_Establisher->GetH (), 32); // h || ciphertext + SHA256 (m_Establisher->m_SessionConfirmedBuffer + 16, m_Establisher->m3p2Len + 32, m_Establisher->m_H); //h = SHA256(h || ciphertext); + KeyDerivationFunctionDataPhase (); + // Bob data phase keys + m_SendKey = m_Kba; + m_ReceiveKey = m_Kab; + m_SendSipKey = m_Sipkeysba; + m_ReceiveSipKey = m_Sipkeysab; + memcpy (m_ReceiveIV.buf, m_Sipkeysab + 16, 8); + memcpy (m_SendIV.buf, m_Sipkeysba + 16, 8); + + // process RI + if (buf[0] != eNTCP2BlkRouterInfo) + { + LogPrint (eLogWarning, "NTCP2: unexpected block ", (int)buf[0], " in SessionConfirmed"); + Terminate (); + return; + } + auto size = bufbe16toh (buf.data () + 1); + if (size > buf.size () - 3) + { + LogPrint (eLogError, "NTCP2: Unexpected RouterInfo size ", size, " in SessionConfirmed"); + Terminate (); + return; + } + // TODO: check flag + i2p::data::RouterInfo ri (buf.data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag + if (ri.IsUnreachable ()) + { + LogPrint (eLogError, "NTCP2: Signature verification failed in SessionConfirmed"); + SendTerminationAndTerminate (eNTCP2RouterInfoSignatureVerificationFail); + return; + } + auto addr = ri.GetNTCP2Address (false); // any NTCP2 address + if (!addr) + { + LogPrint (eLogError, "NTCP2: No NTCP2 address found in SessionConfirmed"); + Terminate (); + return; + } + if (memcmp (addr->ntcp2->staticKey, m_Establisher->m_RemoteStaticKey, 32)) + { + LogPrint (eLogError, "NTCP2: Static key mistmatch in SessionConfirmed"); + SendTerminationAndTerminate (eNTCP2IncorrectSParameter); + return; + } + + i2p::data::netdb.AddRouterInfo (buf.data () + 4, size - 1); // TODO: should insert ri and not parse it twice + // TODO: process options + + // ready to communicate + auto existing = i2p::data::netdb.FindRouter (ri.GetRouterIdentity ()->GetIdentHash ()); // check if exists already + SetRemoteIdentity (existing ? existing->GetRouterIdentity () : ri.GetRouterIdentity ()); + m_Server.AddNTCP2Session (shared_from_this ()); + Established (); + ReceiveLength (); + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 AEAD verification failed "); + Terminate (); + } + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part1 AEAD verification failed "); + Terminate (); + } + } } void NTCP2Session::ClientLogin () @@ -444,14 +667,15 @@ namespace transport void NTCP2Session::ServerLogin () { - m_SessionRequestBuffer = new uint8_t[287]; // 287 bytes max for now - boost::asio::async_read (m_Socket, boost::asio::buffer(m_SessionRequestBuffer, 64), boost::asio::transfer_all (), + m_Establisher->m_SessionRequestBuffer = new uint8_t[287]; // 287 bytes max for now + boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, 64), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } void NTCP2Session::ReceiveLength () { + if (IsTerminated ()) return; 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)); } @@ -460,15 +684,17 @@ namespace transport { if (ecode) { - LogPrint (eLogWarning, "NTCP2: receive length read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + LogPrint (eLogWarning, "NTCP2: receive length read error: ", ecode.message ()); Terminate (); } else { - i2p::crypto::Siphash<8> (m_ReceiveIV, m_ReceiveIV, 8, m_Sipkeysba); // assume Alice TODO: - m_NextReceivedLen = be16toh (m_NextReceivedLen ^ bufbe16toh(m_ReceiveIV)); + i2p::crypto::Siphash<8> (m_ReceiveIV.buf, m_ReceiveIV.buf, 8, m_ReceiveSipKey); + // m_NextRecivedLen comes from the network in BigEndian + m_NextReceivedLen = be16toh (m_NextReceivedLen) ^ le16toh (m_ReceiveIV.key); LogPrint (eLogDebug, "NTCP2: received length ", m_NextReceivedLen); - delete[] m_NextReceivedBuffer; + if (m_NextReceivedBuffer) delete[] m_NextReceivedBuffer; m_NextReceivedBuffer = new uint8_t[m_NextReceivedLen]; Receive (); } @@ -476,6 +702,7 @@ namespace transport void NTCP2Session::Receive () { + if (IsTerminated ()) return; 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)); } @@ -484,26 +711,29 @@ namespace transport { if (ecode) { - LogPrint (eLogWarning, "NTCP2: receive read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + LogPrint (eLogWarning, "NTCP2: receive read error: ", ecode.message ()); Terminate (); } else { + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); + m_NumReceivedBytes += bytes_transferred + 2; // + length + i2p::transport::transports.UpdateReceivedBytes (bytes_transferred); uint8_t nonce[12]; CreateNonce (m_ReceiveSequenceNumber, nonce); m_ReceiveSequenceNumber++; - uint8_t * decrypted = new uint8_t[m_NextReceivedLen]; - if (i2p::crypto::AEADChaCha20Poly1305 (m_NextReceivedBuffer, m_NextReceivedLen-16, nullptr, 0, m_Kba, nonce, decrypted, m_NextReceivedLen, false)) // decrypt. assume Alice TODO: + if (i2p::crypto::AEADChaCha20Poly1305 (m_NextReceivedBuffer, m_NextReceivedLen-16, nullptr, 0, m_ReceiveKey, nonce, m_NextReceivedBuffer, m_NextReceivedLen, false)) { - LogPrint (eLogInfo, "NTCP2: received message decrypted"); - ProcessNextFrame (decrypted, m_NextReceivedLen-16); + LogPrint (eLogDebug, "NTCP2: received message decrypted"); + ProcessNextFrame (m_NextReceivedBuffer, m_NextReceivedLen-16); + delete[] m_NextReceivedBuffer; m_NextReceivedBuffer = nullptr; // we don't need received buffer anymore ReceiveLength (); } else { - LogPrint (eLogWarning, "NTCP2: Received MAC verification failed "); - Terminate (); + LogPrint (eLogWarning, "NTCP2: Received AEAD verification failed "); + SendTerminationAndTerminate (eNTCP2DataPhaseAEADFailure); } - delete[] decrypted; } } @@ -522,33 +752,181 @@ namespace transport LogPrint (eLogError, "NTCP2: Unexpected block length ", size); break; } + switch (blk) + { + case eNTCP2BlkDateTime: + LogPrint (eLogDebug, "NTCP2: datetime"); + break; + case eNTCP2BlkOptions: + LogPrint (eLogDebug, "NTCP2: options"); + break; + case eNTCP2BlkRouterInfo: + { + LogPrint (eLogDebug, "NTCP2: RouterInfo flag=", (int)frame[offset]); + i2p::data::netdb.AddRouterInfo (frame + offset + 1, size - 1); + break; + } + case eNTCP2BlkI2NPMessage: + { + LogPrint (eLogDebug, "NTCP2: I2NP"); + auto nextMsg = NewI2NPMessage (size); + nextMsg->len = nextMsg->offset + size + 7; // 7 more bytes for full I2NP header + memcpy (nextMsg->GetNTCP2Header (), frame + offset, size); + nextMsg->FromNTCP2 (); + m_Handler.PutNextMessage (nextMsg); + break; + } + case eNTCP2BlkTermination: + if (size >= 9) + { + LogPrint (eLogDebug, "NTCP2: termination. reason=", (int)(frame[offset + 8])); + Terminate (); + } + else + LogPrint (eLogWarning, "NTCP2: Unexpected temination block size ", size); + break; + case eNTCP2BlkPadding: + LogPrint (eLogDebug, "NTCP2: padding"); + break; + default: + LogPrint (eLogWarning, "NTCP2: Unknown block type ", (int)blk); + } offset += size; } + m_Handler.Flush (); } void NTCP2Session::SendNextFrame (const uint8_t * payload, size_t len) { + if (IsTerminated ()) return; uint8_t nonce[12]; CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; m_NextSendBuffer = new uint8_t[len + 16 + 2]; - i2p::crypto::AEADChaCha20Poly1305 (payload, len, nullptr, 0, m_Kab, nonce, m_NextSendBuffer + 2, len + 16, true); // encrypt. assume Alice TODO: - i2p::crypto::Siphash<8> (m_SendIV, m_SendIV, 8, m_Sipkeysab); // assume Alice TODO: - htobuf16 (m_NextSendBuffer, bufbe16toh (m_SendIV) ^ htobe16(len + 16)); + i2p::crypto::AEADChaCha20Poly1305 (payload, len, nullptr, 0, m_SendKey, nonce, m_NextSendBuffer + 2, len + 16, true); + i2p::crypto::Siphash<8> (m_SendIV.buf, m_SendIV.buf, 8, m_SendSipKey); + // length must be in BigEndian + htobe16buf (m_NextSendBuffer, (len + 16) ^ le16toh (m_SendIV.key)); LogPrint (eLogDebug, "NTCP2: sent length ", len + 16); // send message + m_IsSending = true; boost::asio::async_write (m_Socket, boost::asio::buffer (m_NextSendBuffer, len + 16 + 2), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleNextFrameSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } void NTCP2Session::HandleNextFrameSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) { + m_IsSending = false; delete[] m_NextSendBuffer; m_NextSendBuffer = nullptr; - LogPrint (eLogDebug, "NTCP2: Next frame sent"); + + if (ecode) + { + LogPrint (eLogWarning, "NTCP2: Couldn't send frame ", ecode.message ()); + } + else + { + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); + m_NumSentBytes += bytes_transferred; + i2p::transport::transports.UpdateSentBytes (bytes_transferred); + LogPrint (eLogDebug, "NTCP2: Next frame sent"); + SendQueue (); + } + } + + void NTCP2Session::SendQueue () + { + if (!m_SendQueue.empty ()) + { + auto buf = m_Server.NewNTCP2FrameBuffer (); + uint8_t * payload = buf->data (); + size_t s = 0; + // add I2NP blocks + while (!m_SendQueue.empty ()) + { + auto msg = m_SendQueue.front (); + size_t len = msg->GetNTCP2Length (); + if (s + len + 3 <= NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) // 3 bytes block header + { + payload[s] = eNTCP2BlkI2NPMessage; // blk + htobe16buf (payload + s + 1, len); // size + s += 3; + msg->ToNTCP2 (); + memcpy (payload + s, msg->GetNTCP2Header (), len); + s += len; + m_SendQueue.pop_front (); + } + else + break; + } + // add padding block + int paddingSize = (s*NTCP2_MAX_PADDING_RATIO)/100; + if (s + paddingSize + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) paddingSize = NTCP2_UNENCRYPTED_FRAME_MAX_SIZE - s -3; + if (paddingSize) paddingSize = rand () % paddingSize; + payload[s] = eNTCP2BlkPadding; // blk + htobe16buf (payload + s + 1, paddingSize); // size + s += 3; + RAND_bytes (payload + s, paddingSize); + s += paddingSize; + // send + SendNextFrame (payload, s); + m_Server.DeleteNTCP2FrameBuffer (buf); + } + } + + void NTCP2Session::SendRouterInfo () + { + auto riLen = i2p::context.GetRouterInfo ().GetBufferLen (); + int paddingSize = (riLen*NTCP2_MAX_PADDING_RATIO)/100; + size_t payloadLen = riLen + paddingSize + 7; // 7 = 2*3 bytes header + 1 byte RI flag + uint8_t * payload = new uint8_t[payloadLen]; + payload[0] = eNTCP2BlkRouterInfo; + htobe16buf (payload + 1, riLen + 1); // size + payload[3] = 0; // flag + memcpy (payload + 4, i2p::context.GetRouterInfo ().GetBuffer (), riLen); + payload[riLen + 4] = eNTCP2BlkPadding; + htobe16buf (payload + riLen + 5, paddingSize); + RAND_bytes (payload + riLen + 7, paddingSize); + SendNextFrame (payload, payloadLen); + delete[] payload; + } + + void NTCP2Session::SendTermination (NTCP2TerminationReason reason) + { + uint8_t payload[12] = { eNTCP2BlkTermination, 0, 9 }; + htobe64buf (payload + 3, m_ReceiveSequenceNumber); + payload[11] = (uint8_t)reason; + SendNextFrame (payload, 12); + } + + void NTCP2Session::SendTerminationAndTerminate (NTCP2TerminationReason reason) + { + SendTermination (reason); + m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ())); // let termination message go + } + + void NTCP2Session::SendI2NPMessages (const std::vector >& msgs) + { + m_Server.GetService ().post (std::bind (&NTCP2Session::PostI2NPMessages, shared_from_this (), msgs)); + } + + void NTCP2Session::PostI2NPMessages (std::vector > msgs) + { + if (m_IsTerminated) return; + for (auto it: msgs) + m_SendQueue.push_back (it); + if (!m_IsSending) + SendQueue (); + } + + void NTCP2Session::SendLocalRouterInfo () + { + if (!IsOutgoing ()) // we send it in SessionConfirmed + SendRouterInfo (); } NTCP2Server::NTCP2Server (): - m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service) + m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), + m_TerminationTimer (m_Service) { } @@ -563,14 +941,68 @@ namespace transport { m_IsRunning = true; m_Thread = new std::thread (std::bind (&NTCP2Server::Run, this)); + auto& addresses = context.GetRouterInfo ().GetAddresses (); + for (const auto& address: addresses) + { + if (!address) continue; + if (address->IsPublishedNTCP2 ()) + { + if (address->host.is_v4()) + { + try + { + m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port))); + } + catch ( std::exception & ex ) + { + LogPrint(eLogError, "NTCP2: Failed to bind to ip4 port ",address->port, ex.what()); + continue; + } + + LogPrint (eLogInfo, "NTCP2: Start listening TCP port ", address->port); + auto conn = std::make_shared(*this); + m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1)); + } + else if (address->host.is_v6() && context.SupportsV6 ()) + { + m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (m_Service)); + try + { + m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6()); + m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true)); + m_NTCP2V6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port)); + m_NTCP2V6Acceptor->listen (); + + LogPrint (eLogInfo, "NTCP2: Start listening V6 TCP port ", address->port); + auto conn = std::make_shared (*this); + m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, conn, std::placeholders::_1)); + } catch ( std::exception & ex ) { + LogPrint(eLogError, "NTCP: failed to bind to ip6 port ", address->port); + continue; + } + } + } + } + ScheduleTermination (); } } void NTCP2Server::Stop () { + { + // we have to copy it because Terminate changes m_NTCP2Sessions + auto ntcpSessions = m_NTCP2Sessions; + for (auto& it: ntcpSessions) + it.second->Terminate (); + for (auto& it: m_PendingIncomingSessions) + it->Terminate (); + } + m_NTCP2Sessions.clear (); + if (m_IsRunning) { m_IsRunning = false; + m_TerminationTimer.cancel (); m_Service.stop (); if (m_Thread) { @@ -596,17 +1028,63 @@ namespace transport } } + bool NTCP2Server::AddNTCP2Session (std::shared_ptr session) + { + if (!session || !session->GetRemoteIdentity ()) return false; + auto& ident = session->GetRemoteIdentity ()->GetIdentHash (); + auto it = m_NTCP2Sessions.find (ident); + if (it != m_NTCP2Sessions.end ()) + { + LogPrint (eLogWarning, "NTCP2: session to ", ident.ToBase64 (), " already exists"); + session->Terminate(); + return false; + } + m_NTCP2Sessions.insert (std::make_pair (ident, session)); + return true; + } + + void NTCP2Server::RemoveNTCP2Session (std::shared_ptr session) + { + if (session && session->GetRemoteIdentity ()) + m_NTCP2Sessions.erase (session->GetRemoteIdentity ()->GetIdentHash ()); + } + + std::shared_ptr NTCP2Server::FindNTCP2Session (const i2p::data::IdentHash& ident) + { + auto it = m_NTCP2Sessions.find (ident); + if (it != m_NTCP2Sessions.end ()) + return it->second; + return nullptr; + } + void NTCP2Server::Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr conn) { LogPrint (eLogDebug, "NTCP2: Connecting to ", address ,":", port); m_Service.post([this, address, port, conn]() { - conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port), std::bind (&NTCP2Server::HandleConnect, this, std::placeholders::_1, conn)); + if (this->AddNTCP2Session (conn)) + { + auto timer = std::make_shared(m_Service); + auto timeout = NTCP2_CONNECT_TIMEOUT * 5; + conn->SetTerminationTimeout(timeout * 2); + timer->expires_from_now (boost::posix_time::seconds(timeout)); + timer->async_wait ([conn, timeout](const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + LogPrint (eLogInfo, "NTCP2: Not connected in ", timeout, " seconds"); + //i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true); + conn->Terminate (); + } + }); + conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port), std::bind (&NTCP2Server::HandleConnect, this, std::placeholders::_1, conn, timer)); + } }); } - void NTCP2Server::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn) + void NTCP2Server::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer) { + timer->cancel (); if (ecode) { LogPrint (eLogInfo, "NTCP2: Connect error ", ecode.message ()); @@ -618,6 +1096,98 @@ namespace transport conn->ClientLogin (); } } + + void NTCP2Server::HandleAccept (std::shared_ptr conn, const boost::system::error_code& error) + { + if (!error) + { + boost::system::error_code ec; + auto ep = conn->GetSocket ().remote_endpoint(ec); + if (!ec) + { + LogPrint (eLogDebug, "NTCP2: Connected from ", ep); + if (conn) + { + conn->ServerLogin (); + m_PendingIncomingSessions.push_back (conn); + } + } + else + LogPrint (eLogError, "NTCP2: Connected from error ", ec.message ()); + } + + if (error != boost::asio::error::operation_aborted) + { + conn = std::make_shared (*this); + m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, + conn, std::placeholders::_1)); + } + } + + void NTCP2Server::HandleAcceptV6 (std::shared_ptr conn, const boost::system::error_code& error) + { + if (!error) + { + boost::system::error_code ec; + auto ep = conn->GetSocket ().remote_endpoint(ec); + if (!ec) + { + LogPrint (eLogDebug, "NTCP2: Connected from ", ep); + if (conn) + { + conn->ServerLogin (); + m_PendingIncomingSessions.push_back (conn); + } + } + else + LogPrint (eLogError, "NTCP2: Connected from error ", ec.message ()); + } + + if (error != boost::asio::error::operation_aborted) + { + conn = std::make_shared (*this); + m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, + conn, std::placeholders::_1)); + } + } + + void NTCP2Server::ScheduleTermination () + { + m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP2_TERMINATION_CHECK_TIMEOUT)); + m_TerminationTimer.async_wait (std::bind (&NTCP2Server::HandleTerminationTimer, + this, std::placeholders::_1)); + } + + void NTCP2Server::HandleTerminationTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + auto ts = i2p::util::GetSecondsSinceEpoch (); + // established + for (auto& it: m_NTCP2Sessions) + if (it.second->IsTerminationTimeoutExpired (ts)) + { + auto session = it.second; + LogPrint (eLogDebug, "NTCP2: No activity for ", session->GetTerminationTimeout (), " seconds"); + session->TerminateByTimeout (); // it doesn't change m_NTCP2Session right a way + } + // pending + for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();) + { + if ((*it)->IsEstablished () || (*it)->IsTerminated ()) + it = m_PendingIncomingSessions.erase (it); // established or terminated + else if ((*it)->IsTerminationTimeoutExpired (ts)) + { + (*it)->Terminate (); + it = m_PendingIncomingSessions.erase (it); // expired + } + else + it++; + } + + ScheduleTermination (); + } + } } } diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h index 6ee76aad..3e202dc5 100644 --- a/libi2pd/NTCP2.h +++ b/libi2pd/NTCP2.h @@ -4,7 +4,12 @@ #include #include #include +#include +#include +#include +#include #include +#include "util.h" #include "RouterInfo.h" #include "TransportSession.h" @@ -12,6 +17,91 @@ namespace i2p { namespace transport { + + const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519; + const int NTCP2_MAX_PADDING_RATIO = 6; // in % + + const int NTCP2_CONNECT_TIMEOUT = 5; // 5 seconds + const int NTCP2_ESTABLISH_TIMEOUT = 10; // 10 seconds + const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes + const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds + + enum NTCP2BlockType + { + eNTCP2BlkDateTime = 0, + eNTCP2BlkOptions, // 1 + eNTCP2BlkRouterInfo, // 2 + eNTCP2BlkI2NPMessage, // 3 + eNTCP2BlkTermination, // 4 + eNTCP2BlkPadding = 254 + }; + + enum NTCP2TerminationReason + { + eNTCP2NormalClose = 0, + eNTCP2TerminationReceived, // 1 + eNTCP2IdleTimeout, // 2 + eNTCP2RouterShutdown, // 3 + eNTCP2DataPhaseAEADFailure, // 4 + eNTCP2IncompatibleOptions, // 5 + eNTCP2IncompatibleSignatureType, // 6 + eNTCP2ClockSkew, // 7 + eNTCP2PaddingViolation, // 8 + eNTCP2AEADFramingError, // 9 + eNTCP2PayloadFormatError, // 10 + eNTCP2Message1Error, // 11 + eNTCP2Message2Error, // 12 + eNTCP2Message3Error, // 13 + eNTCP2IntraFrameReadTimeout, // 14 + eNTCP2RouterInfoSignatureVerificationFail, // 15 + eNTCP2IncorrectSParameter, // 16 + eNTCP2Banned, // 17 + }; + + + typedef std::array NTCP2FrameBuffer; + struct NTCP2Establisher + { + NTCP2Establisher (); + ~NTCP2Establisher (); + + const uint8_t * GetPub () const { return m_EphemeralPublicKey; }; + const uint8_t * GetPriv () const { return m_EphemeralPrivateKey; }; + const uint8_t * GetRemotePub () const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob + uint8_t * GetRemotePub () { return m_RemoteEphemeralPublicKey; }; // to set + + const uint8_t * GetK () const { return m_K; }; + const uint8_t * GetCK () const { return m_CK; }; + const uint8_t * GetH () const { return m_H; }; + + void KDF1Alice (); + void KDF1Bob (); + void KDF2Alice (); + void KDF2Bob (); + void KDF3Alice (); // for SessionConfirmed part 2 + void KDF3Bob (); + + void MixKey (const uint8_t * inputKeyMaterial, uint8_t * derived); + void KeyDerivationFunction1 (const uint8_t * pub, const uint8_t * 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 (); + + void CreateSessionRequestMessage (); + void CreateSessionCreatedMessage (); + void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce); + void CreateSessionConfirmedMessagePart2 (const uint8_t * nonce); + + BN_CTX * m_Ctx; + uint8_t m_EphemeralPrivateKey[32], m_EphemeralPublicKey[32], m_RemoteEphemeralPublicKey[32]; // x25519 + uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[33] /*ck*/, m_K[32] /*k*/; + i2p::data::IdentHash m_RemoteIdentHash; + uint16_t m3p2Len; + + uint8_t * m_SessionRequestBuffer, * m_SessionCreatedBuffer, * m_SessionConfirmedBuffer; + size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen; + + }; + class NTCP2Server; class NTCP2Session: public TransportSession, public std::enable_shared_from_this { @@ -20,25 +110,28 @@ namespace transport NTCP2Session (NTCP2Server& server, std::shared_ptr in_RemoteRouter = nullptr); ~NTCP2Session (); void Terminate (); + void TerminateByTimeout (); void Done (); boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; + bool IsEstablished () const { return m_IsEstablished; }; + bool IsTerminated () const { return m_IsTerminated; }; + void ClientLogin (); // Alice void ServerLogin (); // Bob - void SendI2NPMessages (const std::vector >& msgs) {}; // TODO + + void SendLocalRouterInfo (); // after handshake + void SendI2NPMessages (const std::vector >& msgs); private: - void MixKey (const uint8_t * inputKeyMaterial, uint8_t * derived); + void Established (); + void CreateNonce (uint64_t seqn, uint8_t * nonce); - void KeyDerivationFunction1 (const uint8_t * rs, const uint8_t * priv, const uint8_t * pub, uint8_t * derived); // for SessionRequest - void KeyDerivationFunction2 (const uint8_t * priv, const uint8_t * pub, const uint8_t * sessionRequest, size_t sessionRequestLen, uint8_t * derived); // for SessionCreate - void KeyDerivationFunction3 (const uint8_t * staticPrivKey, uint8_t * derived); // for SessionConfirmed part 2 void KeyDerivationFunctionDataPhase (); // establish - void CreateEphemeralKey (uint8_t * pub); void SendSessionRequest (); void SendSessionCreated (); void SendSessionConfirmed (); @@ -50,6 +143,7 @@ namespace transport void HandleSessionCreatedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionConfirmedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); // data void ReceiveLength (); @@ -60,6 +154,11 @@ namespace transport void SendNextFrame (const uint8_t * payload, size_t len); void HandleNextFrameSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void SendQueue (); + void SendRouterInfo (); + void SendTermination (NTCP2TerminationReason reason); + void SendTerminationAndTerminate (NTCP2TerminationReason reason); + void PostI2NPMessages (std::vector > msgs); private: @@ -67,16 +166,23 @@ namespace transport boost::asio::ip::tcp::socket m_Socket; bool m_IsEstablished, m_IsTerminated; - uint8_t m_EphemeralPrivateKey[32]; // x25519 - uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[33] /*ck*/, m_K[32] /* derived after SessionCreated */, m_Y[32] /* or X for Bob */; - uint8_t * m_SessionRequestBuffer, * m_SessionCreatedBuffer, * m_SessionConfirmedBuffer; - size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen; + std::unique_ptr m_Establisher; // data phase uint8_t m_Kab[33], m_Kba[32], m_Sipkeysab[33], m_Sipkeysba[32]; + const uint8_t * m_SendKey, * m_ReceiveKey, * m_SendSipKey, * m_ReceiveSipKey; uint16_t m_NextReceivedLen; uint8_t * m_NextReceivedBuffer, * m_NextSendBuffer; - uint8_t m_ReceiveIV[8], m_SendIV[8]; + union + { + uint8_t buf[8]; + uint16_t key; + } m_ReceiveIV, m_SendIV; uint64_t m_ReceiveSequenceNumber, m_SendSequenceNumber; + + i2p::I2NPMessagesHandler m_Handler; + + bool m_IsSending; + std::list > m_SendQueue; }; class NTCP2Server @@ -89,14 +195,28 @@ namespace transport void Start (); void Stop (); + bool AddNTCP2Session (std::shared_ptr session); + void RemoveNTCP2Session (std::shared_ptr session); + std::shared_ptr FindNTCP2Session (const i2p::data::IdentHash& ident); + boost::asio::io_service& GetService () { return m_Service; }; void Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr conn); + NTCP2FrameBuffer * NewNTCP2FrameBuffer () { return m_NTCP2FrameBuffersPool.Acquire(); } + void DeleteNTCP2FrameBuffer (NTCP2FrameBuffer * buf) { return m_NTCP2FrameBuffersPool.Release(buf); } + private: void Run (); - void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn); + void HandleAccept (std::shared_ptr conn, const boost::system::error_code& error); + void HandleAcceptV6 (std::shared_ptr conn, const boost::system::error_code& error); + + void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer); + + // timer + void ScheduleTermination (); + void HandleTerminationTimer (const boost::system::error_code& ecode); private: @@ -104,6 +224,17 @@ namespace transport std::thread * m_Thread; boost::asio::io_service m_Service; boost::asio::io_service::work m_Work; + boost::asio::deadline_timer m_TerminationTimer; + std::unique_ptr m_NTCP2Acceptor, m_NTCP2V6Acceptor; + std::map > m_NTCP2Sessions; + std::list > m_PendingIncomingSessions; + + i2p::util::MemoryPool m_NTCP2FrameBuffersPool; + + public: + + // for HTTP/I2PControl + const decltype(m_NTCP2Sessions)& GetNTCP2Sessions () const { return m_NTCP2Sessions; }; }; } } diff --git a/libi2pd/NTCPSession.cpp b/libi2pd/NTCPSession.cpp index a297ad6a..caddd110 100644 --- a/libi2pd/NTCPSession.cpp +++ b/libi2pd/NTCPSession.cpp @@ -819,7 +819,7 @@ namespace transport for (const auto& address: addresses) { if (!address) continue; - if (address->transportStyle == i2p::data::RouterInfo::eTransportNTCP) + if (address->transportStyle == i2p::data::RouterInfo::eTransportNTCP && !address->IsNTCP2 ()) { if (address->host.is_v4()) { diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index b136dfd5..410b71e6 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -734,7 +734,7 @@ namespace data m_Requests.RequestComplete (ident, nullptr); } else - // no more requests for detination possible. delete it + // no more requests for destination possible. delete it m_Requests.RequestComplete (ident, nullptr); } else if(!m_FloodfillBootstrap) diff --git a/libi2pd/Poly1305.cpp b/libi2pd/Poly1305.cpp index 5378d3cb..9ee46640 100644 --- a/libi2pd/Poly1305.cpp +++ b/libi2pd/Poly1305.cpp @@ -133,9 +133,14 @@ namespace crypto struct Poly1305 { - +#if (__GNUC__ == 4) && (__GNUC_MINOR__ < 8) // older than gcc 4.8 + Poly1305(const uint8_t * key) : m_Leftover(0), m_Final(0) + { + memset (&m_H, 0, sizeof (m_H)); +#else Poly1305(const uint8_t * key) : m_Leftover(0), m_H{0}, m_Final(0) { +#endif m_R.PutKey(key); m_Pad.Put(key + 16); } diff --git a/libi2pd/Reseed.cpp b/libi2pd/Reseed.cpp index 4a2f8055..eebe9b38 100644 --- a/libi2pd/Reseed.cpp +++ b/libi2pd/Reseed.cpp @@ -300,7 +300,7 @@ namespace data s.read (localFileName, fileNameLength); localFileName[fileNameLength] = 0; s.seekg (extraFieldLength, std::ios::cur); - // take care about data desriptor if presented + // take care about data descriptor if presented if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) { size_t pos = s.tellg (); diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 9bd6da9f..b2774aa2 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -50,7 +50,9 @@ namespace i2p port = rand () % (30777 - 9111) + 9111; // I2P network ports range bool ipv4; i2p::config::GetOption("ipv4", ipv4); bool ipv6; i2p::config::GetOption("ipv6", ipv6); - bool ntcp2; i2p::config::GetOption("ntcp2", ntcp2); + bool ssu; i2p::config::GetOption("ssu", ssu); + bool ntcp; i2p::config::GetOption("ntcp", ntcp); + bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); bool nat; i2p::config::GetOption("nat", nat); std::string ifname; i2p::config::GetOption("ifname", ifname); std::string ifname4; i2p::config::GetOption("ifname4", ifname4); @@ -67,8 +69,10 @@ namespace i2p if(ifname4.size()) host = i2p::util::net::GetInterfaceAddress(ifname4, false).to_string(); - routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); - routerInfo.AddNTCPAddress (host.c_str(), port); + if (ssu) + routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); + if (ntcp) + routerInfo.AddNTCPAddress (host.c_str(), port); } if (ipv6) { @@ -81,8 +85,10 @@ namespace i2p if(ifname6.size()) host = i2p::util::net::GetInterfaceAddress(ifname6, true).to_string(); - routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); - routerInfo.AddNTCPAddress (host.c_str(), port); + if (ssu) + routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); + if (ntcp) + routerInfo.AddNTCPAddress (host.c_str(), port); } routerInfo.SetCaps (i2p::data::RouterInfo::eReachable | @@ -93,11 +99,12 @@ namespace i2p m_RouterInfo.SetRouterIdentity (GetIdentity ()); m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); - if (ntcp2) + if (ntcp2) // we don't store iv in the address if non published so we must update it from keys { - NewNTCP2Keys (); - m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); + if (!m_NTCP2Keys) NewNTCP2Keys (); + UpdateNTCP2Address (true); } + } void RouterContext::UpdateRouterInfo () @@ -145,7 +152,7 @@ namespace i2p bool updated = false; for (auto& address : m_RouterInfo.GetAddresses ()) { - if (address->port != port) + if (!address->IsNTCP2 () && address->port != port) { address->port = port; updated = true; @@ -155,6 +162,50 @@ namespace i2p UpdateRouterInfo (); } + void RouterContext::PublishNTCP2Address (int port, bool publish) + { + if (!port) + port = rand () % (30777 - 9111) + 9111; // I2P network ports range + bool updated = false; + for (auto& address : m_RouterInfo.GetAddresses ()) + { + if (address->IsNTCP2 () && (address->port != port || address->ntcp2->isPublished != publish)) + { + address->port = port; + address->ntcp2->isPublished = publish; + updated = true; + } + } + if (updated) + UpdateRouterInfo (); + } + + void RouterContext::UpdateNTCP2Address (bool enable) + { + auto& addresses = m_RouterInfo.GetAddresses (); + bool found = false, updated = false; + for (auto it = addresses.begin (); it != addresses.end (); ++it) + { + if ((*it)->IsNTCP2 ()) + { + found = true; + if (!enable) + { + addresses.erase (it); + updated= true; + } + break; + } + } + if (enable && !found) + { + m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); + updated = true; + } + if (updated) + UpdateRouterInfo (); + } + void RouterContext::UpdateAddress (const boost::asio::ip::address& host) { bool updated = false; @@ -291,7 +342,7 @@ namespace i2p auto& addresses = m_RouterInfo.GetAddresses (); for (auto it = addresses.begin (); it != addresses.end (); ++it) { - if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP && + if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP && !(*it)->IsNTCP2 () && (*it)->host.is_v4 ()) { addresses.erase (it); @@ -318,16 +369,19 @@ namespace i2p caps |= i2p::data::RouterInfo::eFloodfill; m_RouterInfo.SetCaps (caps); - // insert NTCP back auto& addresses = m_RouterInfo.GetAddresses (); - for (const auto& addr : addresses) - { - if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU && - addr->host.is_v4 ()) + // insert NTCP back + bool ntcp; i2p::config::GetOption("ntcp", ntcp); + if (ntcp) { + for (const auto& addr : addresses) { - // insert NTCP address with host/port from SSU - m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port); - break; + if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU && + addr->host.is_v4 ()) + { + // insert NTCP address with host/port from SSU + m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port); + break; + } } } // delete previous introducers @@ -429,7 +483,21 @@ namespace i2p m_Keys.FromBuffer (buf, len); delete[] buf; } - + // 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(); + n2k.seekg (0, std::ios::beg); + if (len == sizeof (NTCP2PrivateKeys)) + { + m_NTCP2Keys.reset (new NTCP2PrivateKeys ()); + n2k.read ((char *)m_NTCP2Keys.get (), sizeof (NTCP2PrivateKeys)); + } + n2k.close (); + } + // read RouterInfo m_RouterInfo.SetRouterIdentity (GetIdentity ()); i2p::data::RouterInfo routerInfo(i2p::fs::DataDirPath (ROUTER_INFO)); if (!routerInfo.IsUnreachable ()) // router.info looks good @@ -452,28 +520,14 @@ namespace i2p SetReachable (); // we assume reachable until we discover firewall through peer tests // read NTCP2 - bool ntcp2; i2p::config::GetOption("ntcp2", ntcp2); + bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); if (ntcp2) { - std::ifstream n2k (i2p::fs::DataDirPath (NTCP2_KEYS), std::ifstream::in | std::ifstream::binary); - if (n2k) - { - n2k.seekg (0, std::ios::end); - len = fk.tellg(); - n2k.seekg (0, std::ios::beg); - if (len == sizeof (NTCP2PrivateKeys)) - { - m_NTCP2Keys.reset (new NTCP2PrivateKeys ()); - n2k.read ((char *)m_NTCP2Keys.get (), sizeof (NTCP2PrivateKeys)); - } - n2k.close (); - } - if (!m_NTCP2Keys) - { - NewNTCP2Keys (); - m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); - } + if (!m_NTCP2Keys) NewNTCP2Keys (); + UpdateNTCP2Address (true); // enable NTCP2 } + else + UpdateNTCP2Address (false); // disable NTCP2 return true; } diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index ae4aa17e..f1a62c5a 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -76,8 +76,10 @@ namespace i2p void SetNetID (int netID) { m_NetID = netID; }; bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const; - void UpdatePort (int port); // called from Daemon + void UpdatePort (int port); // called from Daemon void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon + void PublishNTCP2Address (int port, bool publish = true); + void UpdateNTCP2Address (bool enable); bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer); void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); bool IsUnreachable () const; diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index a0b818fb..95e88963 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -176,13 +176,13 @@ namespace data auto address = std::make_shared
(); s.read ((char *)&address->cost, sizeof (address->cost)); s.read ((char *)&address->date, sizeof (address->date)); - bool isNtcp2 = false; + bool isNTCP2Only = false; char transportStyle[6]; auto transportStyleLen = ReadString (transportStyle, 6, s) - 1; if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2 { address->transportStyle = eTransportNTCP; - if (transportStyleLen > 4 || transportStyle[4] == '2') isNtcp2= true; + if (transportStyleLen > 4 && transportStyle[4] == '2') isNTCP2Only= true; } else if (!strcmp (transportStyle, "SSU")) { @@ -259,6 +259,7 @@ namespace data if (!address->ntcp2) address->ntcp2.reset (new NTCP2Ext ()); supportedTransports |= (address->host.is_v4 ()) ? eNTCP2V4 : eNTCP2V6; Base64ToByteStream (value, strlen (value), address->ntcp2->iv, 16); + address->ntcp2->isPublished = true; // presence if "i" means "published" } else if (key[0] == 'i') { @@ -292,7 +293,8 @@ namespace data if (!s) return; } if (introducers) supportedTransports |= eSSUV4; // in case if host is not presented - if (supportedTransports && !isNtcp2) // we ignore NTCP2 addresses for now. TODO: + if (isNTCP2Only && address->ntcp2) address->ntcp2->isNTCP2Only = true; + if (supportedTransports) { addresses->push_back(address); m_SupportedTransports |= supportedTransports; @@ -455,7 +457,7 @@ namespace data else WriteString ("", s); - if (!address.IsNTCP2 ()) // we don't publish NTCP2 address fow now. TODO: implement + if (!address.IsNTCP2 () || address.IsPublishedNTCP2 ()) { WriteString ("host", properties); properties << '='; @@ -537,7 +539,14 @@ namespace data } } - if (!address.IsNTCP2 ()) // we don't publish NTCP2 address fow now. TODO: implement + if (address.IsPublishedNTCP2 ()) + { + // publish i for NTCP2 + WriteString ("i", properties); properties << '='; + WriteString (address.ntcp2->iv.ToBase64 (), properties); properties << ';'; + } + + if (!address.IsNTCP2 () || address.IsPublishedNTCP2 ()) { WriteString ("port", properties); properties << '='; @@ -551,7 +560,6 @@ namespace data WriteString (address.ntcp2->staticKey.ToBase64 (), properties); properties << ';'; WriteString ("v", properties); properties << '='; WriteString ("2", properties); properties << ';'; - // TODO: publish "i" } uint16_t size = htobe16 (properties.str ().size ()); @@ -665,7 +673,7 @@ namespace data for (const auto& it: *m_Addresses) // don't insert same address twice if (*it == *addr) return; m_SupportedTransports |= addr->host.is_v6 () ? eNTCPV6 : eNTCPV4; - m_Addresses->push_back(std::move(addr)); + m_Addresses->push_front(std::move(addr)); // always make NTCP first } void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu) @@ -698,6 +706,7 @@ namespace data addr->cost = 14; addr->date = 0; addr->ntcp2.reset (new NTCP2Ext ()); + addr->ntcp2->isNTCP2Only = true; // NTCP2 only address memcpy (addr->ntcp2->staticKey, staticKey, 32); memcpy (addr->ntcp2->iv, iv, 16); m_Addresses->push_back(std::move(addr)); @@ -853,37 +862,55 @@ namespace data std::shared_ptr RouterInfo::GetNTCPAddress (bool v4only) const { - return GetAddress (eTransportNTCP, v4only); + return GetAddress ( + [v4only](std::shared_ptr address)->bool + { + return (address->transportStyle == eTransportNTCP) && !address->IsNTCP2Only () && (!v4only || address->host.is_v4 ()); + }); } std::shared_ptr RouterInfo::GetSSUAddress (bool v4only) const { - return GetAddress (eTransportSSU, v4only); + return GetAddress ( + [v4only](std::shared_ptr address)->bool + { + return (address->transportStyle == eTransportSSU) && (!v4only || address->host.is_v4 ()); + }); } std::shared_ptr RouterInfo::GetSSUV6Address () const { - return GetAddress (eTransportSSU, false, true); + return GetAddress ( + [](std::shared_ptr address)->bool + { + return (address->transportStyle == eTransportSSU) && address->host.is_v6 (); + }); } - std::shared_ptr RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const + template + std::shared_ptr RouterInfo::GetAddress (Filter filter) const { + // TODO: make it more gereric using comparator #if (BOOST_VERSION >= 105300) auto addresses = boost::atomic_load (&m_Addresses); #else auto addresses = m_Addresses; #endif for (const auto& address : *addresses) - { - if (address->transportStyle == s) - { - if ((!v4only || address->host.is_v4 ()) && (!v6only || address->host.is_v6 ())) - return address; - } - } + if (filter (address)) return address; + return nullptr; } + std::shared_ptr RouterInfo::GetNTCP2Address (bool publishedOnly, bool v4only) const + { + return GetAddress ( + [publishedOnly, v4only](std::shared_ptr address)->bool + { + return address->IsNTCP2 () && (!publishedOnly || address->IsPublishedNTCP2 ()) && (!v4only || address->host.is_v4 ()); + }); + } + std::shared_ptr RouterInfo::GetProfile () const { if (!m_Profile) diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index a12b23e3..47a5d680 100644 --- a/libi2pd/RouterInfo.h +++ b/libi2pd/RouterInfo.h @@ -94,6 +94,8 @@ namespace data { Tag<32> staticKey; Tag<16> iv; + bool isPublished = false; + bool isNTCP2Only = false; }; struct Address @@ -124,6 +126,8 @@ namespace data } bool IsNTCP2 () const { return (bool)ntcp2; }; + bool IsPublishedNTCP2 () const { return IsNTCP2 () && ntcp2->isPublished; }; + bool IsNTCP2Only () const { return ntcp2 && ntcp2->isNTCP2Only; }; }; typedef std::list > Addresses; @@ -140,6 +144,7 @@ namespace data uint64_t GetTimestamp () const { return m_Timestamp; }; Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr std::shared_ptr GetNTCPAddress (bool v4only = true) const; + std::shared_ptr GetNTCP2Address (bool publishedOnly, bool v4only = true) const; std::shared_ptr GetSSUAddress (bool v4only = true) const; std::shared_ptr GetSSUV6Address () const; @@ -213,7 +218,8 @@ namespace data size_t ReadString (char* str, size_t len, std::istream& s) const; void WriteString (const std::string& str, std::ostream& s) const; void ExtractCaps (const char * value); - std::shared_ptr GetAddress (TransportStyle s, bool v4only, bool v6only = false) const; + template + std::shared_ptr GetAddress (Filter filter) const; void UpdateCapsProperty (); private: diff --git a/libi2pd/TransportSession.h b/libi2pd/TransportSession.h index 62bed352..49067ce2 100644 --- a/libi2pd/TransportSession.h +++ b/libi2pd/TransportSession.h @@ -79,6 +79,7 @@ namespace transport bool IsTerminationTimeoutExpired (uint64_t ts) const { return ts >= m_LastActivityTimestamp + GetTerminationTimeout (); }; + virtual void SendLocalRouterInfo () { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); }; virtual void SendI2NPMessages (const std::vector >& msgs) = 0; protected: diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index 9914c75e..6a833ae0 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -117,7 +117,8 @@ namespace transport Transports::Transports (): m_IsOnline (true), m_IsRunning (false), m_IsNAT (true), m_Thread (nullptr), m_Service (nullptr), m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr), - m_NTCPServer (nullptr), m_SSUServer (nullptr), m_DHKeysPairSupplier (5), // 5 pre-generated keys + m_NTCPServer (nullptr), m_SSUServer (nullptr), m_NTCP2Server (nullptr), + m_DHKeysPairSupplier (5), // 5 pre-generated keys m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_TotalTransitTransmittedBytes (0), m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth(0), m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), @@ -191,6 +192,13 @@ namespace transport LogPrint(eLogError, "Transports: invalid NTCP proxy url ", ntcpproxy); return; } + // create NTCP2. TODO: move to acceptor + bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); + if (ntcp2) + { + m_NTCP2Server = new NTCP2Server (); + m_NTCP2Server->Start (); + } // create acceptors auto& addresses = context.GetRouterInfo ().GetAddresses (); @@ -262,6 +270,13 @@ namespace transport m_NTCPServer = nullptr; } + if (m_NTCP2Server) + { + m_NTCP2Server->Stop (); + delete m_NTCP2Server; + m_NTCP2Server = nullptr; + } + m_DHKeysPairSupplier.Stop (); m_IsRunning = false; if (m_Service) m_Service->stop (); @@ -386,9 +401,24 @@ namespace transport { if (peer.router) // we have RI already { - if (!peer.numAttempts) // NTCP + if (!peer.numAttempts) // NTCP2 { peer.numAttempts++; + if (m_NTCP2Server) // we support NTCP2 + { + // NTCP2 have priority over NTCP + auto address = peer.router->GetNTCP2Address (true, !context.SupportsV6 ()); // published only + if (address) + { + auto s = std::make_shared (*m_NTCP2Server, peer.router); + m_NTCP2Server->Connect (address->host, address->port, s); + return true; + } + } + } + if (peer.numAttempts == 1) // NTCP1 + { + peer.numAttempts++; auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ()); if (address && m_NTCPServer) { @@ -446,7 +476,7 @@ namespace transport else LogPrint (eLogDebug, "Transports: NTCP address is not present for ", i2p::data::GetIdentHashAbbreviation (ident), ", trying SSU"); } - if (peer.numAttempts == 1)// SSU + if (peer.numAttempts == 2)// SSU { peer.numAttempts++; if (m_SSUServer && peer.router->IsSSU (!context.SupportsV6 ())) @@ -709,7 +739,7 @@ namespace transport sendDatabaseStore = false; // we have it in the list already } if (sendDatabaseStore) - session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); + session->SendLocalRouterInfo (); else session->SetTerminationTimeout (10); // most likely it's publishing, no follow-up messages expected, set timeout to 10 seconds it->second.sessions.push_back (session); diff --git a/libi2pd/Transports.h b/libi2pd/Transports.h index d4410cb3..cb523545 100644 --- a/libi2pd/Transports.h +++ b/libi2pd/Transports.h @@ -15,6 +15,7 @@ #include "TransportSession.h" #include "NTCPSession.h" #include "SSU.h" +#include "NTCP2.h" #include "RouterInfo.h" #include "I2NPProtocol.h" #include "Identity.h" @@ -80,6 +81,7 @@ namespace transport bool IsBoundNTCP() const { return m_NTCPServer != nullptr; } bool IsBoundSSU() const { return m_SSUServer != nullptr; } + bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; } bool IsOnline() const { return m_IsOnline; }; void SetOnline (bool online) { m_IsOnline = online; }; @@ -154,6 +156,7 @@ namespace transport NTCPServer * m_NTCPServer; SSUServer * m_SSUServer; + NTCP2Server * m_NTCP2Server; mutable std::mutex m_PeersMutex; std::map m_Peers; @@ -179,6 +182,7 @@ namespace transport // for HTTP only const NTCPServer * GetNTCPServer () const { return m_NTCPServer; }; const SSUServer * GetSSUServer () const { return m_SSUServer; }; + const NTCP2Server * GetNTCP2Server () const { return m_NTCP2Server; }; const decltype(m_Peers)& GetPeers () const { return m_Peers; }; }; diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index c7e1b1b4..66620717 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -511,7 +511,7 @@ namespace tunnel HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); break; default: - LogPrint (eLogWarning, "Tunnel: unexpected messsage type ", (int) typeID); + LogPrint (eLogWarning, "Tunnel: unexpected message type ", (int) typeID); } msg = m_Queue.Get (); diff --git a/libi2pd_client/AddressBook.cpp b/libi2pd_client/AddressBook.cpp index f29ecee3..bca0e25b 100644 --- a/libi2pd_client/AddressBook.cpp +++ b/libi2pd_client/AddressBook.cpp @@ -377,7 +377,7 @@ namespace client } numAddresses++; auto it = m_Addresses.find (name); - if (it != m_Addresses.end ()) // aleady exists ? + if (it != m_Addresses.end ()) // already exists ? { if (it->second != ident->GetIdentHash ()) // address changed? { diff --git a/libi2pd_client/HTTPProxy.cpp b/libi2pd_client/HTTPProxy.cpp index ea95a6bd..f0530ac1 100644 --- a/libi2pd_client/HTTPProxy.cpp +++ b/libi2pd_client/HTTPProxy.cpp @@ -387,7 +387,7 @@ namespace proxy { LogPrint(eLogDebug, "HTTPProxy: ", m_ClientRequestURL.host); m_ClientRequestURL.schema = ""; m_ClientRequestURL.host = ""; - std::string origURI = m_ClientRequest.uri; // TODO: what do we need to chage uri for? + std::string origURI = m_ClientRequest.uri; // TODO: what do we need to change uri for? m_ClientRequest.uri = m_ClientRequestURL.to_string(); m_ClientRequest.write(m_ClientRequestBuffer); diff --git a/libi2pd_client/I2CP.cpp b/libi2pd_client/I2CP.cpp index b08fded1..7d7ce558 100644 --- a/libi2pd_client/I2CP.cpp +++ b/libi2pd_client/I2CP.cpp @@ -250,7 +250,7 @@ namespace client if (handler) (this->*handler)(m_Payload, m_PayloadLen); else - LogPrint (eLogError, "I2CP: Unknown I2CP messsage ", (int)m_Header[I2CP_HEADER_TYPE_OFFSET]); + LogPrint (eLogError, "I2CP: Unknown I2CP message ", (int)m_Header[I2CP_HEADER_TYPE_OFFSET]); } void I2CPSession::Terminate () @@ -398,7 +398,7 @@ namespace client } else { - LogPrint (eLogError, "I2CP: create session signature verification falied"); + LogPrint (eLogError, "I2CP: create session signature verification failed"); SendSessionStatusMessage (3); // invalid } } @@ -455,16 +455,16 @@ namespace client LogPrint(eLogError, "I2CP: invalid reconfigure message signature"); } else - LogPrint(eLogError, "I2CP: mapping size missmatch"); + LogPrint(eLogError, "I2CP: mapping size mismatch"); } else - LogPrint(eLogError, "I2CP: destination missmatch"); + LogPrint(eLogError, "I2CP: destination mismatch"); } else LogPrint(eLogError, "I2CP: malfromed destination"); } else - LogPrint(eLogError, "I2CP: session missmatch"); + LogPrint(eLogError, "I2CP: session mismatch"); } else LogPrint(eLogError, "I2CP: short message"); diff --git a/libi2pd_client/SOCKS.cpp b/libi2pd_client/SOCKS.cpp index ddf4bbe9..8a67901c 100644 --- a/libi2pd_client/SOCKS.cpp +++ b/libi2pd_client/SOCKS.cpp @@ -84,8 +84,8 @@ namespace proxy SOCKS5_HOST_UNREACH = 4, // Host unreachable SOCKS5_CONN_REFUSED = 5, // Connection refused by the peer SOCKS5_TTL_EXPIRED = 6, // TTL Expired - SOCKS5_CMD_UNSUP = 7, // Command unsuported - SOCKS5_ADDR_UNSUP = 8, // Address type unsuported + SOCKS5_CMD_UNSUP = 7, // Command unsupported + SOCKS5_ADDR_UNSUP = 8, // Address type unsupported SOCKS4_OK = 90, // No error for SOCKS4 SOCKS4_FAIL = 91, // Failed establishing connecting or not allowed SOCKS4_IDENTD_MISSING = 92, // Couldn't connect to the identd server diff --git a/qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistro.aidl b/qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistro.aidl index bbd8116d..e23cb699 100644 --- a/qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistro.aidl +++ b/qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistro.aidl @@ -51,7 +51,7 @@ interface IMinistro * "sources" StringArray Sources list from where Ministro will download the libs. Make sure you are using ONLY secure locations. * "repository" String Overwrites the default Ministro repository. Possible values: default, stable, testing and unstable * "required.modules" StringArray Required modules by your application -* "application.title" String Application name, used to show more informations to user +* "application.title" String Application name, used to show more information to user * "qt.provider" String Qt libs provider, currently only "necessitas" is supported. * "minimum.ministro.api" Integer Minimum Ministro API level, used to check if Ministro service compatible with your application. Current API Level is 3 ! * "minimum.qt.version" Integer Minimim Qt version (e.g. 0x040800, which means Qt 4.8.0, check http://qt-project.org/doc/qt-4.8/qtglobal.html#QT_VERSION)! diff --git a/qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtActivity.java b/qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtActivity.java index 677e8f46..9c607109 100644 --- a/qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtActivity.java +++ b/qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtActivity.java @@ -97,7 +97,7 @@ import android.view.ActionMode.Callback; public class QtActivity extends Activity { - private final static int MINISTRO_INSTALL_REQUEST_CODE = 0xf3ee; // request code used to know when Ministro instalation is finished + private final static int MINISTRO_INSTALL_REQUEST_CODE = 0xf3ee; // request code used to know when Ministro installation is finished private static final int MINISTRO_API_LEVEL = 5; // Ministro api level (check IMinistro.aidl file) private static final int NECESSITAS_API_LEVEL = 2; // Necessitas api level used by platform plugin private static final int QT_VERSION = 0x050100; // This app requires at least Qt version 5.1.0