diff --git a/ChangeLog b/ChangeLog index 293764a6..f080e1bc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,56 @@ # for this file format description, # see https://github.com/olivierlacan/keep-a-changelog +## [2.32.0] - 2020-05-25 +### Added +- Multiple encryption types for local destinations +- Next key and tagset for ECIES-X25519-AEAD-Ratchet +- NTCP2 through SOCKS proxy +- Throw error message if any port to bind is occupied +- gzip parameter for UDP tunnels +- Show ECIES-X25519-AEAD-Ratchet sessions and tags on the web console +- Simplified implementation of gzip for no compression mode +- Allow ECIES-X25519-AEAD-Ratchet session restart after 2 minutes +- Added logrotate config for rpm package +### Changed +- Select peers for client tunnels among routers >= 0.9.36 +- Check ECIES flag for encrypted lookup reply +- Streaming MTU size 1812 for ECIES-X25519-AEAD-Ratchet +- Don't calculate checksum for Data message send through ECIES-X25519-AEAD-Ratchet +- Catch network connectivity status for Windows +- Stop as soon as no more transit tunnels during graceful shutdown for Android +- RouterInfo gzip compression level depends on size +- Send response to received datagram from ECIES-X25519-AEAD-Ratchet session +- Update webconsole functional +- Increased max transit tunnels limit +- Reseeds list +- Dropped windows support in cmake +### Fixed +- Correct timestamp check for LeaseSet2 +- Encrypted leaseset without authentication +- Change SOCKS proxy connection response for clients without socks5h support (#1336) + +## [2.31.0] - 2020-04-10 +### Added +- NTCP2 through HTTP proxy +- Publish LeaseSet2 for I2CP destinations +- Show status page on main activity for android +- Handle ECIESFlag in DatabaseLookup at floodfill +- C++17 features for eligible compilers +### Changed +- Droped Websockets and Lua support +- Send DeliveryStatusMsg for LeaseSet for ECIES-X25519-AEAD-Ratchet +- Keep sending new session reply until established for ECIES-X25519-AEAD-Ratchet +- Updated SSU log messages +- Reopen SSU socket on exception +- Security hardening headers in web console +- Various web console changes +- Various QT changes +### Fixed +- NTCP2 socket descriptors leak +- Race condition with router's identity in transport sessions +- Not terminated streams remain forever + ## [2.30.0] - 2020-02-25 ### Added - Single threaded SAM @@ -21,7 +71,7 @@ ### Added - Client auth flag for b33 address ### Changed -- Remove incoming NTCP2 session from pending list when established +- Remove incoming NTCP2 session from pending list when established - Handle errors for NTCP2 SessionConfrimed send ### Fixed - Failure to start on Windows XP @@ -70,7 +120,7 @@ ## [2.25.0] - 2019-05-09 ### Added - Create, publish and handle encrypted LeaseSet2 -- Support of b33 addresses +- Support of b33 addresses - RedDSA key blinding - .b32.i2p addresses in jump links - ntcp2.addressv6 parameter @@ -102,7 +152,7 @@ - Correct SAM response for invalid key - SAM crash on termination for Windows - Race condition for publishing - + ## [2.23.0] - 2019-01-21 ### Added - Standard LeaseSet2 support @@ -161,7 +211,7 @@ - NTCP2 is enabled by default - Show lease's expiration time in readable format in the web console ### Fixed -- Correct names for transports in the web console +- Correct names for transports in the web console ## [2.19.0] - 2018-06-26 ### Added diff --git a/Makefile b/Makefile index a814aecf..35d76b82 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS))) DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp include Makefile.bsd else ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS))) - DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32Service.cpp Win32/Win32App.cpp + DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32Service.cpp Win32/Win32App.cpp Win32/Win32NetState.cpp include Makefile.mingw else # not supported $(error Not supported platform) diff --git a/Makefile.linux b/Makefile.linux index fa2f2fdf..1cdf0a48 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -20,7 +20,7 @@ else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # gcc 4.7 - 4.9 else ifeq ($(shell expr match ${CXXVER} "[5-6]"),1) # gcc 5 - 6 NEEDED_CXXFLAGS += -std=c++11 LDLIBS = -latomic -else ifeq ($(shell expr match ${CXXVER} "[7-9]"),1) # gcc >= 7 +else ifeq ($(shell expr match ${CXXVER} "[1,7-9]"),1) # gcc >= 7 NEEDED_CXXFLAGS += -std=c++17 LDLIBS = -latomic else # not supported diff --git a/Makefile.mingw b/Makefile.mingw index 17be71eb..2782b715 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -2,10 +2,19 @@ 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 INCFLAGS = -Idaemon -I. LDFLAGS := ${LD_DEBUG} -Wl,-Bstatic -static-libgcc -static-libstdc++ +# detect proper flag for c++11 support by compilers +CXXVER := $(shell $(CXX) -dumpversion) +ifeq ($(shell expr match ${CXXVER} "[4]\.[7-9]\|4\.1[0-9]\|[5-6]"),4) # gcc 4.7 - 6 + NEEDED_CXXFLAGS += -std=c++11 +else ifeq ($(shell expr match ${CXXVER} "[1,7-9]"),1) # gcc >= 7 + NEEDED_CXXFLAGS += -std=c++17 +else # not supported +$(error Compiler too old) +endif + # Boost libraries suffix BOOST_SUFFIX = -mt @@ -27,6 +36,8 @@ LDLIBS += \ -lws2_32 \ -lgdi32 \ -liphlpapi \ + -lole32 \ + -luuid \ -lstdc++ \ -lpthread diff --git a/README.md b/README.md index 25e6c3e5..ec7548f0 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Snapcraft release](https://snapcraft.io/i2pd/badge.svg)](https://snapcraft.io/i2pd) [![License](https://img.shields.io/github/license/PurpleI2P/i2pd.svg)](https://github.com/PurpleI2P/i2pd/blob/openssl/LICENSE) [![Packaging status](https://repology.org/badge/tiny-repos/i2pd.svg)](https://repology.org/project/i2pd/versions) +[![Docker Pulls](https://img.shields.io/docker/pulls/purplei2p/i2pd)](https://hub.docker.com/r/purplei2p/i2pd) i2pd ==== @@ -68,7 +69,7 @@ Build instructions: * Alpine, ArchLinux, openSUSE, Gentoo, Debian, Ubuntu, etc. * Windows - [![Build status](https://ci.appveyor.com/api/projects/status/1908qe4p48ff1x23?svg=true)](https://ci.appveyor.com/project/PurpleI2P/i2pd) * Mac OS X - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd) -* Docker image - [![Build Status](https://dockerbuildbadges.quelltext.eu/status.svg?organization=meeh&repository=i2pd)](https://hub.docker.com/r/meeh/i2pd/builds/) +* Docker image - [![Build Status](https://img.shields.io/docker/cloud/build/purplei2p/i2pd)](https://hub.docker.com/r/purplei2p/i2pd/builds/) * Snap - [![Snap Status](https://build.snapcraft.io/badge/PurpleI2P/i2pd-snap.svg)](https://build.snapcraft.io/user/PurpleI2P/i2pd-snap) * FreeBSD * Android diff --git a/Win32/DaemonWin32.cpp b/Win32/DaemonWin32.cpp index 9fffe7fb..1214ff68 100644 --- a/Win32/DaemonWin32.cpp +++ b/Win32/DaemonWin32.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include "Config.h" @@ -8,6 +16,7 @@ #ifdef _WIN32 #include "Win32/Win32Service.h" #ifdef WIN32_APP +#include #include "Win32/Win32App.h" #endif @@ -23,6 +32,11 @@ namespace util setlocale(LC_ALL, "Russian"); setlocale(LC_TIME, "C"); + i2p::log::SetThrowFunction ([](const std::string& s) + { + MessageBox(0, TEXT(s.c_str ()), TEXT("i2pd"), MB_ICONERROR | MB_TASKMODAL | MB_OK ); + }); + if (!Daemon_Singleton::init(argc, argv)) return false; diff --git a/Win32/Win32App.cpp b/Win32/Win32App.cpp index df87da3a..df345b11 100644 --- a/Win32/Win32App.cpp +++ b/Win32/Win32App.cpp @@ -1,453 +1,463 @@ -#include -#include -#include -#include "ClientContext.h" -#include "Config.h" -#include "NetDb.hpp" -#include "RouterContext.h" -#include "Transports.h" -#include "Tunnel.h" -#include "version.h" -#include "resource.h" -#include "Daemon.h" -#include "Win32App.h" -#include - -#define ID_ABOUT 2000 -#define ID_EXIT 2001 -#define ID_CONSOLE 2002 -#define ID_APP 2003 -#define ID_GRACEFUL_SHUTDOWN 2004 -#define ID_STOP_GRACEFUL_SHUTDOWN 2005 -#define ID_RELOAD 2006 -#define ID_ACCEPT_TRANSIT 2007 -#define ID_DECLINE_TRANSIT 2008 - -#define ID_TRAY_ICON 2050 -#define WM_TRAYICON (WM_USER + 1) - -#define IDT_GRACEFUL_SHUTDOWN_TIMER 2100 -#define FRAME_UPDATE_TIMER 2101 -#define IDT_GRACEFUL_TUNNELCHECK_TIMER 2102 - -namespace i2p -{ -namespace win32 -{ - static DWORD GracefulShutdownEndtime = 0; - - typedef DWORD (* IPN)(); - IPN GetTickCountLocal = (IPN)GetProcAddress (GetModuleHandle ("KERNEL32.dll"), "GetTickCount"); - - static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem) - { - HMENU hPopup = CreatePopupMenu(); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_CONSOLE, "Open &console"); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "Show app"); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About..."); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); - if(!i2p::context.AcceptsTunnels()) - InsertMenu (hPopup, -1, - i2p::util::DaemonWin32::Instance ().isGraceful ? MF_BYPOSITION | MF_STRING | MF_GRAYED : MF_BYPOSITION | MF_STRING, - ID_ACCEPT_TRANSIT, "Accept &transit"); - else - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit"); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload tunnels config"); - if (!i2p::util::DaemonWin32::Instance ().isGraceful) - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown"); - else - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_STOP_GRACEFUL_SHUTDOWN, "Stop &graceful shutdown"); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_EXIT, "E&xit"); - SetMenuDefaultItem (hPopup, ID_CONSOLE, FALSE); - SendMessage (hWnd, WM_INITMENUPOPUP, (WPARAM)hPopup, 0); - - POINT p; - if (!curpos) - { - GetCursorPos (&p); - curpos = &p; - } - - WORD cmd = TrackPopupMenu (hPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, curpos->x, curpos->y, 0, hWnd, NULL); - SendMessage (hWnd, WM_COMMAND, cmd, 0); - - DestroyMenu(hPopup); - } - - static void AddTrayIcon (HWND hWnd) - { - NOTIFYICONDATA nid; - memset(&nid, 0, sizeof(nid)); - nid.cbSize = sizeof(nid); - nid.hWnd = hWnd; - nid.uID = ID_TRAY_ICON; - nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO; - nid.uCallbackMessage = WM_TRAYICON; - nid.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE (MAINICON)); - strcpy (nid.szTip, "i2pd"); - strcpy (nid.szInfo, "i2pd is starting"); - Shell_NotifyIcon(NIM_ADD, &nid ); - } - - static void RemoveTrayIcon (HWND hWnd) - { - NOTIFYICONDATA nid; - nid.hWnd = hWnd; - nid.uID = ID_TRAY_ICON; - Shell_NotifyIcon (NIM_DELETE, &nid); - } - - static void ShowUptime (std::stringstream& s, int seconds) - { - int num; - - if ((num = seconds / 86400) > 0) { - s << num << " days, "; - seconds -= num * 86400; - } - if ((num = seconds / 3600) > 0) { - s << num << " hours, "; - seconds -= num * 3600; - } - if ((num = seconds / 60) > 0) { - s << num << " min, "; - seconds -= num * 60; - } - s << seconds << " seconds\n"; - } - - template static void ShowTransfered (std::stringstream& s, size transfer) - { - auto bytes = transfer & 0x03ff; - transfer >>= 10; - auto kbytes = transfer & 0x03ff; - transfer >>= 10; - auto mbytes = transfer & 0x03ff; - transfer >>= 10; - auto gbytes = transfer & 0x03ff; - - if (gbytes) - s << gbytes << " GB, "; - if (mbytes) - s << mbytes << " MB, "; - if (kbytes) - s << kbytes << " KB, "; - s << bytes << " Bytes\n"; - } - - static void PrintMainWindowText (std::stringstream& s) - { - s << "\n"; - s << "Status: "; - switch (i2p::context.GetStatus()) - { - case eRouterStatusOK: s << "OK"; break; - case eRouterStatusTesting: s << "Testing"; break; - case eRouterStatusFirewalled: s << "Firewalled"; break; - case eRouterStatusError: - { - switch (i2p::context.GetError()) - { - case eRouterErrorClockSkew: s << "Clock skew"; break; - default: s << "Error"; - } - break; - } - default: s << "Unknown"; - } - s << "; "; - s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n"; - s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ()); - if (GracefulShutdownEndtime != 0) - { - DWORD GracefulTimeLeft = (GracefulShutdownEndtime - GetTickCountLocal()) / 1000; - s << "Graceful shutdown, time left: "; ShowUptime(s, GracefulTimeLeft); - } - else - s << "\n"; - s << "Inbound: " << i2p::transport::transports.GetInBandwidth() / 1024 << " KiB/s; "; - s << "Outbound: " << i2p::transport::transports.GetOutBandwidth() / 1024 << " KiB/s\n"; - s << "Received: "; ShowTransfered (s, i2p::transport::transports.GetTotalReceivedBytes()); - s << "Sent: "; ShowTransfered (s, i2p::transport::transports.GetTotalSentBytes()); - s << "\n"; - s << "Routers: " << i2p::data::netdb.GetNumRouters () << "; "; - s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << "; "; - s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "\n"; - s << "Tunnels: "; - s << "In: " << i2p::tunnel::tunnels.CountInboundTunnels() << "; "; - s << "Out: " << i2p::tunnel::tunnels.CountOutboundTunnels() << "; "; - s << "Transit: " << i2p::tunnel::tunnels.CountTransitTunnels() << "\n"; - s << "\n"; - } - - static LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) - { - static UINT s_uTaskbarRestart; - - switch (uMsg) - { - case WM_CREATE: - { - s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); - AddTrayIcon (hWnd); - break; - } - case WM_CLOSE: - { - RemoveTrayIcon (hWnd); - KillTimer (hWnd, FRAME_UPDATE_TIMER); - KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER); - KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER); - PostQuitMessage (0); - break; - } - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case ID_ABOUT: - { - std::stringstream text; - text << "Version: " << I2PD_VERSION << " " << CODENAME; - MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK ); - return 0; - } - case ID_EXIT: - { - PostMessage (hWnd, WM_CLOSE, 0, 0); - return 0; - } - case ID_ACCEPT_TRANSIT: - { - i2p::context.SetAcceptsTunnels (true); - std::stringstream text; - text << "I2Pd now accept transit tunnels"; - MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK ); - return 0; - } - case ID_DECLINE_TRANSIT: - { - i2p::context.SetAcceptsTunnels (false); - std::stringstream text; - text << "I2Pd now decline new transit tunnels"; - MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK ); - return 0; - } - case ID_GRACEFUL_SHUTDOWN: - { - i2p::context.SetAcceptsTunnels (false); - SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes - SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second - GracefulShutdownEndtime = GetTickCountLocal() + 10*60*1000; - i2p::util::DaemonWin32::Instance ().isGraceful = true; - return 0; - } - case ID_STOP_GRACEFUL_SHUTDOWN: - { - i2p::context.SetAcceptsTunnels (true); - KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER); - KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER); - GracefulShutdownEndtime = 0; - i2p::util::DaemonWin32::Instance ().isGraceful = false; - return 0; - } - case ID_RELOAD: - { - i2p::client::context.ReloadConfig(); - std::stringstream text; - text << "I2Pd reloading configs..."; - MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK ); - return 0; - } - case ID_CONSOLE: - { - char buf[30]; - std::string httpAddr; i2p::config::GetOption("http.address", httpAddr); - uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); - snprintf(buf, 30, "http://%s:%d", httpAddr.c_str(), httpPort); - ShellExecute(NULL, "open", buf, NULL, NULL, SW_SHOWNORMAL); - return 0; - } - case ID_APP: - { - ShowWindow(hWnd, SW_SHOW); - SetTimer(hWnd, FRAME_UPDATE_TIMER, 3000, NULL); - return 0; - } - } - break; - } - case WM_SYSCOMMAND: - { - switch (wParam) - { - case SC_MINIMIZE: - { - ShowWindow(hWnd, SW_HIDE); - KillTimer (hWnd, FRAME_UPDATE_TIMER); - return 0; - } - case SC_CLOSE: - { - std::string close; i2p::config::GetOption("close", close); - if (0 == close.compare("ask")) - switch(::MessageBox(hWnd, "Would you like to minimize instead of exiting?" - " You can add 'close' configuration option. Valid values are: ask, minimize, exit.", - "Minimize instead of exiting?", MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON1)) - { - case IDYES: close = "minimize"; break; - case IDNO: close = "exit"; break; - default: return 0; - } - if (0 == close.compare("minimize")) - { - ShowWindow(hWnd, SW_HIDE); - KillTimer (hWnd, FRAME_UPDATE_TIMER); - return 0; - } - if (0 != close.compare("exit")) - { - ::MessageBox(hWnd, close.c_str(), "Unknown close action in config", MB_OK | MB_ICONWARNING); - return 0; - } - } - } - } - case WM_TRAYICON: - { - switch (lParam) - { - case WM_LBUTTONUP: - case WM_RBUTTONUP: - { - SetForegroundWindow (hWnd); - ShowPopupMenu(hWnd, NULL, -1); - PostMessage (hWnd, WM_APP + 1, 0, 0); - break; - } - } - break; - } - case WM_TIMER: - { - switch(wParam) - { - case IDT_GRACEFUL_SHUTDOWN_TIMER: - { - GracefulShutdownEndtime = 0; - PostMessage (hWnd, WM_CLOSE, 0, 0); // exit - return 0; - } - case FRAME_UPDATE_TIMER: - { - InvalidateRect(hWnd, NULL, TRUE); - return 0; - } - case IDT_GRACEFUL_TUNNELCHECK_TIMER: - { - if (i2p::tunnel::tunnels.CountTransitTunnels() == 0) - PostMessage (hWnd, WM_CLOSE, 0, 0); - else - SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); - return 0; - } - } - break; - } - case WM_PAINT: - { - HDC hDC; - PAINTSTRUCT ps; - RECT rp; - HFONT hFont; - std::stringstream s; PrintMainWindowText (s); - hDC = BeginPaint (hWnd, &ps); - GetClientRect(hWnd, &rp); - SetTextColor(hDC, 0x00D43B69); - hFont = CreateFont(18,0,0,0,0,0,0,0,DEFAULT_CHARSET,0,0,0,0,TEXT("Times New Roman")); - SelectObject(hDC,hFont); - DrawText(hDC, TEXT(s.str().c_str()), s.str().length(), &rp, DT_CENTER|DT_VCENTER); - DeleteObject(hFont); - EndPaint(hWnd, &ps); - break; - } - default: - { - if (uMsg == s_uTaskbarRestart) - AddTrayIcon (hWnd); - break; - } - } - return DefWindowProc( hWnd, uMsg, wParam, lParam); - } - - bool StartWin32App () - { - if (FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd"))) - { - MessageBox(NULL, TEXT("I2Pd is running already"), TEXT("Warning"), MB_OK); - return false; - } - // register main window - auto hInst = GetModuleHandle(NULL); - WNDCLASSEX wclx; - memset (&wclx, 0, sizeof(wclx)); - wclx.cbSize = sizeof(wclx); - wclx.style = 0; - wclx.lpfnWndProc = WndProc; - //wclx.cbClsExtra = 0; - //wclx.cbWndExtra = 0; - wclx.hInstance = hInst; - wclx.hIcon = LoadIcon (hInst, MAKEINTRESOURCE(MAINICON)); - wclx.hCursor = LoadCursor (NULL, IDC_ARROW); - //wclx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); - wclx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); - wclx.lpszMenuName = NULL; - wclx.lpszClassName = I2PD_WIN32_CLASSNAME; - RegisterClassEx (&wclx); - // create new window - if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 100, 100, 350, 210, NULL, NULL, hInst, NULL)) - { - MessageBox(NULL, "Failed to create main window", TEXT("Warning!"), MB_ICONERROR | MB_OK | MB_TOPMOST); - return false; - } - return true; - } - - int RunWin32App () - { - MSG msg; - while (GetMessage (&msg, NULL, 0, 0 )) - { - TranslateMessage (&msg); - DispatchMessage (&msg); - } - return msg.wParam; - } - - void StopWin32App () - { - HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd")); - if (hWnd) - PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_EXIT, 0), 0); - UnregisterClass (I2PD_WIN32_CLASSNAME, GetModuleHandle(NULL)); - } - - bool GracefulShutdown () - { - HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd")); - if (hWnd) - PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_GRACEFUL_SHUTDOWN, 0), 0); - return hWnd; - } - - bool StopGracefulShutdown () - { - HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd")); - if (hWnd) - PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_STOP_GRACEFUL_SHUTDOWN, 0), 0); - return hWnd; - } - -} -} +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include +#include +#include +#include +#include "ClientContext.h" +#include "Config.h" +#include "NetDb.hpp" +#include "RouterContext.h" +#include "Transports.h" +#include "Tunnel.h" +#include "version.h" +#include "resource.h" +#include "Daemon.h" +#include "Win32App.h" +#include "Win32NetState.h" + +#define ID_ABOUT 2000 +#define ID_EXIT 2001 +#define ID_CONSOLE 2002 +#define ID_APP 2003 +#define ID_GRACEFUL_SHUTDOWN 2004 +#define ID_STOP_GRACEFUL_SHUTDOWN 2005 +#define ID_RELOAD 2006 +#define ID_ACCEPT_TRANSIT 2007 +#define ID_DECLINE_TRANSIT 2008 + +#define ID_TRAY_ICON 2050 +#define WM_TRAYICON (WM_USER + 1) + +#define IDT_GRACEFUL_SHUTDOWN_TIMER 2100 +#define FRAME_UPDATE_TIMER 2101 +#define IDT_GRACEFUL_TUNNELCHECK_TIMER 2102 + +namespace i2p +{ +namespace win32 +{ + static DWORD GracefulShutdownEndtime = 0; + + typedef DWORD (* IPN)(); + IPN GetTickCountLocal = (IPN)GetProcAddress (GetModuleHandle ("KERNEL32.dll"), "GetTickCount"); + + static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem) + { + HMENU hPopup = CreatePopupMenu(); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_CONSOLE, "Open &console"); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "Show app"); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About..."); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); + if(!i2p::context.AcceptsTunnels()) + InsertMenu (hPopup, -1, + i2p::util::DaemonWin32::Instance ().isGraceful ? MF_BYPOSITION | MF_STRING | MF_GRAYED : MF_BYPOSITION | MF_STRING, + ID_ACCEPT_TRANSIT, "Accept &transit"); + else + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit"); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload tunnels config"); + if (!i2p::util::DaemonWin32::Instance ().isGraceful) + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown"); + else + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_STOP_GRACEFUL_SHUTDOWN, "Stop &graceful shutdown"); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_EXIT, "E&xit"); + SetMenuDefaultItem (hPopup, ID_CONSOLE, FALSE); + SendMessage (hWnd, WM_INITMENUPOPUP, (WPARAM)hPopup, 0); + + POINT p; + if (!curpos) + { + GetCursorPos (&p); + curpos = &p; + } + + WORD cmd = TrackPopupMenu (hPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, curpos->x, curpos->y, 0, hWnd, NULL); + SendMessage (hWnd, WM_COMMAND, cmd, 0); + + DestroyMenu(hPopup); + } + + static void AddTrayIcon (HWND hWnd) + { + NOTIFYICONDATA nid; + memset(&nid, 0, sizeof(nid)); + nid.cbSize = sizeof(nid); + nid.hWnd = hWnd; + nid.uID = ID_TRAY_ICON; + nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO; + nid.uCallbackMessage = WM_TRAYICON; + nid.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE (MAINICON)); + strcpy (nid.szTip, "i2pd"); + strcpy (nid.szInfo, "i2pd is starting"); + Shell_NotifyIcon(NIM_ADD, &nid ); + } + + static void RemoveTrayIcon (HWND hWnd) + { + NOTIFYICONDATA nid; + nid.hWnd = hWnd; + nid.uID = ID_TRAY_ICON; + Shell_NotifyIcon (NIM_DELETE, &nid); + } + + static void ShowUptime (std::stringstream& s, int seconds) + { + int num; + + if ((num = seconds / 86400) > 0) { + s << num << " days, "; + seconds -= num * 86400; + } + if ((num = seconds / 3600) > 0) { + s << num << " hours, "; + seconds -= num * 3600; + } + if ((num = seconds / 60) > 0) { + s << num << " min, "; + seconds -= num * 60; + } + s << seconds << " seconds\n"; + } + + template static void ShowTransfered (std::stringstream& s, size transfer) + { + auto bytes = transfer & 0x03ff; + transfer >>= 10; + auto kbytes = transfer & 0x03ff; + transfer >>= 10; + auto mbytes = transfer & 0x03ff; + transfer >>= 10; + auto gbytes = transfer & 0x03ff; + + if (gbytes) + s << gbytes << " GB, "; + if (mbytes) + s << mbytes << " MB, "; + if (kbytes) + s << kbytes << " KB, "; + s << bytes << " Bytes\n"; + } + + static void PrintMainWindowText (std::stringstream& s) + { + s << "\n"; + s << "Status: "; + switch (i2p::context.GetStatus()) + { + case eRouterStatusOK: s << "OK"; break; + case eRouterStatusTesting: s << "Testing"; break; + case eRouterStatusFirewalled: s << "Firewalled"; break; + case eRouterStatusError: + { + switch (i2p::context.GetError()) + { + case eRouterErrorClockSkew: s << "Clock skew"; break; + default: s << "Error"; + } + break; + } + default: s << "Unknown"; + } + s << "; "; + s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n"; + s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ()); + if (GracefulShutdownEndtime != 0) + { + DWORD GracefulTimeLeft = (GracefulShutdownEndtime - GetTickCountLocal()) / 1000; + s << "Graceful shutdown, time left: "; ShowUptime(s, GracefulTimeLeft); + } + else + s << "\n"; + s << "Inbound: " << i2p::transport::transports.GetInBandwidth() / 1024 << " KiB/s; "; + s << "Outbound: " << i2p::transport::transports.GetOutBandwidth() / 1024 << " KiB/s\n"; + s << "Received: "; ShowTransfered (s, i2p::transport::transports.GetTotalReceivedBytes()); + s << "Sent: "; ShowTransfered (s, i2p::transport::transports.GetTotalSentBytes()); + s << "\n"; + s << "Routers: " << i2p::data::netdb.GetNumRouters () << "; "; + s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << "; "; + s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "\n"; + s << "Tunnels: "; + s << "In: " << i2p::tunnel::tunnels.CountInboundTunnels() << "; "; + s << "Out: " << i2p::tunnel::tunnels.CountOutboundTunnels() << "; "; + s << "Transit: " << i2p::tunnel::tunnels.CountTransitTunnels() << "\n"; + s << "\n"; + } + + static LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + static UINT s_uTaskbarRestart; + + switch (uMsg) + { + case WM_CREATE: + { + s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); + AddTrayIcon (hWnd); + break; + } + case WM_CLOSE: + { + RemoveTrayIcon (hWnd); + KillTimer (hWnd, FRAME_UPDATE_TIMER); + KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER); + KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER); + PostQuitMessage (0); + break; + } + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case ID_ABOUT: + { + std::stringstream text; + text << "Version: " << I2PD_VERSION << " " << CODENAME; + MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK ); + return 0; + } + case ID_EXIT: + { + PostMessage (hWnd, WM_CLOSE, 0, 0); + return 0; + } + case ID_ACCEPT_TRANSIT: + { + i2p::context.SetAcceptsTunnels (true); + std::stringstream text; + text << "I2Pd now accept transit tunnels"; + MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK ); + return 0; + } + case ID_DECLINE_TRANSIT: + { + i2p::context.SetAcceptsTunnels (false); + std::stringstream text; + text << "I2Pd now decline new transit tunnels"; + MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK ); + return 0; + } + case ID_GRACEFUL_SHUTDOWN: + { + i2p::context.SetAcceptsTunnels (false); + SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes + SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second + GracefulShutdownEndtime = GetTickCountLocal() + 10*60*1000; + i2p::util::DaemonWin32::Instance ().isGraceful = true; + return 0; + } + case ID_STOP_GRACEFUL_SHUTDOWN: + { + i2p::context.SetAcceptsTunnels (true); + KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER); + KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER); + GracefulShutdownEndtime = 0; + i2p::util::DaemonWin32::Instance ().isGraceful = false; + return 0; + } + case ID_RELOAD: + { + i2p::client::context.ReloadConfig(); + std::stringstream text; + text << "I2Pd reloading configs..."; + MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK ); + return 0; + } + case ID_CONSOLE: + { + char buf[30]; + std::string httpAddr; i2p::config::GetOption("http.address", httpAddr); + uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); + snprintf(buf, 30, "http://%s:%d", httpAddr.c_str(), httpPort); + ShellExecute(NULL, "open", buf, NULL, NULL, SW_SHOWNORMAL); + return 0; + } + case ID_APP: + { + ShowWindow(hWnd, SW_SHOW); + SetTimer(hWnd, FRAME_UPDATE_TIMER, 3000, NULL); + return 0; + } + } + break; + } + case WM_SYSCOMMAND: + { + switch (wParam) + { + case SC_MINIMIZE: + { + ShowWindow(hWnd, SW_HIDE); + KillTimer (hWnd, FRAME_UPDATE_TIMER); + return 0; + } + case SC_CLOSE: + { + std::string close; i2p::config::GetOption("close", close); + if (0 == close.compare("ask")) + switch(::MessageBox(hWnd, "Would you like to minimize instead of exiting?" + " You can add 'close' configuration option. Valid values are: ask, minimize, exit.", + "Minimize instead of exiting?", MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON1)) + { + case IDYES: close = "minimize"; break; + case IDNO: close = "exit"; break; + default: return 0; + } + if (0 == close.compare("minimize")) + { + ShowWindow(hWnd, SW_HIDE); + KillTimer (hWnd, FRAME_UPDATE_TIMER); + return 0; + } + if (0 != close.compare("exit")) + { + ::MessageBox(hWnd, close.c_str(), "Unknown close action in config", MB_OK | MB_ICONWARNING); + return 0; + } + } + } + } + case WM_TRAYICON: + { + switch (lParam) + { + case WM_LBUTTONUP: + case WM_RBUTTONUP: + { + SetForegroundWindow (hWnd); + ShowPopupMenu(hWnd, NULL, -1); + PostMessage (hWnd, WM_APP + 1, 0, 0); + break; + } + } + break; + } + case WM_TIMER: + { + switch(wParam) + { + case IDT_GRACEFUL_SHUTDOWN_TIMER: + { + GracefulShutdownEndtime = 0; + PostMessage (hWnd, WM_CLOSE, 0, 0); // exit + return 0; + } + case IDT_GRACEFUL_TUNNELCHECK_TIMER: + { + if (i2p::tunnel::tunnels.CountTransitTunnels() == 0) + PostMessage (hWnd, WM_CLOSE, 0, 0); + else + SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); + return 0; + } + case FRAME_UPDATE_TIMER: + { + InvalidateRect(hWnd, NULL, TRUE); + return 0; + } + } + break; + } + case WM_PAINT: + { + HDC hDC; + PAINTSTRUCT ps; + RECT rp; + HFONT hFont; + std::stringstream s; PrintMainWindowText (s); + hDC = BeginPaint (hWnd, &ps); + GetClientRect(hWnd, &rp); + SetTextColor(hDC, 0x00D43B69); + hFont = CreateFont(18,0,0,0,0,0,0,0,DEFAULT_CHARSET,0,0,0,0,TEXT("Times New Roman")); + SelectObject(hDC,hFont); + DrawText(hDC, TEXT(s.str().c_str()), s.str().length(), &rp, DT_CENTER|DT_VCENTER); + DeleteObject(hFont); + EndPaint(hWnd, &ps); + break; + } + default: + { + if (uMsg == s_uTaskbarRestart) + AddTrayIcon (hWnd); + break; + } + } + return DefWindowProc( hWnd, uMsg, wParam, lParam); + } + + bool StartWin32App () + { + if (FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd"))) + { + MessageBox(NULL, TEXT("I2Pd is running already"), TEXT("Warning"), MB_OK); + return false; + } + // register main window + auto hInst = GetModuleHandle(NULL); + WNDCLASSEX wclx; + memset (&wclx, 0, sizeof(wclx)); + wclx.cbSize = sizeof(wclx); + wclx.style = 0; + wclx.lpfnWndProc = WndProc; + //wclx.cbClsExtra = 0; + //wclx.cbWndExtra = 0; + wclx.hInstance = hInst; + wclx.hIcon = LoadIcon (hInst, MAKEINTRESOURCE(MAINICON)); + wclx.hCursor = LoadCursor (NULL, IDC_ARROW); + //wclx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); + wclx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wclx.lpszMenuName = NULL; + wclx.lpszClassName = I2PD_WIN32_CLASSNAME; + RegisterClassEx (&wclx); + // create new window + if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 100, 100, 350, 210, NULL, NULL, hInst, NULL)) + { + MessageBox(NULL, "Failed to create main window", TEXT("Warning!"), MB_ICONERROR | MB_OK | MB_TOPMOST); + return false; + } + SubscribeToEvents(); + return true; + } + + int RunWin32App () + { + MSG msg; + while (GetMessage (&msg, NULL, 0, 0 )) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + return msg.wParam; + } + + void StopWin32App () + { + HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd")); + if (hWnd) + PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_EXIT, 0), 0); + // UnSubscribeFromEvents(); // TODO: understand why unsubscribing crashes app + UnregisterClass (I2PD_WIN32_CLASSNAME, GetModuleHandle(NULL)); + } + + bool GracefulShutdown () + { + HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd")); + if (hWnd) + PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_GRACEFUL_SHUTDOWN, 0), 0); + return hWnd; + } + + bool StopGracefulShutdown () + { + HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd")); + if (hWnd) + PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_STOP_GRACEFUL_SHUTDOWN, 0), 0); + return hWnd; + } +} +} diff --git a/Win32/Win32App.h b/Win32/Win32App.h index b230eb06..d242f7d3 100644 --- a/Win32/Win32App.h +++ b/Win32/Win32App.h @@ -1,17 +1,25 @@ -#ifndef WIN32APP_H__ -#define WIN32APP_H__ - -#define I2PD_WIN32_CLASSNAME "i2pd main window" - -namespace i2p -{ -namespace win32 -{ - bool StartWin32App (); - void StopWin32App (); - int RunWin32App (); - bool GracefulShutdown (); - bool StopGracefulShutdown (); -} -} -#endif // WIN32APP_H__ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#ifndef WIN32APP_H__ +#define WIN32APP_H__ + +#define I2PD_WIN32_CLASSNAME "i2pd main window" + +namespace i2p +{ +namespace win32 +{ + bool StartWin32App (); + void StopWin32App (); + int RunWin32App (); + bool GracefulShutdown (); + bool StopGracefulShutdown (); +} +} +#endif // WIN32APP_H__ diff --git a/Win32/Win32NetState.cpp b/Win32/Win32NetState.cpp new file mode 100644 index 00000000..dd4dd08c --- /dev/null +++ b/Win32/Win32NetState.cpp @@ -0,0 +1,86 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#if WINVER != 0x0501 // supported since Vista +#include "Win32NetState.h" +#include +#include "Log.h" + +IUnknown *pUnknown = nullptr; +INetworkListManager *pNetworkListManager = nullptr; +IConnectionPointContainer *pCPContainer = nullptr; +IConnectionPoint *pConnectPoint = nullptr; +DWORD Cookie = 0; + +void SubscribeToEvents() +{ + LogPrint(eLogInfo, "NetState: Trying to subscribe to NetworkListManagerEvents"); + CoInitialize(NULL); + + HRESULT Result = CoCreateInstance(CLSID_NetworkListManager, NULL, CLSCTX_ALL, IID_IUnknown, (void **)&pUnknown); + if (SUCCEEDED(Result)) + { + Result = pUnknown->QueryInterface(IID_INetworkListManager, (void **)&pNetworkListManager); + if (SUCCEEDED(Result)) + { + VARIANT_BOOL IsConnect = VARIANT_FALSE; + Result = pNetworkListManager->IsConnectedToInternet(&IsConnect); + if (SUCCEEDED(Result)) { + i2p::transport::transports.SetOnline (true); + LogPrint(eLogInfo, "NetState: current state: ", IsConnect == VARIANT_TRUE ? "connected" : "disconnected"); + } + + Result = pNetworkListManager->QueryInterface(IID_IConnectionPointContainer, (void **)&pCPContainer); + if (SUCCEEDED(Result)) + { + Result = pCPContainer->FindConnectionPoint(IID_INetworkListManagerEvents, &pConnectPoint); + if(SUCCEEDED(Result)) + { + CNetworkListManagerEvent *NetEvent = new CNetworkListManagerEvent; + Result = pConnectPoint->Advise((IUnknown *)NetEvent, &Cookie); + if (SUCCEEDED(Result)) + LogPrint(eLogInfo, "NetState: Successfully subscribed to NetworkListManagerEvent messages"); + else + LogPrint(eLogError, "NetState: Unable to subscribe to NetworkListManagerEvent messages"); + } else + LogPrint(eLogError, "NetState: Unable to find interface connection point"); + } else + LogPrint(eLogError, "NetState: Unable to query NetworkListManager interface"); + } else + LogPrint(eLogError, "NetState: Unable to query global interface"); + } else + LogPrint(eLogError, "NetState: Unable to create INetworkListManager interface"); +} + +void UnSubscribeFromEvents() +{ + try + { + if (pConnectPoint) { + pConnectPoint->Unadvise(Cookie); + pConnectPoint->Release(); + } + + if (pCPContainer) + pCPContainer->Release(); + + if (pNetworkListManager) + pNetworkListManager->Release(); + + if (pUnknown) + pUnknown->Release(); + + CoUninitialize(); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "NetState: received exception: ", ex.what ()); + } +} + +#endif // WINVER diff --git a/Win32/Win32NetState.h b/Win32/Win32NetState.h new file mode 100644 index 00000000..bcb6c8d7 --- /dev/null +++ b/Win32/Win32NetState.h @@ -0,0 +1,93 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#ifndef WIN_32_NETSTATE_H__ +#define WIN_32_NETSTATE_H__ + +#if WINVER != 0x0501 // supported since Vista +#include +#include +#include "Log.h" +#include "Transports.h" + +class CNetworkListManagerEvent : public INetworkListManagerEvents +{ +public: + CNetworkListManagerEvent() : m_ref(1) { } + ~CNetworkListManagerEvent() { } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) + { + HRESULT Result = S_OK; + if (IsEqualIID(riid, IID_IUnknown)) { + *ppvObject = (IUnknown *)this; + } else if (IsEqualIID(riid ,IID_INetworkListManagerEvents)) { + *ppvObject = (INetworkListManagerEvents *)this; + } else { + Result = E_NOINTERFACE; + } + + return Result; + } + + ULONG STDMETHODCALLTYPE AddRef() + { + return (ULONG)InterlockedIncrement(&m_ref); + } + + ULONG STDMETHODCALLTYPE Release() + { + LONG Result = InterlockedDecrement(&m_ref); + if (Result == 0) + delete this; + return (ULONG)Result; + } + + virtual HRESULT STDMETHODCALLTYPE ConnectivityChanged(NLM_CONNECTIVITY newConnectivity) + { + if (newConnectivity == NLM_CONNECTIVITY_DISCONNECTED) { + i2p::transport::transports.SetOnline (false); + LogPrint(eLogInfo, "NetState: disconnected from network"); + } + + if (((int)newConnectivity & (int)NLM_CONNECTIVITY_IPV4_INTERNET) != 0) { + i2p::transport::transports.SetOnline (true); + LogPrint(eLogInfo, "NetState: connected to internet with IPv4 capability"); + } + + if (((int)newConnectivity & (int)NLM_CONNECTIVITY_IPV6_INTERNET) != 0) { + i2p::transport::transports.SetOnline (true); + LogPrint(eLogInfo, "NetState: connected to internet with IPv6 capability"); + } + + if ( + (((int)newConnectivity & (int)NLM_CONNECTIVITY_IPV4_INTERNET) == 0) && + (((int)newConnectivity & (int)NLM_CONNECTIVITY_IPV6_INTERNET) == 0) + ) { + i2p::transport::transports.SetOnline (false); + LogPrint(eLogInfo, "NetState: connected without internet access"); + } + + return S_OK; + } + +private: + + LONG m_ref; +}; + +void SubscribeToEvents(); +void UnSubscribeFromEvents(); + +#else // WINVER == 0x0501 + +void SubscribeToEvents() { } +void UnSubscribeFromEvents() { } + +#endif // WINVER +#endif \ No newline at end of file diff --git a/Win32/Win32Service.cpp b/Win32/Win32Service.cpp index 717bc887..7a6d7abf 100644 --- a/Win32/Win32Service.cpp +++ b/Win32/Win32Service.cpp @@ -1,10 +1,18 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifdef _WIN32 #define _CRT_SECURE_NO_WARNINGS // to use freopen #endif #include "Win32Service.h" #include -#include +//#include #include #include "Daemon.h" diff --git a/Win32/Win32Service.h b/Win32/Win32Service.h index 2830d237..40fff787 100644 --- a/Win32/Win32Service.h +++ b/Win32/Win32Service.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef WIN_32_SERVICE_H__ #define WIN_32_SERVICE_H__ @@ -26,48 +34,48 @@ class I2PService { -public: + public: - I2PService(PSTR pszServiceName, - BOOL fCanStop = TRUE, - BOOL fCanShutdown = TRUE, - BOOL fCanPauseContinue = FALSE); + I2PService(PSTR pszServiceName, + BOOL fCanStop = TRUE, + BOOL fCanShutdown = TRUE, + BOOL fCanPauseContinue = FALSE); - virtual ~I2PService(void); + virtual ~I2PService(void); - static BOOL isService(); - static BOOL Run(I2PService &service); - void Stop(); + static BOOL isService(); + static BOOL Run(I2PService &service); + void Stop(); -protected: + protected: - virtual void OnStart(DWORD dwArgc, PSTR *pszArgv); - virtual void OnStop(); - virtual void OnPause(); - virtual void OnContinue(); - virtual void OnShutdown(); - void SetServiceStatus(DWORD dwCurrentState, - DWORD dwWin32ExitCode = NO_ERROR, - DWORD dwWaitHint = 0); + virtual void OnStart(DWORD dwArgc, PSTR *pszArgv); + virtual void OnStop(); + virtual void OnPause(); + virtual void OnContinue(); + virtual void OnShutdown(); + void SetServiceStatus(DWORD dwCurrentState, + DWORD dwWin32ExitCode = NO_ERROR, + DWORD dwWaitHint = 0); -private: + private: - static void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv); - static void WINAPI ServiceCtrlHandler(DWORD dwCtrl); - void WorkerThread(); - void Start(DWORD dwArgc, PSTR *pszArgv); - void Pause(); - void Continue(); - void Shutdown(); - static I2PService* s_service; - PSTR m_name; - SERVICE_STATUS m_status; - SERVICE_STATUS_HANDLE m_statusHandle; + static void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv); + static void WINAPI ServiceCtrlHandler(DWORD dwCtrl); + void WorkerThread(); + void Start(DWORD dwArgc, PSTR *pszArgv); + void Pause(); + void Continue(); + void Shutdown(); + static I2PService* s_service; + PSTR m_name; + SERVICE_STATUS m_status; + SERVICE_STATUS_HANDLE m_statusHandle; - BOOL m_fStopping; - HANDLE m_hStoppedEvent; + BOOL m_fStopping; + HANDLE m_hStoppedEvent; - std::thread* _worker; + std::thread* _worker; }; void InstallService( @@ -77,8 +85,8 @@ void InstallService( PCSTR pszDependencies, PCSTR pszAccount, PCSTR pszPassword - ); +); void UninstallService(PCSTR pszServiceName); -#endif // WIN_32_SERVICE_H__ \ No newline at end of file +#endif // WIN_32_SERVICE_H__ diff --git a/Win32/installer.iss b/Win32/installer.iss index d4f6f247..88856558 100644 --- a/Win32/installer.iss +++ b/Win32/installer.iss @@ -1,5 +1,5 @@ #define I2Pd_AppName "i2pd" -#define I2Pd_ver "2.30.0" +#define I2Pd_ver "2.32.0" #define I2Pd_Publisher "PurpleI2P" [Setup] diff --git a/Win32/resource.h b/Win32/resource.h index 3b188481..daa1ddcb 100644 --- a/Win32/resource.h +++ b/Win32/resource.h @@ -1,11 +1,11 @@ -//{{NO_DEPENDENCIES}} -#define MAINICON 101 - -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif +//{{NO_DEPENDENCIES}} +#define MAINICON 101 + +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/android/.gitignore b/android/.gitignore index 666c6694..57a0a6cd 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -4,6 +4,7 @@ bin libs log* obj +.cxx .gradle .idea .externalNativeBuild @@ -14,3 +15,4 @@ android.iml build *.iml *.local +*.jks diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index a95e3773..88985138 100755 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -17,8 +17,9 @@ android:label="@string/app_name" android:theme="@android:style/Theme.Holo.Light.DarkActionBar" android:requestLegacyExternalStorage="true" - android:usesCleartextTraffic="true" + android:usesCleartextTraffic="true" > + @@ -30,10 +31,10 @@ android:label="@string/app_name"> - + diff --git a/android/assets/i2pd.conf b/android/assets/i2pd.conf index 312a24ea..14254248 100644 --- a/android/assets/i2pd.conf +++ b/android/assets/i2pd.conf @@ -42,6 +42,8 @@ inbound.quantity = 5 outbound.length = 1 outbound.quantity = 5 signaturetype=7 +i2cp.leaseSetType=3 +i2cp.leaseSetEncType=0,4 keys = proxy-keys.dat # addresshelper = true # outproxy = http://false.i2p diff --git a/android/build.gradle b/android/build.gradle index 6fcb4521..ef6bf36e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -30,8 +30,8 @@ android { applicationId "org.purplei2p.i2pd" targetSdkVersion 29 minSdkVersion 14 - versionCode 2300 - versionName "2.30.0" + versionCode 2320 + versionName "2.32.0" setProperty("archivesBaseName", archivesBaseName + "-" + versionName) ndk { abiFilters 'armeabi-v7a' diff --git a/android/jni/DaemonAndroid.cpp b/android/jni/DaemonAndroid.cpp index c3a1b805..39c06ea0 100644 --- a/android/jni/DaemonAndroid.cpp +++ b/android/jni/DaemonAndroid.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include diff --git a/android/jni/DaemonAndroid.h b/android/jni/DaemonAndroid.h index 64bf64fd..912f6f49 100644 --- a/android/jni/DaemonAndroid.h +++ b/android/jni/DaemonAndroid.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef DAEMON_ANDROID_H #define DAEMON_ANDROID_H diff --git a/android/jni/i2pd_android.cpp b/android/jni/i2pd_android.cpp index da908648..c6e309dd 100755 --- a/android/jni/i2pd_android.cpp +++ b/android/jni/i2pd_android.cpp @@ -1,8 +1,18 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include "org_purplei2p_i2pd_I2PD_JNI.h" #include "DaemonAndroid.h" #include "RouterContext.h" +#include "ClientContext.h" #include "Transports.h" +#include "Tunnel.h" JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getABICompiledWith (JNIEnv *env, jclass clazz) { @@ -61,6 +71,11 @@ JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels i2p::context.SetAcceptsTunnels (true); } +JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_reloadTunnelsConfigs + (JNIEnv *env, jclass clazz) { + i2p::client::context.ReloadConfig(); +} + JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged (JNIEnv *env, jclass clazz, jboolean isConnected) { bool isConnectedBool = (bool) isConnected; @@ -92,3 +107,8 @@ JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setDataDir // Set DataDir i2p::android::SetDataDir(dataDir); } + +JNIEXPORT jint JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_GetTransitTunnelsCount + (JNIEnv *env, jclass clazz) { + return i2p::tunnel::tunnels.CountTransitTunnels(); +} diff --git a/android/jni/org_purplei2p_i2pd_I2PD_JNI.h b/android/jni/org_purplei2p_i2pd_I2PD_JNI.h index 6939a153..68935ad1 100644 --- a/android/jni/org_purplei2p_i2pd_I2PD_JNI.h +++ b/android/jni/org_purplei2p_i2pd_I2PD_JNI.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class org_purplei2p_i2pd_I2PD_JNI */ @@ -27,12 +35,18 @@ JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopAcceptingTunnels JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels (JNIEnv *, jclass); +JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_reloadTunnelsConfigs + (JNIEnv *, jclass); + JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged (JNIEnv * env, jclass clazz, jboolean isConnected); JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setDataDir (JNIEnv *env, jclass clazz, jstring jdataDir); +JNIEXPORT jint JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_GetTransitTunnelsCount + (JNIEnv *, jclass); + #ifdef __cplusplus } #endif diff --git a/android/res/layout/activity_perms_asker.xml b/android/res/layout/activity_perms_asker.xml index d2d12cb6..778c9ef5 100644 --- a/android/res/layout/activity_perms_asker.xml +++ b/android/res/layout/activity_perms_asker.xml @@ -15,13 +15,12 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/horizontal_page_margin" - android:visibility="gone" - /> + android:visibility="gone" /> \r\n"; + s << "\r\n
\r\n"; } void ShowTransitTunnels (std::stringstream& s) @@ -861,8 +900,8 @@ namespace http { void HTTPConnection::Receive () { m_Socket->async_read_some (boost::asio::buffer (m_Buffer, HTTP_CONNECTION_BUFFER_SIZE), - std::bind(&HTTPConnection::HandleReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); + std::bind(&HTTPConnection::HandleReceive, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); } void HTTPConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) @@ -1125,6 +1164,19 @@ namespace http { res.add_header("Refresh", redirect.c_str()); return; } + else if (cmd == HTTP_COMMAND_LIMITTRANSIT) + { + uint32_t limit = std::stoul(params["limit"], nullptr); + if (limit > 0 && limit <= 65535) + SetMaxNumTransitTunnels (limit); + else { + s << "ERROR: Transit tunnels count must not exceed 65535

\r\n"; + s << "Back to commands list
\r\n"; + s << "

You will be redirected back in 5 seconds"; + res.add_header("Refresh", redirect.c_str()); + return; + } + } else { res.code = 400; @@ -1182,8 +1234,9 @@ namespace http { i2p::config::SetOption("http.pass", pass); LogPrint(eLogInfo, "HTTPServer: password set to ", pass); } + m_IsRunning = true; - m_Thread = std::unique_ptr(new std::thread (std::bind (&HTTPServer::Run, this))); + m_Thread.reset (new std::thread (std::bind (&HTTPServer::Run, this))); m_Acceptor.listen (); Accept (); } diff --git a/daemon/HTTPServer.h b/daemon/HTTPServer.h index abc4fea5..a977e3e8 100644 --- a/daemon/HTTPServer.h +++ b/daemon/HTTPServer.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef HTTP_SERVER_H__ #define HTTP_SERVER_H__ @@ -79,17 +87,17 @@ namespace http std::string m_Hostname; }; - //all the below functions are also used by Qt GUI, see mainwindow.cpp -> getStatusPageHtml - enum OutputFormatEnum { forWebConsole, forQtUi }; - void ShowStatus (std::stringstream& s, bool includeHiddenContent, OutputFormatEnum outputFormat); - void ShowLocalDestinations (std::stringstream& s); - void ShowLeasesSets(std::stringstream& s); - void ShowTunnels (std::stringstream& s); - void ShowTransitTunnels (std::stringstream& s); - void ShowTransports (std::stringstream& s); - void ShowSAMSessions (std::stringstream& s); - void ShowI2PTunnels (std::stringstream& s); - void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token); + //all the below functions are also used by Qt GUI, see mainwindow.cpp -> getStatusPageHtml + enum OutputFormatEnum { forWebConsole, forQtUi }; + void ShowStatus (std::stringstream& s, bool includeHiddenContent, OutputFormatEnum outputFormat); + void ShowLocalDestinations (std::stringstream& s); + void ShowLeasesSets(std::stringstream& s); + void ShowTunnels (std::stringstream& s); + void ShowTransitTunnels (std::stringstream& s); + void ShowTransports (std::stringstream& s); + void ShowSAMSessions (std::stringstream& s); + void ShowI2PTunnels (std::stringstream& s); + void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token); } // http } // i2p diff --git a/daemon/I2PControl.cpp b/daemon/I2PControl.cpp index acf054f1..77614f2f 100644 --- a/daemon/I2PControl.cpp +++ b/daemon/I2PControl.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include @@ -59,29 +67,28 @@ namespace client m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem); // handlers - m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler; - m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler; - m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler; - m_MethodHandlers["RouterInfo"] = &I2PControlService::RouterInfoHandler; - m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler; - m_MethodHandlers["NetworkSetting"] = &I2PControlService::NetworkSettingHandler; - m_MethodHandlers["ClientServicesInfo"] = &I2PControlService::ClientServicesInfoHandler; + m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler; + m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler; + m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler; + m_MethodHandlers["RouterInfo"] = &I2PControlService::RouterInfoHandler; + m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler; + m_MethodHandlers["NetworkSetting"] = &I2PControlService::NetworkSettingHandler; + m_MethodHandlers["ClientServicesInfo"] = &I2PControlService::ClientServicesInfoHandler; // I2PControl m_I2PControlHandlers["i2pcontrol.password"] = &I2PControlService::PasswordHandler; // RouterInfo - m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlService::UptimeHandler; - m_RouterInfoHandlers["i2p.router.version"] = &I2PControlService::VersionHandler; - m_RouterInfoHandlers["i2p.router.status"] = &I2PControlService::StatusHandler; - m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlService::NetDbKnownPeersHandler; - m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlService::NetDbActivePeersHandler; - m_RouterInfoHandlers["i2p.router.net.bw.inbound.1s"] = &I2PControlService::InboundBandwidth1S; - m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S; - m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler; + m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlService::UptimeHandler; + m_RouterInfoHandlers["i2p.router.version"] = &I2PControlService::VersionHandler; + m_RouterInfoHandlers["i2p.router.status"] = &I2PControlService::StatusHandler; + m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlService::NetDbKnownPeersHandler; + m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlService::NetDbActivePeersHandler; + m_RouterInfoHandlers["i2p.router.net.bw.inbound.1s"] = &I2PControlService::InboundBandwidth1S; + m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S; + m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler; m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlService::TunnelsParticipatingHandler; - m_RouterInfoHandlers["i2p.router.net.tunnels.successrate"] = -&I2PControlService::TunnelsSuccessRateHandler; + m_RouterInfoHandlers["i2p.router.net.tunnels.successrate"] = &I2PControlService::TunnelsSuccessRateHandler; m_RouterInfoHandlers["i2p.router.net.total.received.bytes"] = &I2PControlService::NetTotalReceivedBytes; m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlService::NetTotalSentBytes; @@ -97,10 +104,10 @@ namespace client // ClientServicesInfo m_ClientServicesInfoHandlers["I2PTunnel"] = &I2PControlService::I2PTunnelInfoHandler; m_ClientServicesInfoHandlers["HTTPProxy"] = &I2PControlService::HTTPProxyInfoHandler; - m_ClientServicesInfoHandlers["SOCKS"] = &I2PControlService::SOCKSInfoHandler; - m_ClientServicesInfoHandlers["SAM"] = &I2PControlService::SAMInfoHandler; - m_ClientServicesInfoHandlers["BOB"] = &I2PControlService::BOBInfoHandler; - m_ClientServicesInfoHandlers["I2CP"] = &I2PControlService::I2CPInfoHandler; + m_ClientServicesInfoHandlers["SOCKS"] = &I2PControlService::SOCKSInfoHandler; + m_ClientServicesInfoHandlers["SAM"] = &I2PControlService::SAMInfoHandler; + m_ClientServicesInfoHandlers["BOB"] = &I2PControlService::BOBInfoHandler; + m_ClientServicesInfoHandlers["I2CP"] = &I2PControlService::I2CPInfoHandler; } I2PControlService::~I2PControlService () @@ -401,8 +408,8 @@ namespace client auto it1 = m_RouterInfoHandlers.find (it->first); if (it1 != m_RouterInfoHandlers.end ()) { - if (!first) results << ","; - else first = false; + if (!first) results << ","; + else first = false; (this->*(it1->second))(results); } else @@ -500,7 +507,7 @@ namespace client m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(1)); // 1 second to make sure response has been sent m_ShutdownTimer.async_wait ( [](const boost::system::error_code& ecode) - { + { Daemon.running = 0; }); } @@ -514,7 +521,7 @@ namespace client m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(timeout + 1)); // + 1 second m_ShutdownTimer.async_wait ( [](const boost::system::error_code& ecode) - { + { Daemon.running = 0; }); } diff --git a/daemon/I2PControl.h b/daemon/I2PControl.h index 3233ad12..d731c24e 100644 --- a/daemon/I2PControl.h +++ b/daemon/I2PControl.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef I2P_CONTROL_H__ #define I2P_CONTROL_H__ @@ -27,6 +35,7 @@ namespace client class I2PControlService { typedef boost::asio::ssl::stream ssl_socket; + public: I2PControlService (const std::string& address, int port); diff --git a/daemon/UPnP.cpp b/daemon/UPnP.cpp index b2ebb005..852122f2 100644 --- a/daemon/UPnP.cpp +++ b/daemon/UPnP.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifdef USE_UPNP #include #include @@ -80,10 +88,10 @@ namespace transport void UPnP::Discover () { bool isError; - int err; + int err; #if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS)) - err = UPNPDISCOVER_SUCCESS; + err = UPNPDISCOVER_SUCCESS; #if (MINIUPNPC_API_VERSION >= 14) m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0, 0, 2, &err); @@ -92,9 +100,9 @@ namespace transport #endif isError = err != UPNPDISCOVER_SUCCESS; -#else // MINIUPNPC_API_VERSION >= 8 - err = 0; - m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0); +#else // MINIUPNPC_API_VERSION >= 8 + err = 0; + m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0); isError = m_Devlist == NULL; #endif // MINIUPNPC_API_VERSION >= 8 { @@ -105,15 +113,15 @@ namespace transport if (isError) { - LogPrint (eLogError, "UPnP: unable to discover Internet Gateway Devices: error ", err); + LogPrint (eLogError, "UPnP: unable to discover Internet Gateway Devices: error ", err); return; } err = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr)); - m_upnpUrlsInitialized=err!=0; + m_upnpUrlsInitialized = err != 0; if (err == UPNP_IGD_VALID_CONNECTED) { - err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress); + err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress); if(err != UPNPCOMMAND_SUCCESS) { LogPrint (eLogError, "UPnP: unable to get external address: error ", err); @@ -124,14 +132,14 @@ namespace transport LogPrint (eLogError, "UPnP: found Internet Gateway Device ", m_upnpUrls.controlURL); if (!m_externalIPAddress[0]) { - LogPrint (eLogError, "UPnP: found Internet Gateway Device doesn't know our external address"); + LogPrint (eLogError, "UPnP: found Internet Gateway Device doesn't know our external address"); return; } } } else { - LogPrint (eLogError, "UPnP: unable to find valid Internet Gateway Device: error ", err); + LogPrint (eLogError, "UPnP: unable to find valid Internet Gateway Device: error ", err); return; } @@ -182,7 +190,7 @@ namespace transport err = CheckMapping (strPort.c_str (), strType.c_str ()); if (err != UPNPCOMMAND_SUCCESS) // if mapping not found { - LogPrint (eLogDebug, "UPnP: possibly port ", strPort, " is not forwarded: return code ", err); + LogPrint (eLogDebug, "UPnP: possibly port ", strPort, " is not forwarded: return code ", err); #if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS)) err = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), NULL, NULL); @@ -202,7 +210,7 @@ namespace transport } else { - LogPrint (eLogDebug, "UPnP: external forward from ", m_NetworkAddr, ":", strPort, " exists on current Internet Gateway Device"); + LogPrint (eLogDebug, "UPnP: external forward from ", m_NetworkAddr, ":", strPort, " exists on current Internet Gateway Device"); return; } } @@ -219,14 +227,14 @@ namespace transport void UPnP::CloseMapping (std::shared_ptr address) { - if(!m_upnpUrlsInitialized) { - return; - } + if(!m_upnpUrlsInitialized) { + return; + } std::string strType (GetProto (address)), strPort (std::to_string (address->port)); int err = UPNPCOMMAND_SUCCESS; - + err = CheckMapping (strPort.c_str (), strType.c_str ()); - if (err == UPNPCOMMAND_SUCCESS) + if (err == UPNPCOMMAND_SUCCESS) { err = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), NULL); LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", err); @@ -237,11 +245,11 @@ namespace transport { freeUPNPDevlist (m_Devlist); m_Devlist = 0; - if(m_upnpUrlsInitialized){ - FreeUPNPUrls (&m_upnpUrls); - m_upnpUrlsInitialized=false; - } - } + if(m_upnpUrlsInitialized){ + FreeUPNPUrls (&m_upnpUrls); + m_upnpUrlsInitialized=false; + } + } std::string UPnP::GetProto (std::shared_ptr address) { diff --git a/daemon/UPnP.h b/daemon/UPnP.h index e66f0aff..e8220e24 100644 --- a/daemon/UPnP.h +++ b/daemon/UPnP.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef __UPNP_H__ #define __UPNP_H__ @@ -33,41 +41,41 @@ namespace transport { public: - UPnP (); - ~UPnP (); - void Close (); + UPnP (); + ~UPnP (); + void Close (); - void Start (); - void Stop (); + void Start (); + void Stop (); private: - void Discover (); - int CheckMapping (const char* port, const char* type); - void PortMapping (); - void TryPortMapping (std::shared_ptr address); - void CloseMapping (); - void CloseMapping (std::shared_ptr address); + void Discover (); + int CheckMapping (const char* port, const char* type); + void PortMapping (); + void TryPortMapping (std::shared_ptr address); + void CloseMapping (); + void CloseMapping (std::shared_ptr address); - void Run (); - std::string GetProto (std::shared_ptr address); + void Run (); + std::string GetProto (std::shared_ptr address); private: - bool m_IsRunning; - std::unique_ptr m_Thread; - std::condition_variable m_Started; - std::mutex m_StartedMutex; - boost::asio::io_service m_Service; - boost::asio::deadline_timer m_Timer; - bool m_upnpUrlsInitialized=false; - struct UPNPUrls m_upnpUrls; - struct IGDdatas m_upnpData; + bool m_IsRunning; + std::unique_ptr m_Thread; + std::condition_variable m_Started; + std::mutex m_StartedMutex; + boost::asio::io_service m_Service; + boost::asio::deadline_timer m_Timer; + bool m_upnpUrlsInitialized = false; + struct UPNPUrls m_upnpUrls; + struct IGDdatas m_upnpData; - // For miniupnpc - struct UPNPDev * m_Devlist = 0; - char m_NetworkAddr[64]; - char m_externalIPAddress[40]; + // For miniupnpc + struct UPNPDev * m_Devlist = 0; + char m_NetworkAddr[64]; + char m_externalIPAddress[40]; }; } } @@ -79,10 +87,10 @@ namespace transport { class UPnP { public: - UPnP () {}; - ~UPnP () {}; - void Start () { LogPrint(eLogWarning, "UPnP: this module was disabled at compile-time"); } - void Stop () {}; + UPnP () {}; + ~UPnP () {}; + void Start () { LogPrint(eLogWarning, "UPnP: this module was disabled at compile-time"); } + void Stop () {}; }; } } diff --git a/daemon/UnixDaemon.cpp b/daemon/UnixDaemon.cpp index 3dd38fba..ffc5f1c0 100644 --- a/daemon/UnixDaemon.cpp +++ b/daemon/UnixDaemon.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include "Daemon.h" #ifndef _WIN32 @@ -167,7 +175,7 @@ namespace i2p sigaction(SIGABRT, &sa, 0); sigaction(SIGTERM, &sa, 0); sigaction(SIGINT, &sa, 0); - sigaction(SIGPIPE, &sa, 0); + sigaction(SIGPIPE, &sa, 0); return Daemon_Singleton::start(); } @@ -175,7 +183,6 @@ namespace i2p bool DaemonLinux::stop() { i2p::fs::Remove(pidfile); - return Daemon_Singleton::stop(); } @@ -197,5 +204,4 @@ namespace i2p } } } - #endif diff --git a/daemon/i2pd.cpp b/daemon/i2pd.cpp index 8718ad0c..028aa916 100644 --- a/daemon/i2pd.cpp +++ b/daemon/i2pd.cpp @@ -1,24 +1,31 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include "Daemon.h" #if defined(QT_GUI_LIB) - namespace i2p { namespace qt { - int RunQT (int argc, char* argv[]); + int RunQT (int argc, char* argv[]); } } -int main( int argc, char* argv[] ) -{ - return i2p::qt::RunQT (argc, argv); -} +int main( int argc, char* argv[] ) +{ + return i2p::qt::RunQT (argc, argv); +} #else int main( int argc, char* argv[] ) { - if (Daemon.init(argc, argv)) + if (Daemon.init(argc, argv)) { if (Daemon.start()) Daemon.run (); diff --git a/debian/changelog b/debian/changelog index da177cbd..43c3fc60 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,18 @@ +i2pd (2.32.0-1) unstable; urgency=high + + * updated to version 2.32.0/0.9.46 + * updated systemd service file (see #1394) + * updated apparmor profile (see 9318388007cff0495b4b360d0480f4fc1219a9dc) + * updated logrotate config and moved it to contrib + + -- r4sas Mon, 25 May 2020 12:45:00 +0000 + +i2pd (2.31.0-1) unstable; urgency=medium + + * updated to version 2.31.0 + + -- orignal Fri, 10 Apr 2020 16:00:00 +0000 + i2pd (2.30.0-1) unstable; urgency=medium * updated to version 2.30.0/0.9.45 diff --git a/debian/copyright b/debian/copyright index 3c513020..9f18f53a 100644 --- a/debian/copyright +++ b/debian/copyright @@ -3,7 +3,7 @@ Upstream-Name: i2pd Source: https://github.com/PurpleI2P Files: * -Copyright: 2013-2017 PurpleI2P +Copyright: 2013-2020 PurpleI2P License: BSD-3-clause Files: qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistro.aidl @@ -16,8 +16,8 @@ License: BSD-2-Clause Files: debian/* Copyright: 2013-2015 Kill Your TV 2014-2016 hagen - 2016-2017 R4SAS - 2017-2018 Yangfl + 2016-2020 R4SAS + 2017-2020 Yangfl License: GPL-2+ License: BSD-3-clause diff --git a/debian/i2pd.logrotate b/debian/i2pd.logrotate deleted file mode 100644 index 63c44270..00000000 --- a/debian/i2pd.logrotate +++ /dev/null @@ -1,9 +0,0 @@ -/var/log/i2pd/i2pd.log { - rotate 6 - daily - missingok - notifempty - compress - delaycompress - copytruncate -} diff --git a/debian/i2pd.logrotate b/debian/i2pd.logrotate new file mode 120000 index 00000000..40dea037 --- /dev/null +++ b/debian/i2pd.logrotate @@ -0,0 +1 @@ +../contrib/i2pd.logrotate \ No newline at end of file diff --git a/debian/patches/01-tune-build-opts.patch b/debian/patches/01-tune-build-opts.patch index dd2b4638..e06a8621 100644 --- a/debian/patches/01-tune-build-opts.patch +++ b/debian/patches/01-tune-build-opts.patch @@ -1,17 +1,15 @@ -diff --git a/Makefile b/Makefile -index bdadfe0..2f71eec 100644 ---- a/Makefile -+++ b/Makefile -@@ -9,10 +9,10 @@ DEPS := obj/make.dep - +Index: i2pd/Makefile +=================================================================== +--- i2pd.orig/Makefile ++++ i2pd/Makefile +@@ -13,8 +13,8 @@ DAEMON_SRC_DIR := daemon + include filelist.mk - + -USE_AESNI := yes -+USE_AESNI := no -USE_AVX := yes ++USE_AESNI := no +USE_AVX := no USE_STATIC := no USE_MESHNET := no USE_UPNP := no - DEBUG := yes - diff --git a/debian/patches/02-fix-1210.patch b/debian/patches/02-fix-1210.patch index 6a4caf94..9ad9bb0f 100644 --- a/debian/patches/02-fix-1210.patch +++ b/debian/patches/02-fix-1210.patch @@ -4,10 +4,12 @@ Author: r4sas Bug: https://github.com/PurpleI2P/i2pd/issues/1210 Reviewed-By: r4sas -Last-Update: 2018-08-25 +Last-Update: 2020-05-25 ---- a/contrib/i2pd.service -+++ b/contrib/i2pd.service +Index: i2pd/contrib/i2pd.service +=================================================================== +--- i2pd.orig/contrib/i2pd.service ++++ i2pd/contrib/i2pd.service @@ -6,10 +6,10 @@ After=network.target [Service] User=i2pd @@ -21,5 +23,5 @@ Last-Update: 2018-08-25 +#LogsDirectory=i2pd +#LogsDirectoryMode=0700 Type=forking - ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/var/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service - ExecReload=/bin/kill -HUP $MAINPID + ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service + ExecReload=/bin/sh -c "kill -HUP $MAINPID" diff --git a/debian/postrm b/debian/postrm index 53ab15e7..ba69785e 100755 --- a/debian/postrm +++ b/debian/postrm @@ -6,7 +6,7 @@ if [ "$1" = "purge" ]; then rm -rf /etc/i2pd rm -rf /var/lib/i2pd rm -rf /var/log/i2pd - rm -rf /var/run/i2pd + rm -rf /run/i2pd fi #DEBHELPER# diff --git a/libi2pd/Base.cpp b/libi2pd/Base.cpp index f80f2751..921c20af 100644 --- a/libi2pd/Base.cpp +++ b/libi2pd/Base.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include @@ -7,7 +15,8 @@ namespace i2p { namespace data { - static const char T32[32] = { + static const char T32[32] = + { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', @@ -29,15 +38,16 @@ namespace data * Direct Substitution Table */ - static const char T64[64] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '-', '~' + static const char T64[64] = + { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '-', '~' }; const char * GetBase64SubstitutionTable () @@ -67,14 +77,12 @@ namespace data * */ - size_t /* Number of bytes in the encoded buffer */ - ByteStreamToBase64 ( - const uint8_t * InBuffer, /* Input buffer, binary data */ - size_t InCount, /* Number of bytes in the input buffer */ - char * OutBuffer, /* output buffer */ - size_t len /* length of output buffer */ + size_t ByteStreamToBase64 ( /* Number of bytes in the encoded buffer */ + const uint8_t * InBuffer, /* Input buffer, binary data */ + size_t InCount, /* Number of bytes in the input buffer */ + char * OutBuffer, /* output buffer */ + size_t len /* length of output buffer */ ) - { unsigned char * ps; unsigned char * pd; @@ -83,55 +91,60 @@ namespace data int i; int n; int m; - size_t outCount; + size_t outCount; ps = (unsigned char *)InBuffer; - n = InCount/3; - m = InCount%3; + n = InCount / 3; + m = InCount % 3; if (!m) - outCount = 4*n; + outCount = 4 * n; else - outCount = 4*(n+1); + outCount = 4 * (n + 1); + if (outCount > len) return 0; + pd = (unsigned char *)OutBuffer; - for ( i = 0; i>= 2; /* base64 digit #1 */ - *pd++ = T64[acc_1]; - acc_1 = *ps++; - acc_2 |= acc_1 >> 4; /* base64 digit #2 */ - *pd++ = T64[acc_2]; - acc_1 &= 0x0f; - acc_1 <<=2; - acc_2 = *ps++; - acc_1 |= acc_2>>6; /* base64 digit #3 */ - *pd++ = T64[acc_1]; - acc_2 &= 0x3f; /* base64 digit #4 */ - *pd++ = T64[acc_2]; + for ( i = 0; i < n; i++ ) + { + acc_1 = *ps++; + acc_2 = (acc_1 << 4) & 0x30; + acc_1 >>= 2; /* base64 digit #1 */ + *pd++ = T64[acc_1]; + acc_1 = *ps++; + acc_2 |= acc_1 >> 4; /* base64 digit #2 */ + *pd++ = T64[acc_2]; + acc_1 &= 0x0f; + acc_1 <<= 2; + acc_2 = *ps++; + acc_1 |= acc_2 >> 6; /* base64 digit #3 */ + *pd++ = T64[acc_1]; + acc_2 &= 0x3f; /* base64 digit #4 */ + *pd++ = T64[acc_2]; } - if ( m == 1 ){ - acc_1 = *ps++; - acc_2 = (acc_1<<4)&0x3f; /* base64 digit #2 */ - acc_1 >>= 2; /* base64 digit #1 */ - *pd++ = T64[acc_1]; - *pd++ = T64[acc_2]; - *pd++ = P64; - *pd++ = P64; + if ( m == 1 ) + { + acc_1 = *ps++; + acc_2 = (acc_1 << 4) & 0x3f; /* base64 digit #2 */ + acc_1 >>= 2; /* base64 digit #1 */ + *pd++ = T64[acc_1]; + *pd++ = T64[acc_2]; + *pd++ = P64; + *pd++ = P64; } - else if ( m == 2 ){ - acc_1 = *ps++; - acc_2 = (acc_1<<4)&0x3f; - acc_1 >>= 2; /* base64 digit #1 */ - *pd++ = T64[acc_1]; - acc_1 = *ps++; - acc_2 |= acc_1 >> 4; /* base64 digit #2 */ - *pd++ = T64[acc_2]; - acc_1 &= 0x0f; - acc_1 <<=2; /* base64 digit #3 */ - *pd++ = T64[acc_1]; - *pd++ = P64; + else if ( m == 2 ) + { + acc_1 = *ps++; + acc_2 = (acc_1 << 4) & 0x3f; + acc_1 >>= 2; /* base64 digit #1 */ + *pd++ = T64[acc_1]; + acc_1 = *ps++; + acc_2 |= acc_1 >> 4; /* base64 digit #2 */ + *pd++ = T64[acc_2]; + acc_1 &= 0x0f; + acc_1 <<= 2; /* base64 digit #3 */ + *pd++ = T64[acc_1]; + *pd++ = P64; } return outCount; @@ -147,12 +160,11 @@ namespace data * */ - size_t /* Number of output bytes */ - Base64ToByteStream ( - const char * InBuffer, /* BASE64 encoded buffer */ - size_t InCount, /* Number of input bytes */ - uint8_t * OutBuffer, /* output buffer length */ - size_t len /* length of output buffer */ + size_t Base64ToByteStream ( /* Number of output bytes */ + const char * InBuffer, /* BASE64 encoded buffer */ + size_t InCount, /* Number of input bytes */ + uint8_t * OutBuffer, /* output buffer length */ + size_t len /* length of output buffer */ ) { unsigned char * ps; @@ -162,42 +174,52 @@ namespace data int i; int n; int m; - size_t outCount; + size_t outCount; + + if (isFirstTime) + iT64Build(); + + n = InCount / 4; + m = InCount % 4; - if (isFirstTime) iT64Build(); - n = InCount/4; - m = InCount%4; if (InCount && !m) - outCount = 3*n; - else { - outCount = 0; - return 0; + outCount = 3 * n; + else + { + outCount = 0; + return 0; } ps = (unsigned char *)(InBuffer + InCount - 1); - while ( *ps-- == P64 ) outCount--; + while ( *ps-- == P64 ) + outCount--; ps = (unsigned char *)InBuffer; - if (outCount > len) return -1; + if (outCount > len) + return -1; + pd = OutBuffer; auto endOfOutBuffer = OutBuffer + outCount; - for ( i = 0; i < n; i++ ){ - acc_1 = iT64[*ps++]; - acc_2 = iT64[*ps++]; - acc_1 <<= 2; - acc_1 |= acc_2>>4; - *pd++ = acc_1; - if (pd >= endOfOutBuffer) break; + for ( i = 0; i < n; i++ ) + { + acc_1 = iT64[*ps++]; + acc_2 = iT64[*ps++]; + acc_1 <<= 2; + acc_1 |= acc_2 >> 4; + *pd++ = acc_1; + if (pd >= endOfOutBuffer) + break; - acc_2 <<= 4; - acc_1 = iT64[*ps++]; - acc_2 |= acc_1 >> 2; - *pd++ = acc_2; - if (pd >= endOfOutBuffer) break; + acc_2 <<= 4; + acc_1 = iT64[*ps++]; + acc_2 |= acc_1 >> 2; + *pd++ = acc_2; + if (pd >= endOfOutBuffer) + break; - acc_2 = iT64[*ps++]; - acc_2 |= acc_1 << 6; - *pd++ = acc_2; + acc_2 = iT64[*ps++]; + acc_2 |= acc_1 << 6; + *pd++ = acc_2; } return outCount; @@ -206,20 +228,25 @@ namespace data size_t Base64EncodingBufferSize (const size_t input_size) { auto d = div (input_size, 3); - if (d.rem) d.quot++; - return 4*d.quot; + if (d.rem) + d.quot++; + + return 4 * d.quot; } std::string ToBase64Standard (const std::string& in) { - auto len = Base64EncodingBufferSize (in.length ()); - char * str = new char[len+1]; + auto len = Base64EncodingBufferSize (in.length ()); + char * str = new char[len + 1]; auto l = ByteStreamToBase64 ((const uint8_t *)in.c_str (), in.length (), str, len); str[l] = 0; // replace '-' by '+' and '~' by '/' for (size_t i = 0; i < l; i++) - if (str[i] == '-') str[i] = '+'; - else if (str[i] == '~') str[i] = '/'; + if (str[i] == '-') + str[i] = '+'; + else if (str[i] == '~') + str[i] = '/'; + std::string s(str); delete[] str; return s; @@ -236,10 +263,10 @@ namespace data static void iT64Build() { - int i; + int i; isFirstTime = 0; - for ( i=0; i<256; i++ ) iT64[i] = -1; - for ( i=0; i<64; i++ ) iT64[(int)T64[i]] = i; + for ( i = 0; i < 256; i++ ) iT64[i] = -1; + for ( i = 0; i < 64; i++ ) iT64[(int)T64[i]] = i; iT64[(int)P64] = 0; } @@ -302,4 +329,3 @@ namespace data } } } - diff --git a/libi2pd/Base.h b/libi2pd/Base.h index a273f468..073d9b40 100644 --- a/libi2pd/Base.h +++ b/libi2pd/Base.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef BASE_H__ #define BASE_H__ @@ -15,13 +23,13 @@ namespace data { size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen); size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen); - /** - Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes - */ + /** + Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes + */ size_t Base64EncodingBufferSize(const size_t input_size); - - std::string ToBase64Standard (const std::string& in); // using standard table, for Proxy-Authorization - + + std::string ToBase64Standard (const std::string& in); // using standard table, for Proxy-Authorization + } // data } // i2p diff --git a/libi2pd/Blinding.cpp b/libi2pd/Blinding.cpp index 8d3f4b40..6770d223 100644 --- a/libi2pd/Blinding.cpp +++ b/libi2pd/Blinding.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include // for crc32 #include #include @@ -17,21 +25,21 @@ namespace i2p namespace data { static EC_POINT * BlindPublicKeyECDSA (const EC_GROUP * group, const EC_POINT * pub, const uint8_t * seed) - { + { BN_CTX * ctx = BN_CTX_new (); BN_CTX_start (ctx); - BIGNUM * q = BN_CTX_get (ctx); + BIGNUM * q = BN_CTX_get (ctx); EC_GROUP_get_order (group, q, ctx); // calculate alpha = seed mod q BIGNUM * alpha = BN_CTX_get (ctx); - BN_bin2bn (seed, 64, alpha); // seed is in BigEndian + BN_bin2bn (seed, 64, alpha); // seed is in BigEndian BN_mod (alpha, alpha, q, ctx); // % q // A' = BLIND_PUBKEY(A, alpha) = A + DERIVE_PUBLIC(alpha) auto p = EC_POINT_new (group); EC_POINT_mul (group, p, alpha, nullptr, nullptr, ctx); // B*alpha EC_POINT_add (group, p, pub, p, ctx); // pub + B*alpha BN_CTX_end (ctx); - BN_CTX_free (ctx); + BN_CTX_free (ctx); return p; } @@ -39,18 +47,18 @@ namespace data { BN_CTX * ctx = BN_CTX_new (); BN_CTX_start (ctx); - BIGNUM * q = BN_CTX_get (ctx); + BIGNUM * q = BN_CTX_get (ctx); EC_GROUP_get_order (group, q, ctx); // calculate alpha = seed mod q BIGNUM * alpha = BN_CTX_get (ctx); - BN_bin2bn (seed, 64, alpha); // seed is in BigEndian - BN_mod (alpha, alpha, q, ctx); // % q + BN_bin2bn (seed, 64, alpha); // seed is in BigEndian + BN_mod (alpha, alpha, q, ctx); // % q BN_add (alpha, alpha, priv); // alpha = alpha + priv - // a' = BLIND_PRIVKEY(a, alpha) = (a + alpha) mod q + // a' = BLIND_PRIVKEY(a, alpha) = (a + alpha) mod q BN_mod (blindedPriv, alpha, q, ctx); // % q BN_CTX_end (ctx); BN_CTX_free (ctx); - } + } static void BlindEncodedPublicKeyECDSA (size_t publicKeyLen, const EC_GROUP * group, const uint8_t * pub, const uint8_t * seed, uint8_t * blindedPub) { @@ -63,7 +71,7 @@ namespace data EC_POINT_get_affine_coordinates_GFp (group, p1, x, y, NULL); EC_POINT_free (p1); i2p::crypto::bn2buf (x, blindedPub, publicKeyLen/2); - i2p::crypto::bn2buf (y, blindedPub + publicKeyLen/2, publicKeyLen/2); + i2p::crypto::bn2buf (y, blindedPub + publicKeyLen/2, publicKeyLen/2); BN_free (x); BN_free (y); } @@ -85,7 +93,7 @@ namespace data i2p::crypto::bn2buf (x, blindedPub, publicKeyLen/2); i2p::crypto::bn2buf (y, blindedPub + publicKeyLen/2, publicKeyLen/2); BN_free (x); BN_free (y); - } + } template static size_t BlindECDSA (i2p::data::SigningKeyType sigType, const uint8_t * key, const uint8_t * seed, Fn blind, Args&&...args) @@ -97,7 +105,7 @@ namespace data { case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256: { - publicKeyLength = i2p::crypto::ECDSAP256_KEY_LENGTH; + publicKeyLength = i2p::crypto::ECDSAP256_KEY_LENGTH; group = EC_GROUP_new_by_curve_name (NID_X9_62_prime256v1); break; } @@ -116,18 +124,18 @@ namespace data default: LogPrint (eLogError, "Blinding: signature type ", (int)sigType, " is not ECDSA"); } - if (group) + if (group) { blind (publicKeyLength, group, key, seed, std::forward(args)...); EC_GROUP_free (group); - } + } return publicKeyLength; } //---------------------------------------------------------- const uint8_t B33_TWO_BYTES_SIGTYPE_FLAG = 0x01; - const uint8_t B33_PER_SECRET_FLAG = 0x02; // not used for now + const uint8_t B33_PER_SECRET_FLAG = 0x02; // not used for now const uint8_t B33_PER_CLIENT_AUTH_FLAG = 0x04; BlindedPublicKey::BlindedPublicKey (std::shared_ptr identity, bool clientAuth): @@ -138,7 +146,7 @@ namespace data m_PublicKey.resize (len); memcpy (m_PublicKey.data (), identity->GetSigningPublicKeyBuffer (), len); m_SigType = identity->GetSigningKeyType (); - m_BlindedSigType = m_SigType; + m_BlindedSigType = m_SigType; } BlindedPublicKey::BlindedPublicKey (const std::string& b33): @@ -150,12 +158,12 @@ namespace data { LogPrint (eLogError, "Blinding: malformed b33 ", b33); return; - } - uint32_t checksum = crc32 (0, addr + 3, l - 3); + } + uint32_t checksum = crc32 (0, addr + 3, l - 3); // checksum is Little Endian - addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16); + addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16); uint8_t flags = addr[0]; - size_t offset = 1; + size_t offset = 1; if (flags & B33_TWO_BYTES_SIGTYPE_FLAG) // two bytes signatures { m_SigType = bufbe16toh (addr + offset); offset += 2; @@ -178,7 +186,7 @@ namespace data memcpy (m_PublicKey.data (), addr + offset, len); } else - LogPrint (eLogError, "Blinding: public key in b33 address is too short for signature type ", (int)m_SigType); + LogPrint (eLogError, "Blinding: public key in b33 address is too short for signature type ", (int)m_SigType); } else LogPrint (eLogError, "Blinding: unknown signature type ", (int)m_SigType, " in b33"); @@ -189,25 +197,25 @@ namespace data if (m_PublicKey.size () > 32) return ""; // assume 25519 uint8_t addr[35]; char str[60]; // TODO: define actual length uint8_t flags = 0; - if (m_IsClientAuth) flags |= B33_PER_CLIENT_AUTH_FLAG; + if (m_IsClientAuth) flags |= B33_PER_CLIENT_AUTH_FLAG; addr[0] = flags; // flags addr[1] = m_SigType; // sig type addr[2] = m_BlindedSigType; // blinded sig type memcpy (addr + 3, m_PublicKey.data (), m_PublicKey.size ()); - uint32_t checksum = crc32 (0, addr + 3, m_PublicKey.size ()); + uint32_t checksum = crc32 (0, addr + 3, m_PublicKey.size ()); // checksum is Little Endian - addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16); + addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16); auto l = ByteStreamToBase32 (addr, m_PublicKey.size () + 3, str, 60); return std::string (str, str + l); } void BlindedPublicKey::GetCredential (uint8_t * credential) const { - // A = destination's signing public key + // A = destination's signing public key // stA = signature type of A, 2 bytes big endian uint16_t stA = htobe16 (GetSigType ()); // stA1 = signature type of blinded A, 2 bytes big endian - uint16_t stA1 = htobe16 (GetBlindedSigType ()); + uint16_t stA1 = htobe16 (GetBlindedSigType ()); // credential = H("credential", A || stA || stA1) H ("credential", { {GetPublicKey (), GetPublicKeyLen ()}, {(const uint8_t *)&stA, 2}, {(const uint8_t *)&stA1, 2} }, credential); } @@ -224,15 +232,15 @@ namespace data { uint16_t stA = htobe16 (GetSigType ()), stA1 = htobe16 (GetBlindedSigType ()); uint8_t salt[32]; - //seed = HKDF(H("I2PGenerateAlpha", keydata), datestring || secret, "i2pblinding1", 64) + //seed = HKDF(H("I2PGenerateAlpha", keydata), datestring || secret, "i2pblinding1", 64) H ("I2PGenerateAlpha", { {GetPublicKey (), GetPublicKeyLen ()}, {(const uint8_t *)&stA, 2}, {(const uint8_t *)&stA1, 2} }, salt); i2p::crypto::HKDF (salt, (const uint8_t *)date, 8, "i2pblinding1", seed); } size_t BlindedPublicKey::GetBlindedKey (const char * date, uint8_t * blindedKey) const { - uint8_t seed[64]; - GenerateAlpha (date, seed); + uint8_t seed[64]; + GenerateAlpha (date, seed); size_t publicKeyLength = 0; switch (m_SigType) @@ -244,7 +252,7 @@ namespace data break; case i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: case i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: - i2p::crypto::GetEd25519 ()->BlindPublicKey (GetPublicKey (), seed, blindedKey); + i2p::crypto::GetEd25519 ()->BlindPublicKey (GetPublicKey (), seed, blindedKey); publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; break; default: @@ -255,8 +263,8 @@ namespace data size_t BlindedPublicKey::BlindPrivateKey (const uint8_t * priv, const char * date, uint8_t * blindedPriv, uint8_t * blindedPub) const { - uint8_t seed[64]; - GenerateAlpha (date, seed); + uint8_t seed[64]; + GenerateAlpha (date, seed); size_t publicKeyLength = 0; switch (m_SigType) { @@ -272,15 +280,15 @@ namespace data default: LogPrint (eLogError, "Blinding: can't blind signature type ", (int)m_SigType); } - return publicKeyLength; + return publicKeyLength; } - void BlindedPublicKey::H (const std::string& p, const std::vector >& bufs, uint8_t * hash) const + void BlindedPublicKey::H (const std::string& p, const std::vector >& bufs, uint8_t * hash) const { SHA256_CTX ctx; SHA256_Init (&ctx); SHA256_Update (&ctx, p.c_str (), p.length ()); - for (const auto& it: bufs) + for (const auto& it: bufs) SHA256_Update (&ctx, it.first, it.second); SHA256_Final (hash, &ctx); } @@ -289,15 +297,15 @@ namespace data { i2p::data::IdentHash hash; uint8_t blinded[128]; - size_t publicKeyLength = 0; + size_t publicKeyLength = 0; if (date) publicKeyLength = GetBlindedKey (date, blinded); else { char currentDate[9]; i2p::util::GetCurrentDate (currentDate); - publicKeyLength = GetBlindedKey (currentDate, blinded); - } + publicKeyLength = GetBlindedKey (currentDate, blinded); + } if (publicKeyLength) { auto stA1 = htobe16 (m_BlindedSigType); @@ -308,10 +316,9 @@ namespace data SHA256_Final ((uint8_t *)hash, &ctx); } else - LogPrint (eLogError, "Blinding: blinded key type ", (int)m_BlindedSigType, " is not supported"); + LogPrint (eLogError, "Blinding: blinded key type ", (int)m_BlindedSigType, " is not supported"); return hash; } } } - diff --git a/libi2pd/Blinding.h b/libi2pd/Blinding.h index 65b8c0f8..2f670882 100644 --- a/libi2pd/Blinding.h +++ b/libi2pd/Blinding.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef BLINDING_H__ #define BLINDING_H__ @@ -15,7 +23,7 @@ namespace data public: BlindedPublicKey (std::shared_ptr identity, bool clientAuth = false); - BlindedPublicKey (const std::string& b33); // from b33 without .b32.i2p + BlindedPublicKey (const std::string& b33); // from b33 without .b32.i2p std::string ToB33 () const; const uint8_t * GetPublicKey () const { return m_PublicKey.data (); }; @@ -25,14 +33,14 @@ namespace data bool IsValid () const { return GetSigType (); }; // signature type 0 means invalid void GetSubcredential (const uint8_t * blinded, size_t len, uint8_t * subcredential) const; // 32 bytes - size_t GetBlindedKey (const char * date, uint8_t * blindedKey) const; // date is 8 chars "YYYYMMDD", return public key length - size_t BlindPrivateKey (const uint8_t * priv, const char * date, uint8_t * blindedPriv, uint8_t * blindedPub) const; // date is 8 chars "YYYYMMDD", return public key length + size_t GetBlindedKey (const char * date, uint8_t * blindedKey) const; // date is 8 chars "YYYYMMDD", return public key length + size_t BlindPrivateKey (const uint8_t * priv, const char * date, uint8_t * blindedPriv, uint8_t * blindedPub) const; // date is 8 chars "YYYYMMDD", return public key length i2p::data::IdentHash GetStoreHash (const char * date = nullptr) const; // date is 8 chars "YYYYMMDD", use current if null private: void GetCredential (uint8_t * credential) const; // 32 bytes - void GenerateAlpha (const char * date, uint8_t * seed) const; // 64 bytes, date is 8 chars "YYYYMMDD" + void GenerateAlpha (const char * date, uint8_t * seed) const; // 64 bytes, date is 8 chars "YYYYMMDD" void H (const std::string& p, const std::vector >& bufs, uint8_t * hash) const; private: diff --git a/libi2pd/BloomFilter.cpp b/libi2pd/BloomFilter.cpp index b92039df..de077e60 100644 --- a/libi2pd/BloomFilter.cpp +++ b/libi2pd/BloomFilter.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include "BloomFilter.h" #include "I2PEndian.h" #include diff --git a/libi2pd/BloomFilter.h b/libi2pd/BloomFilter.h index 2745fbf5..ade854e4 100644 --- a/libi2pd/BloomFilter.h +++ b/libi2pd/BloomFilter.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef BLOOM_FILTER_H_ #define BLOOM_FILTER_H_ #include diff --git a/libi2pd/CPU.cpp b/libi2pd/CPU.cpp index a707c3dc..e7eff473 100644 --- a/libi2pd/CPU.cpp +++ b/libi2pd/CPU.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include "CPU.h" #if defined(__x86_64__) || defined(__i386__) #include diff --git a/libi2pd/CPU.h b/libi2pd/CPU.h index b4c19607..9677b293 100644 --- a/libi2pd/CPU.h +++ b/libi2pd/CPU.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef LIBI2PD_CPU_H #define LIBI2PD_CPU_H @@ -5,10 +13,10 @@ namespace i2p { namespace cpu { - extern bool aesni; - extern bool avx; + extern bool aesni; + extern bool avx; - void Detect(); + void Detect(); } } diff --git a/libi2pd/ChaCha20.cpp b/libi2pd/ChaCha20.cpp index 222111b7..66bc135f 100644 --- a/libi2pd/ChaCha20.cpp +++ b/libi2pd/ChaCha20.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2018, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -12,73 +12,72 @@ #include "I2PEndian.h" #include "ChaCha20.h" -#if !OPENSSL_AEAD_CHACHA20_POLY1305 +#if !OPENSSL_AEAD_CHACHA20_POLY1305 namespace i2p { namespace crypto { namespace chacha { -void u32t8le(uint32_t v, uint8_t * p) +void u32t8le(uint32_t v, uint8_t * p) { - p[0] = v & 0xff; - p[1] = (v >> 8) & 0xff; - p[2] = (v >> 16) & 0xff; - p[3] = (v >> 24) & 0xff; + p[0] = v & 0xff; + p[1] = (v >> 8) & 0xff; + p[2] = (v >> 16) & 0xff; + p[3] = (v >> 24) & 0xff; } -uint32_t u8t32le(const uint8_t * p) +uint32_t u8t32le(const uint8_t * p) { - uint32_t value = p[3]; + uint32_t value = p[3]; - value = (value << 8) | p[2]; - value = (value << 8) | p[1]; - value = (value << 8) | p[0]; + value = (value << 8) | p[2]; + value = (value << 8) | p[1]; + value = (value << 8) | p[0]; - return value; + return value; } -uint32_t rotl32(uint32_t x, int n) +uint32_t rotl32(uint32_t x, int n) { - return x << n | (x >> (-n & 31)); + return x << n | (x >> (-n & 31)); } -void quarterround(uint32_t *x, int a, int b, int c, int d) +void quarterround(uint32_t *x, int a, int b, int c, int d) { - x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 16); - x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 12); - x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 8); - x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 7); + x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 16); + x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 12); + x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 8); + x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 7); } void Chacha20Block::operator << (const Chacha20State & st) { int i; - for (i = 0; i < 16; i++) + for (i = 0; i < 16; i++) u32t8le(st.data[i], data + (i << 2)); } void block (Chacha20State &input, int rounds) { - int i; - Chacha20State x; - x.Copy(input); - - for (i = rounds; i > 0; i -= 2) - { - quarterround(x.data, 0, 4, 8, 12); - quarterround(x.data, 1, 5, 9, 13); - quarterround(x.data, 2, 6, 10, 14); - quarterround(x.data, 3, 7, 11, 15); - quarterround(x.data, 0, 5, 10, 15); - quarterround(x.data, 1, 6, 11, 12); - quarterround(x.data, 2, 7, 8, 13); - quarterround(x.data, 3, 4, 9, 14); - } - x += input; - input.block << x; + int i; + Chacha20State x; + x.Copy(input); + for (i = rounds; i > 0; i -= 2) + { + quarterround(x.data, 0, 4, 8, 12); + quarterround(x.data, 1, 5, 9, 13); + quarterround(x.data, 2, 6, 10, 14); + quarterround(x.data, 3, 7, 11, 15); + quarterround(x.data, 0, 5, 10, 15); + quarterround(x.data, 1, 6, 11, 12); + quarterround(x.data, 2, 7, 8, 13); + quarterround(x.data, 3, 4, 9, 14); + } + x += input; + input.block << x; } void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t * key, uint32_t counter) @@ -87,52 +86,52 @@ void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t * state.data[1] = 0x3320646e; state.data[2] = 0x79622d32; state.data[3] = 0x6b206574; - for (size_t i = 0; i < 8; i++) - state.data[4 + i] = chacha::u8t32le(key + i * 4); - + for (size_t i = 0; i < 8; i++) + state.data[4 + i] = chacha::u8t32le(key + i * 4); + state.data[12] = htole32 (counter); - for (size_t i = 0; i < 3; i++) - state.data[13 + i] = chacha::u8t32le(nonce + i * 4); + for (size_t i = 0; i < 3; i++) + state.data[13 + i] = chacha::u8t32le(nonce + i * 4); } void Chacha20SetCounter (Chacha20State& state, uint32_t counter) { state.data[12] = htole32 (counter); state.offset = 0; -} +} void Chacha20Encrypt (Chacha20State& state, uint8_t * buf, size_t sz) -{ +{ if (state.offset > 0) { - // previous block if any - auto s = chacha::blocksize - state.offset; + // previous block if any + auto s = chacha::blocksize - state.offset; if (sz < s) s = sz; for (size_t i = 0; i < s; i++) buf[i] ^= state.block.data[state.offset + i]; buf += s; sz -= s; state.offset += s; - if (state.offset >= chacha::blocksize) state.offset = 0; + if (state.offset >= chacha::blocksize) state.offset = 0; } - for (size_t i = 0; i < sz; i += chacha::blocksize) + for (size_t i = 0; i < sz; i += chacha::blocksize) { - chacha::block(state, chacha::rounds); - state.data[12]++; - for (size_t j = i; j < i + chacha::blocksize; j++) - { - if (j >= sz) + chacha::block(state, chacha::rounds); + state.data[12]++; + for (size_t j = i; j < i + chacha::blocksize; j++) + { + if (j >= sz) { state.offset = j & 0x3F; // % 64 break; } - buf[j] ^= state.block.data[j - i]; - } + buf[j] ^= state.block.data[j - i]; + } } } + } // namespace chacha +} // namespace crypto +} // namespace i2p -} -} #endif - diff --git a/libi2pd/ChaCha20.h b/libi2pd/ChaCha20.h index b2eec320..4364024b 100644 --- a/libi2pd/ChaCha20.h +++ b/libi2pd/ChaCha20.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2018, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -21,23 +21,23 @@ namespace i2p { namespace crypto { - const std::size_t CHACHA20_KEY_BYTES = 32; - const std::size_t CHACHA20_NOUNCE_BYTES = 12; - + const std::size_t CHACHA20_KEY_BYTES = 32; + const std::size_t CHACHA20_NOUNCE_BYTES = 12; + namespace chacha { - constexpr std::size_t blocksize = 64; + constexpr std::size_t blocksize = 64; constexpr int rounds = 20; struct Chacha20State; struct Chacha20Block { - Chacha20Block () {}; - Chacha20Block (Chacha20Block &&) = delete; + Chacha20Block () {}; + Chacha20Block (Chacha20Block &&) = delete; - uint8_t data[blocksize]; + uint8_t data[blocksize]; - void operator << (const Chacha20State & st); + void operator << (const Chacha20State & st); }; struct Chacha20State @@ -54,19 +54,19 @@ namespace chacha void Copy(const Chacha20State & other) { - memcpy(data, other.data, sizeof(uint32_t) * 16); + memcpy(data, other.data, sizeof(uint32_t) * 16); } uint32_t data[16]; Chacha20Block block; - size_t offset; + size_t offset; }; void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t * key, uint32_t counter); void Chacha20SetCounter (Chacha20State& state, uint32_t counter); - void Chacha20Encrypt (Chacha20State& state, uint8_t * buf, size_t sz); // encrypt buf in place -} -} -} -#endif + void Chacha20Encrypt (Chacha20State& state, uint8_t * buf, size_t sz); // encrypt buf in place +} // namespace chacha +} // namespace crypto +} // namespace i2p #endif +#endif diff --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp index 975a027e..d11152dd 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2017, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -35,11 +35,11 @@ namespace config { ("version", "Show i2pd version") ("conf", value()->default_value(""), "Path to main i2pd config file (default: try ~/.i2pd/i2pd.conf or /var/lib/i2pd/i2pd.conf)") ("tunconf", value()->default_value(""), "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.conf or /var/lib/i2pd/tunnels.conf)") - ("tunnelsdir", value()->default_value(""), "Path to extra tunnels' configs folder (default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d") + ("tunnelsdir", value()->default_value(""), "Path to extra tunnels' configs folder (default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d") ("pidfile", value()->default_value(""), "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)") ("log", value()->default_value(""), "Logs destination: stdout, file, syslog (stdout if not set)") ("logfile", value()->default_value(""), "Path to logfile (stdout if not set, autodetect if daemon)") - ("loglevel", value()->default_value("info"), "Set the minimal level of log messages (debug, info, warn, error, none)") + ("loglevel", value()->default_value("warn"), "Set the minimal level of log messages (debug, info, warn, error, none)") ("logclftime", bool_switch()->default_value(false), "Write full CLF-formatted date and time to log (default: disabled, write only time)") ("family", value()->default_value(""), "Specify a family, router belongs to") ("datadir", value()->default_value(""), "Path to storage of i2pd data (RI, keys, peer profiles, ...)") @@ -58,9 +58,9 @@ namespace config { ("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)") ("bandwidth", value()->default_value(""), "Bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)") ("share", value()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)") - ("ntcp", value()->default_value(false), "Enable NTCP transport (default: disabled)") + ("ntcp", value()->default_value(false), "Enable NTCP transport (default: disabled)") ("ssu", value()->default_value(true), "Enable SSU transport (default: enabled)") - ("ntcpproxy", value()->default_value(""), "Proxy URL for NTCP transport") + ("ntcpproxy", value()->default_value(""), "Proxy URL for NTCP transport") #ifdef _WIN32 ("svcctl", value()->default_value(""), "Windows service management ('install' or 'remove')") ("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)") @@ -88,7 +88,7 @@ namespace config { ("http.pass", value()->default_value(""), "Password for basic auth (default: random, see logs)") ("http.strictheaders", value()->default_value(true), "Enable strict host checking on WebUI") ("http.hostname", value()->default_value("localhost"), "Expected hostname for WebUI") - ("http.webroot", value()->default_value("/"), "WebUI root path (default: / )") + ("http.webroot", value()->default_value("/"), "WebUI root path (default: / )") ; options_description httpproxy("HTTP Proxy options"); @@ -97,7 +97,8 @@ namespace config { ("httpproxy.address", value()->default_value("127.0.0.1"), "HTTP Proxy listen address") ("httpproxy.port", value()->default_value(4444), "HTTP Proxy listen port") ("httpproxy.keys", value()->default_value(""), "File to persist HTTP Proxy keys") - ("httpproxy.signaturetype", value()->default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default") + ("httpproxy.signaturetype", value()-> + default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default") ("httpproxy.inbound.length", value()->default_value("3"), "HTTP proxy inbound tunnel length") ("httpproxy.outbound.length", value()->default_value("3"), "HTTP proxy outbound tunnel length") ("httpproxy.inbound.quantity", value()->default_value("5"), "HTTP proxy inbound tunnels quantity") @@ -106,6 +107,8 @@ namespace config { ("httpproxy.latency.max", value()->default_value("0"), "HTTP proxy max latency for tunnels") ("httpproxy.outproxy", value()->default_value(""), "HTTP proxy upstream out proxy url") ("httpproxy.addresshelper", value()->default_value(true), "Enable or disable addresshelper") + ("httpproxy.i2cp.leaseSetType", value()->default_value("1"), "Local destination's LeaseSet type") + ("httpproxy.i2cp.leaseSetEncType", value()->default_value("0"), "Local destination's LeaseSet encryption type") ; options_description socksproxy("SOCKS Proxy options"); @@ -114,7 +117,8 @@ namespace config { ("socksproxy.address", value()->default_value("127.0.0.1"), "SOCKS Proxy listen address") ("socksproxy.port", value()->default_value(4447), "SOCKS Proxy listen port") ("socksproxy.keys", value()->default_value(""), "File to persist SOCKS Proxy keys") - ("socksproxy.signaturetype", value()->default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default") + ("socksproxy.signaturetype", value()-> + default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default") ("socksproxy.inbound.length", value()->default_value("3"), "SOCKS proxy inbound tunnel length") ("socksproxy.outbound.length", value()->default_value("3"), "SOCKS proxy outbound tunnel length") ("socksproxy.inbound.quantity", value()->default_value("5"), "SOCKS proxy inbound tunnels quantity") @@ -124,6 +128,8 @@ namespace config { ("socksproxy.outproxy.enabled", value()->default_value(false), "Enable or disable SOCKS outproxy") ("socksproxy.outproxy", value()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy") ("socksproxy.outproxyport", value()->default_value(9050), "Upstream outproxy port for SOCKS Proxy") + ("socksproxy.i2cp.leaseSetType", value()->default_value("1"), "Local destination's LeaseSet type") + ("socksproxy.i2cp.leaseSetEncType", value()->default_value("0"), "Local destination's LeaseSet encryption type") ; options_description sam("SAM bridge options"); @@ -131,7 +137,7 @@ namespace config { ("sam.enabled", value()->default_value(true), "Enable or disable SAM Application bridge") ("sam.address", value()->default_value("127.0.0.1"), "SAM listen address") ("sam.port", value()->default_value(7656), "SAM listen port") - ("sam.singlethread", value()->default_value(true), "Sessions run in the SAM bridge's thread") + ("sam.singlethread", value()->default_value(true), "Sessions run in the SAM bridge's thread") ; options_description bob("BOB options"); @@ -190,16 +196,13 @@ namespace config { ("reseed.urls", value()->default_value( "https://reseed.i2p-projekt.de/," "https://i2p.mooo.com/netDb/," - "https://netdb.i2p2.no/," - "https://reseed.i2p2.no/," - "https://reseed2.i2p2.no/," - // "https://us.reseed.i2p2.no:444/," // mamoth's shit - // "https://uk.reseed.i2p2.no:444/," // mamoth's shit + "https://reseed.i2p2.no/," "https://reseed-fr.i2pd.xyz/," "https://reseed.memcpy.io/," "https://reseed.onion.im/," "https://i2pseed.creativecowpat.net:8443/," - "https://i2p.novg.net/" + "https://reseed.i2pgit.org/," + "https://i2p.novg.net/" ), "Reseed URLs, separated by comma") ; @@ -218,6 +221,14 @@ namespace config { ("trust.hidden", value()->default_value(false), "Should we hide our router from other routers?") ; + // Save deprecated websocket options for compatibility + options_description websocket("Websocket Options"); + websocket.add_options() + ("websockets.enabled", value()->default_value(false), "Deprecated option") + ("websockets.address", value()->default_value(""), "Deprecated option") + ("websockets.port", value()->default_value(0), "Deprecated option") + ; + options_description exploratory("Exploratory Options"); exploratory.add_options() ("exploratory.inbound.length", value()->default_value(2), "Exploratory inbound tunnel length") @@ -228,29 +239,29 @@ namespace config { options_description ntcp2("NTCP2 Options"); ntcp2.add_options() - ("ntcp2.enabled", value()->default_value(true), "Enable NTCP2 (default: enabled)") - ("ntcp2.published", value()->default_value(true), "Publish NTCP2 (default: enabled)") - ("ntcp2.port", value()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)") + ("ntcp2.enabled", value()->default_value(true), "Enable NTCP2 (default: enabled)") + ("ntcp2.published", value()->default_value(true), "Publish NTCP2 (default: enabled)") + ("ntcp2.port", value()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)") ("ntcp2.addressv6", value()->default_value("::"), "Address to bind NTCP2 on") - ("ntcp2.proxy", value()->default_value(""), "Proxy URL for NTCP2 transport") + ("ntcp2.proxy", value()->default_value(""), "Proxy URL for NTCP2 transport") ; options_description nettime("Time sync options"); nettime.add_options() - ("nettime.enabled", value()->default_value(false), "Disable time sync (default: disabled)") + ("nettime.enabled", value()->default_value(false), "Disable time sync (default: disabled)") ("nettime.ntpservers", value()->default_value( "0.pool.ntp.org," "1.pool.ntp.org," "2.pool.ntp.org," "3.pool.ntp.org" - ), "Comma separated list of NTCP servers") - ("nettime.ntpsyncinterval", value()->default_value(72), "NTP sync interval in hours (default: 72)") + ), "Comma separated list of NTCP servers") + ("nettime.ntpsyncinterval", value()->default_value(72), "NTP sync interval in hours (default: 72)") ; options_description persist("Network information persisting options"); persist.add_options() - ("persist.profiles", value()->default_value(true), "Persist peer profiles (default: true)") - ("persist.addressbook", value()->default_value(true), "Persist full addresses (default: true)") + ("persist.profiles", value()->default_value(true), "Persist peer profiles (default: true)") + ("persist.addressbook", value()->default_value(true), "Persist full addresses (default: true)") ; m_OptionsDesc @@ -268,6 +279,7 @@ namespace config { .add(reseed) .add(addressbook) .add(trust) + .add(websocket) // deprecated .add(exploratory) .add(ntcp2) .add(nettime) diff --git a/libi2pd/Config.h b/libi2pd/Config.h index 679ae3bb..dac5fc80 100644 --- a/libi2pd/Config.h +++ b/libi2pd/Config.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef CONFIG_H #define CONFIG_H @@ -18,98 +26,101 @@ namespace i2p { namespace config { - extern boost::program_options::variables_map m_Options; + extern boost::program_options::variables_map m_Options; - /** - * @brief Initialize list of acceptable parameters - * - * Should be called before any Parse* functions. - */ - void Init(); + /** + * @brief Initialize list of acceptable parameters + * + * Should be called before any Parse* functions. + */ + void Init(); - /** - * @brief Parse cmdline parameters, and show help if requested - * @param argc Cmdline arguments count, should be passed from main(). - * @param argv Cmdline parameters array, should be passed from main() - * - * If --help is given in parameters, shows its list with description - * and terminates the program with exitcode 0. - * - * In case of parameter misuse boost throws an exception. - * We internally handle type boost::program_options::unknown_option, - * and then terminate the program with exitcode 1. - * - * Other exceptions will be passed to higher level. - */ - void ParseCmdline(int argc, char* argv[], bool ignoreUnknown = false); + /** + * @brief Parse cmdline parameters, and show help if requested + * @param argc Cmdline arguments count, should be passed from main(). + * @param argv Cmdline parameters array, should be passed from main() + * + * If --help is given in parameters, shows its list with description + * and terminates the program with exitcode 0. + * + * In case of parameter misuse boost throws an exception. + * We internally handle type boost::program_options::unknown_option, + * and then terminate the program with exitcode 1. + * + * Other exceptions will be passed to higher level. + */ + void ParseCmdline(int argc, char* argv[], bool ignoreUnknown = false); - /** - * @brief Load and parse given config file - * @param path Path to config file - * - * If error occurred when opening file path is points to, - * we show the error message and terminate program. - * - * In case of parameter misuse boost throws an exception. - * We internally handle type boost::program_options::unknown_option, - * and then terminate program with exitcode 1. - * - * Other exceptions will be passed to higher level. - */ - void ParseConfig(const std::string& path); + /** + * @brief Load and parse given config file + * @param path Path to config file + * + * If error occurred when opening file path is points to, + * we show the error message and terminate program. + * + * In case of parameter misuse boost throws an exception. + * We internally handle type boost::program_options::unknown_option, + * and then terminate program with exitcode 1. + * + * Other exceptions will be passed to higher level. + */ + void ParseConfig(const std::string& path); - /** - * @brief Used to combine options from cmdline, config and default values - */ - void Finalize(); + /** + * @brief Used to combine options from cmdline, config and default values + */ + void Finalize(); - /* @brief Accessor to parameters by name - * @param name Name of the requested parameter - * @param value Variable where to store option - * @return this function returns false if parameter not found - * - * Example: uint16_t port; GetOption("sam.port", port); - */ - template - bool GetOption(const char *name, T& value) { - if (!m_Options.count(name)) - return false; - value = m_Options[name].as(); - return true; - } + /** + * @brief Accessor to parameters by name + * @param name Name of the requested parameter + * @param value Variable where to store option + * @return this function returns false if parameter not found + * + * Example: uint16_t port; GetOption("sam.port", port); + */ + template + bool GetOption(const char *name, T& value) + { + if (!m_Options.count(name)) + return false; + value = m_Options[name].as(); + return true; + } - template - bool GetOption(const std::string& name, T& value) - { - return GetOption (name.c_str (), value); - } + template + bool GetOption(const std::string& name, T& value) + { + return GetOption (name.c_str (), value); + } - bool GetOptionAsAny(const char *name, boost::any& value); - bool GetOptionAsAny(const std::string& name, boost::any& value); + bool GetOptionAsAny(const char *name, boost::any& value); + bool GetOptionAsAny(const std::string& name, boost::any& value); - /** - * @brief Set value of given parameter - * @param name Name of settable parameter - * @param value New parameter value - * @return true if value set up successful, false otherwise - * - * Example: uint16_t port = 2827; SetOption("bob.port", port); - */ - template - bool SetOption(const char *name, const T& value) { - if (!m_Options.count(name)) - return false; - m_Options.at(name).value() = value; - notify(m_Options); - return true; - } + /** + * @brief Set value of given parameter + * @param name Name of settable parameter + * @param value New parameter value + * @return true if value set up successful, false otherwise + * + * Example: uint16_t port = 2827; SetOption("bob.port", port); + */ + template + bool SetOption(const char *name, const T& value) + { + if (!m_Options.count(name)) + return false; + m_Options.at(name).value() = value; + notify(m_Options); + return true; + } - /** - * @brief Check is value explicitly given or default - * @param name Name of checked parameter - * @return true if value set to default, false otherwise - */ - bool IsDefault(const char *name); + /** + * @brief Check is value explicitly given or default + * @param name Name of checked parameter + * @return true if value set to default, false otherwise + */ + bool IsDefault(const char *name); } } diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index 567ae574..6db124c0 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include @@ -286,14 +294,14 @@ namespace crypto #if OPENSSL_X25519 m_Ctx = EVP_PKEY_CTX_new_id (NID_X25519, NULL); m_Pkey = nullptr; -#else +#else m_Ctx = BN_CTX_new (); -#endif - } +#endif + } X25519Keys::X25519Keys (const uint8_t * priv, const uint8_t * pub) { -#if OPENSSL_X25519 +#if OPENSSL_X25519 m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32); m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); if (pub) @@ -302,33 +310,33 @@ namespace crypto { size_t len = 32; EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len); - } + } #else - m_Ctx = BN_CTX_new (); + m_Ctx = BN_CTX_new (); memcpy (m_PrivateKey, priv, 32); if (pub) memcpy (m_PublicKey, pub, 32); else GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx); -#endif - } - +#endif + } + X25519Keys::~X25519Keys () { #if OPENSSL_X25519 EVP_PKEY_CTX_free (m_Ctx); - if (m_Pkey) EVP_PKEY_free (m_Pkey); -#else + if (m_Pkey) EVP_PKEY_free (m_Pkey); +#else BN_CTX_free (m_Ctx); -#endif - } +#endif + } void X25519Keys::GenerateKeys () { #if OPENSSL_X25519 if (m_Pkey) - { - EVP_PKEY_free (m_Pkey); + { + EVP_PKEY_free (m_Pkey); m_Pkey = nullptr; } EVP_PKEY_keygen_init (m_Ctx); @@ -337,26 +345,26 @@ namespace crypto m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); // TODO: do we really need to re-create m_Ctx? size_t len = 32; EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len); -#else +#else RAND_bytes (m_PrivateKey, 32); GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx); -#endif - } +#endif + } void X25519Keys::Agree (const uint8_t * pub, uint8_t * shared) { -#if OPENSSL_X25519 +#if OPENSSL_X25519 EVP_PKEY_derive_init (m_Ctx); auto pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_X25519, NULL, pub, 32); EVP_PKEY_derive_set_peer (m_Ctx, pkey); size_t len = 32; EVP_PKEY_derive (m_Ctx, shared, &len); EVP_PKEY_free (pkey); -#else +#else GetEd25519 ()->ScalarMul (pub, m_PrivateKey, shared, m_Ctx); -#endif - } - +#endif + } + void X25519Keys::GetPrivateKey (uint8_t * priv) const { #if OPENSSL_X25519 @@ -369,14 +377,14 @@ namespace crypto void X25519Keys::SetPrivateKey (const uint8_t * priv) { -#if OPENSSL_X25519 - if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx); +#if OPENSSL_X25519 + if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx); if (m_Pkey) EVP_PKEY_free (m_Pkey); m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32); m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); #else memcpy (m_PrivateKey, priv, 32); -#endif +#endif } // ElGamal @@ -681,12 +689,12 @@ namespace crypto // AES #ifdef __AES__ - #ifdef ARM64AES - void init_aesenc(void){ + #ifdef ARM64AES + void init_aesenc(void){ // TODO: Implementation - } + } - #endif + #endif #define KeyExpansion256(round0,round1) \ "pshufd $0xff, %%xmm2, %%xmm2 \n" \ @@ -884,7 +892,6 @@ namespace crypto } } - void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) { #ifdef __AES__ @@ -1139,10 +1146,10 @@ namespace crypto } EVP_CIPHER_CTX_free (ctx); -#else +#else chacha::Chacha20State state; // generate one time poly key - chacha::Chacha20Init (state, nonce, key, 0); + chacha::Chacha20Init (state, nonce, key, 0); uint64_t polyKey[8]; memset(polyKey, 0, sizeof(polyKey)); chacha::Chacha20Encrypt (state, (uint8_t *)polyKey, 64); @@ -1158,7 +1165,7 @@ namespace crypto { // padding1 rem = 16 - rem; - polyHash.Update (padding, rem); + polyHash.Update (padding, rem); } } // encrypt/decrypt data and add to hash @@ -1174,20 +1181,20 @@ namespace crypto { polyHash.Update (buf, msgLen); // before decryption chacha::Chacha20Encrypt (state, buf, msgLen); // decrypt - } + } auto rem = msgLen & 0x0F; // %16 if (rem) { // padding2 rem = 16 - rem; - polyHash.Update (padding, rem); + polyHash.Update (padding, rem); } // adLen and msgLen htole64buf (padding, adLen); htole64buf (padding + 8, msgLen); - polyHash.Update (padding, 16); - + polyHash.Update (padding, 16); + if (encrypt) // calculate Poly1305 tag and write in after encrypted data polyHash.Finish ((uint64_t *)(buf + msgLen)); @@ -1195,7 +1202,7 @@ namespace crypto { uint64_t tag[4]; // calculate Poly1305 tag - polyHash.Finish (tag); + polyHash.Finish (tag); if (memcmp (tag, msg + msgLen, 16)) ret = false; // compare with provided } #endif @@ -1211,20 +1218,20 @@ namespace crypto EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0); EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0); EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce); - for (const auto& it: bufs) + for (const auto& it: bufs) EVP_EncryptUpdate(ctx, it.first, &outlen, it.first, it.second); EVP_EncryptFinal_ex(ctx, NULL, &outlen); EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, mac); EVP_CIPHER_CTX_free (ctx); -#else +#else chacha::Chacha20State state; // generate one time poly key - chacha::Chacha20Init (state, nonce, key, 0); + chacha::Chacha20Init (state, nonce, key, 0); uint64_t polyKey[8]; memset(polyKey, 0, sizeof(polyKey)); chacha::Chacha20Encrypt (state, (uint8_t *)polyKey, 64); Poly1305 polyHash (polyKey); - // encrypt buffers + // encrypt buffers Chacha20SetCounter (state, 1); size_t size = 0; for (const auto& it: bufs) @@ -1234,22 +1241,22 @@ namespace crypto size += it.second; } // padding - uint8_t padding[16]; + uint8_t padding[16]; memset (padding, 0, 16); auto rem = size & 0x0F; // %16 if (rem) { // padding2 rem = 16 - rem; - polyHash.Update (padding, rem); + polyHash.Update (padding, rem); } // adLen and msgLen // adLen is always zero htole64buf (padding + 8, size); - polyHash.Update (padding, 16); + polyHash.Update (padding, 16); // MAC - polyHash.Finish ((uint64_t *)mac); -#endif + polyHash.Finish ((uint64_t *)mac); +#endif } void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out) @@ -1265,13 +1272,13 @@ namespace crypto EVP_CIPHER_CTX_free (ctx); #else chacha::Chacha20State state; - chacha::Chacha20Init (state, nonce, key, 1); + chacha::Chacha20Init (state, nonce, key, 1); if (out != msg) memcpy (out, msg, msgLen); chacha::Chacha20Encrypt (state, out, msgLen); #endif } - void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, + void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out, size_t outLen) { #if OPENSSL_HKDF @@ -1279,10 +1286,10 @@ namespace crypto EVP_PKEY_derive_init (pctx); EVP_PKEY_CTX_set_hkdf_md (pctx, EVP_sha256()); if (key && keyLen) - { + { EVP_PKEY_CTX_set1_hkdf_salt (pctx, salt, 32); EVP_PKEY_CTX_set1_hkdf_key (pctx, key, keyLen); - } + } else { // zerolen @@ -1290,22 +1297,22 @@ namespace crypto uint8_t tempKey[32]; unsigned int len; HMAC(EVP_sha256(), salt, 32, nullptr, 0, tempKey, &len); EVP_PKEY_CTX_set1_hkdf_key (pctx, tempKey, len); - } + } if (info.length () > 0) EVP_PKEY_CTX_add1_hkdf_info (pctx, info.c_str (), info.length ()); EVP_PKEY_derive (pctx, out, &outLen); EVP_PKEY_CTX_free (pctx); #else uint8_t prk[32]; unsigned int len; - HMAC(EVP_sha256(), salt, 32, key, keyLen, prk, &len); + HMAC(EVP_sha256(), salt, 32, key, keyLen, prk, &len); auto l = info.length (); memcpy (out, info.c_str (), l); out[l] = 0x01; HMAC(EVP_sha256(), prk, 32, out, l + 1, out, &len); if (outLen > 32) // 64 - { + { memcpy (out + 32, info.c_str (), l); out[l + 32] = 0x02; - HMAC(EVP_sha256(), prk, 32, out, l + 33, out + 32, &len); - } + HMAC(EVP_sha256(), prk, 32, out, l + 33, out + 32, &len); + } #endif } @@ -1323,10 +1330,10 @@ namespace crypto } }*/ - + void InitCrypto (bool precomputation) { - i2p::cpu::Detect (); + i2p::cpu::Detect (); #if LEGACY_OPENSSL SSL_library_init (); #endif @@ -1364,4 +1371,3 @@ namespace crypto } } } - diff --git a/libi2pd/Crypto.h b/libi2pd/Crypto.h index 32410daf..56c8c10b 100644 --- a/libi2pd/Crypto.h +++ b/libi2pd/Crypto.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef CRYPTO_H__ #define CRYPTO_H__ @@ -28,13 +36,13 @@ #else # define LEGACY_OPENSSL 0 # if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1 -# define OPENSSL_HKDF 1 -# define OPENSSL_EDDSA 1 -# define OPENSSL_X25519 1 -# define OPENSSL_SIPHASH 1 +# define OPENSSL_HKDF 1 +# define OPENSSL_EDDSA 1 +# define OPENSSL_X25519 1 +# define OPENSSL_SIPHASH 1 # endif # if !defined OPENSSL_NO_CHACHA && !defined OPENSSL_NO_POLY1305 // some builds might not include them -# define OPENSSL_AEAD_CHACHA20_POLY1305 1 +# define OPENSSL_AEAD_CHACHA20_POLY1305 1 # endif #endif @@ -81,20 +89,20 @@ namespace crypto const uint8_t * GetPublicKey () const { return m_PublicKey; }; void GetPrivateKey (uint8_t * priv) const; void SetPrivateKey (const uint8_t * priv); // wihout calculating public - void Agree (const uint8_t * pub, uint8_t * shared); + void Agree (const uint8_t * pub, uint8_t * shared); private: - uint8_t m_PublicKey[32]; + uint8_t m_PublicKey[32]; #if OPENSSL_X25519 EVP_PKEY_CTX * m_Ctx; EVP_PKEY * m_Pkey; -#else +#else BN_CTX * m_Ctx; uint8_t m_PrivateKey[32]; -#endif +#endif }; - + // ElGamal void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding = false); bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding = false); @@ -117,15 +125,15 @@ namespace crypto void operator^=(const ChipherBlock& other) // XOR { if (!(((size_t)buf | (size_t)other.buf) & 0x03)) // multiple of 4 ? - { + { for (int i = 0; i < 4; i++) reinterpret_cast(buf)[i] ^= reinterpret_cast(other.buf)[i]; - } + } else - { + { for (int i = 0; i < 16; i++) buf[i] ^= other.buf[i]; - } + } } }; @@ -297,7 +305,7 @@ namespace crypto // HKDF - void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out, size_t outLen = 64); // salt - 32, out - 32 or 64, info <= 32 + void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out, size_t outLen = 64); // salt - 32, out - 32 or 64, info <= 32 // init and terminate void InitCrypto (bool precomputation); diff --git a/libi2pd/CryptoKey.cpp b/libi2pd/CryptoKey.cpp index 878e984a..d786e193 100644 --- a/libi2pd/CryptoKey.cpp +++ b/libi2pd/CryptoKey.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include "Log.h" #include "Gost.h" @@ -151,7 +159,7 @@ namespace crypto ECIESX25519AEADRatchetEncryptor::ECIESX25519AEADRatchetEncryptor (const uint8_t * pub) { memcpy (m_PublicKey, pub, 32); - } + } void ECIESX25519AEADRatchetEncryptor::Encrypt (const uint8_t *, uint8_t * pub, BN_CTX *, bool) { @@ -166,16 +174,15 @@ namespace crypto bool ECIESX25519AEADRatchetDecryptor::Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding) { m_StaticKeys.Agree (epub, sharedSecret); - return true; + return true; } void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub) { X25519Keys k; - k.GenerateKeys (); + k.GenerateKeys (); k.GetPrivateKey (priv); memcpy (pub, k.GetPublicKey (), 32); } } } - diff --git a/libi2pd/CryptoKey.h b/libi2pd/CryptoKey.h index 701e9482..1c8b0a71 100644 --- a/libi2pd/CryptoKey.h +++ b/libi2pd/CryptoKey.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef CRYPTO_KEY_H__ #define CRYPTO_KEY_H__ @@ -45,7 +53,7 @@ namespace crypto ElGamalDecryptor (const uint8_t * priv); bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding); size_t GetPublicKeyLen () const { return 256; }; - + private: uint8_t m_PrivateKey[256]; @@ -76,7 +84,7 @@ namespace crypto ~ECIESP256Decryptor (); bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding); size_t GetPublicKeyLen () const { return 64; }; - + private: EC_GROUP * m_Curve; @@ -109,7 +117,7 @@ namespace crypto ~ECIESGOSTR3410Decryptor (); bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding); size_t GetPublicKeyLen () const { return 64; }; - + private: BIGNUM * m_PrivateKey; @@ -119,14 +127,14 @@ namespace crypto // ECIES-X25519-AEAD-Ratchet - class ECIESX25519AEADRatchetEncryptor: public CryptoKeyEncryptor + class ECIESX25519AEADRatchetEncryptor: public CryptoKeyEncryptor { public: ECIESX25519AEADRatchetEncryptor (const uint8_t * pub); ~ECIESX25519AEADRatchetEncryptor () {}; void Encrypt (const uint8_t *, uint8_t * pub, BN_CTX *, bool); - // copies m_PublicKey to pub + // copies m_PublicKey to pub private: @@ -139,7 +147,7 @@ namespace crypto ECIESX25519AEADRatchetDecryptor (const uint8_t * priv); ~ECIESX25519AEADRatchetDecryptor () {}; - bool Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding); + bool Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding); // agree with static and return in sharedSecret (32 bytes) size_t GetPublicKeyLen () const { return 32; }; @@ -153,4 +161,3 @@ namespace crypto } #endif - diff --git a/libi2pd/CryptoWorker.h b/libi2pd/CryptoWorker.h index d43e356c..27b012e7 100644 --- a/libi2pd/CryptoWorker.h +++ b/libi2pd/CryptoWorker.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef CRYPTO_WORKER_H_ #define CRYPTO_WORKER_H_ @@ -77,5 +85,4 @@ namespace worker } } - #endif diff --git a/libi2pd/Datagram.cpp b/libi2pd/Datagram.cpp index 7e04fce8..04792bc5 100644 --- a/libi2pd/Datagram.cpp +++ b/libi2pd/Datagram.cpp @@ -1,5 +1,12 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include -#include #include "Crypto.h" #include "Log.h" #include "TunnelBase.h" @@ -11,9 +18,13 @@ namespace i2p { namespace datagram { - DatagramDestination::DatagramDestination (std::shared_ptr owner): - m_Owner (owner), m_Receiver (nullptr), m_RawReceiver (nullptr) + DatagramDestination::DatagramDestination (std::shared_ptr owner, bool gzip): + m_Owner (owner), m_Receiver (nullptr), m_RawReceiver (nullptr), m_Gzip (gzip) { + auto identityLen = m_Owner->GetIdentity ()->GetFullLen (); + m_From.resize (identityLen); + m_Owner->GetIdentity ()->ToBuffer (m_From.data (), identityLen); + m_Signature.resize (m_Owner->GetIdentity ()->GetSignatureLen ()); } DatagramDestination::~DatagramDestination () @@ -23,38 +34,28 @@ namespace datagram void DatagramDestination::SendDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort) { - auto owner = m_Owner; - std::vector v(MAX_DATAGRAM_SIZE); - uint8_t * buf = v.data(); - auto localIdentity = m_Owner->GetIdentity (); - auto identityLen = localIdentity->ToBuffer (buf, MAX_DATAGRAM_SIZE); - uint8_t * signature = buf + identityLen; - auto signatureLen = localIdentity->GetSignatureLen (); - uint8_t * buf1 = signature + signatureLen; - size_t headerLen = identityLen + signatureLen; - - memcpy (buf1, payload, len); - if (localIdentity->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) + if (m_Owner->GetIdentity ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) { uint8_t hash[32]; - SHA256(buf1, len, hash); - owner->Sign (hash, 32, signature); + SHA256(payload, len, hash); + m_Owner->Sign (hash, 32, m_Signature.data ()); } else - owner->Sign (buf1, len, signature); + m_Owner->Sign (payload, len, m_Signature.data ()); - auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort); auto session = ObtainSession(identity); + auto msg = CreateDataMessage ({{m_From.data (), m_From.size ()}, {m_Signature.data (), m_Signature.size ()}, {payload, len}}, + fromPort, toPort, false, !session->IsRatchets ()); // datagram session->SendMsg(msg); } void DatagramDestination::SendRawDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort) { - auto msg = CreateDataMessage (payload, len, fromPort, toPort, true); // raw auto session = ObtainSession(identity); + auto msg = CreateDataMessage ({{payload, len}}, fromPort, toPort, true, !session->IsRatchets ()); // raw session->SendMsg(msg); - } - + } + void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort,uint8_t * const &buf, size_t len) { i2p::data::IdentityEx identity; @@ -93,8 +94,8 @@ namespace datagram m_RawReceiver (fromPort, toPort, buf, len); else LogPrint (eLogWarning, "DatagramDestination: no receiver for raw datagram"); - } - + } + DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port) { std::lock_guard lock(m_ReceiversMutex); @@ -114,20 +115,22 @@ namespace datagram { if (isRaw) HandleRawDatagram (fromPort, toPort, uncompressed, uncompressedLen); - else + else HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen); - } + } else LogPrint (eLogWarning, "Datagram: decompression failed"); } - - std::shared_ptr DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort, bool isRaw) + std::shared_ptr DatagramDestination::CreateDataMessage ( + const std::vector >& payloads, + uint16_t fromPort, uint16_t toPort, bool isRaw, bool checksum) { auto msg = NewI2NPMessage (); uint8_t * buf = msg->GetPayload (); buf += 4; // reserve for length - size_t size = m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len); + size_t size = m_Gzip ? m_Deflator.Deflate (payloads, buf, msg->maxLen - msg->len) : + i2p::data::GzipNoCompression (payloads, buf, msg->maxLen - msg->len); if (size) { htobe32buf (msg->GetPayload (), size); // length @@ -135,7 +138,7 @@ namespace datagram htobe16buf (buf + 6, toPort); // destination port buf[9] = isRaw ? i2p::client::PROTOCOL_TYPE_RAW : i2p::client::PROTOCOL_TYPE_DATAGRAM; // raw or datagram protocol msg->len += size + 4; - msg->FillI2NPMessageHeader (eI2NPData); + msg->FillI2NPMessageHeader (eI2NPData, 0, checksum); } else msg = nullptr; @@ -190,7 +193,7 @@ namespace datagram } DatagramSession::DatagramSession(std::shared_ptr localDestination, - const i2p::data::IdentHash & remoteIdent) : + const i2p::data::IdentHash & remoteIdent) : m_LocalDestination(localDestination), m_RemoteIdent(remoteIdent), m_SendQueueTimer(localDestination->GetService()), @@ -247,11 +250,14 @@ namespace datagram auto path = GetSharedRoutingPath(); if(path) path->updateTime = i2p::util::GetSecondsSinceEpoch (); + if (IsRatchets ()) + SendMsg (nullptr); // send empty message in case if we have some data to send } std::shared_ptr DatagramSession::GetSharedRoutingPath () { - if(!m_RoutingSession) { + if (!m_RoutingSession || !m_RoutingSession->GetOwner ()) + { if(!m_RemoteLeaseSet) { m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent); } @@ -352,14 +358,14 @@ namespace datagram void DatagramSession::HandleSend(std::shared_ptr msg) { - m_SendQueue.push_back(msg); + if (msg || m_SendQueue.empty ()) + m_SendQueue.push_back(msg); // flush queue right away if full if(m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE) FlushSendQueue(); } void DatagramSession::FlushSendQueue () { - std::vector send; auto routingPath = GetSharedRoutingPath(); // if we don't have a routing path we will drop all queued messages @@ -368,7 +374,8 @@ namespace datagram for (const auto & msg : m_SendQueue) { auto m = m_RoutingSession->WrapSingleMessage(msg); - send.push_back(i2p::tunnel::TunnelMessageBlock{i2p::tunnel::eDeliveryTypeTunnel,routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID, m}); + if (m) + send.push_back(i2p::tunnel::TunnelMessageBlock{i2p::tunnel::eDeliveryTypeTunnel,routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID, m}); } routingPath->outboundTunnel->SendTunnelDataMsg(send); } @@ -385,4 +392,3 @@ namespace datagram } } } - diff --git a/libi2pd/Datagram.h b/libi2pd/Datagram.h index 0cfec838..e81f738a 100644 --- a/libi2pd/Datagram.h +++ b/libi2pd/Datagram.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef DATAGRAM_H__ #define DATAGRAM_H__ @@ -5,6 +13,7 @@ #include #include #include +#include #include "Base.h" #include "Identity.h" #include "LeaseSet.h" @@ -31,25 +40,29 @@ namespace datagram const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE = 1000; // milliseconds minimum time between path switches const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000; - // max 64 messages buffered in send queue for each datagram session - const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64; + // max 64 messages buffered in send queue for each datagram session + const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64; class DatagramSession : public std::enable_shared_from_this { - public: - DatagramSession(std::shared_ptr localDestination, const i2p::data::IdentHash & remoteIdent); - void Start (); - void Stop (); + public: + + DatagramSession(std::shared_ptr localDestination, const i2p::data::IdentHash & remoteIdent); + + void Start (); + void Stop (); - /** @brief ack the garlic routing path */ - void Ack(); + /** @brief ack the garlic routing path */ + void Ack(); - /** send an i2np message to remote endpoint for this session */ - void SendMsg(std::shared_ptr msg); - /** get the last time in milliseconds for when we used this datagram session */ - uint64_t LastActivity() const { return m_LastUse; } + /** send an i2np message to remote endpoint for this session */ + void SendMsg(std::shared_ptr msg); + /** get the last time in milliseconds for when we used this datagram session */ + uint64_t LastActivity() const { return m_LastUse; } + + bool IsRatchets () const { return m_RoutingSession && m_RoutingSession->IsRatchets (); } struct Info { @@ -57,40 +70,41 @@ namespace datagram std::shared_ptr OBEP; const uint64_t activity; - Info() : IBGW(nullptr), OBEP(nullptr), activity(0) {} - Info(const uint8_t * ibgw, const uint8_t * obep, const uint64_t a) : - activity(a) { - if(ibgw) IBGW = std::make_shared(ibgw); - else IBGW = nullptr; - if(obep) OBEP = std::make_shared(obep); - else OBEP = nullptr; - } - }; + Info() : IBGW(nullptr), OBEP(nullptr), activity(0) {} + Info(const uint8_t * ibgw, const uint8_t * obep, const uint64_t a) : + activity(a) { + if(ibgw) IBGW = std::make_shared(ibgw); + else IBGW = nullptr; + if(obep) OBEP = std::make_shared(obep); + else OBEP = nullptr; + } + }; - Info GetSessionInfo() const; + Info GetSessionInfo() const; - private: + private: - void FlushSendQueue(); - void ScheduleFlushSendQueue(); + void FlushSendQueue(); + void ScheduleFlushSendQueue(); - void HandleSend(std::shared_ptr msg); + void HandleSend(std::shared_ptr msg); - std::shared_ptr GetSharedRoutingPath(); + std::shared_ptr GetSharedRoutingPath(); - void HandleLeaseSetUpdated(std::shared_ptr ls); + void HandleLeaseSetUpdated(std::shared_ptr ls); - private: - std::shared_ptr m_LocalDestination; - i2p::data::IdentHash m_RemoteIdent; - std::shared_ptr m_RemoteLeaseSet; - std::shared_ptr m_RoutingSession; - std::shared_ptr m_CurrentRemoteLease; - std::shared_ptr m_CurrentOutboundTunnel; - boost::asio::deadline_timer m_SendQueueTimer; - std::vector > m_SendQueue; - uint64_t m_LastUse; - bool m_RequestingLS; + private: + + std::shared_ptr m_LocalDestination; + i2p::data::IdentHash m_RemoteIdent; + std::shared_ptr m_RemoteLeaseSet; + std::shared_ptr m_RoutingSession; + std::shared_ptr m_CurrentRemoteLease; + std::shared_ptr m_CurrentOutboundTunnel; + boost::asio::deadline_timer m_SendQueueTimer; + std::vector > m_SendQueue; + uint64_t m_LastUse; + bool m_RequestingLS; }; typedef std::shared_ptr DatagramSession_ptr; @@ -101,17 +115,15 @@ namespace datagram typedef std::function Receiver; typedef std::function RawReceiver; - public: - - DatagramDestination (std::shared_ptr owner); + DatagramDestination (std::shared_ptr owner, bool gzip); ~DatagramDestination (); void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0); void SendRawDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0); void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw = false); - + void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; }; void ResetReceiver () { m_Receiver = nullptr; }; @@ -120,7 +132,7 @@ namespace datagram void SetRawReceiver (const RawReceiver& receiver) { m_RawReceiver = receiver; }; void ResetRawReceiver () { m_RawReceiver = nullptr; }; - + std::shared_ptr GetInfoForRemote(const i2p::data::IdentHash & remote); // clean up stale sessions @@ -128,13 +140,14 @@ namespace datagram private: - std::shared_ptr ObtainSession(const i2p::data::IdentHash & ident); + std::shared_ptr ObtainSession(const i2p::data::IdentHash & ident); - std::shared_ptr CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort, bool isRaw = false); + std::shared_ptr CreateDataMessage (const std::vector >& payloads, + uint16_t fromPort, uint16_t toPort, bool isRaw = false, bool checksum = true); void HandleDatagram (uint16_t fromPort, uint16_t toPort, uint8_t *const& buf, size_t len); void HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - + /** find a receiver by port, if none by port is found try default receiever, otherwise returns nullptr */ Receiver FindReceiver(uint16_t port); @@ -143,6 +156,7 @@ namespace datagram std::shared_ptr m_Owner; Receiver m_Receiver; // default RawReceiver m_RawReceiver; // default + bool m_Gzip; // gzip compression of data messages std::mutex m_SessionsMutex; std::map m_Sessions; std::mutex m_ReceiversMutex; @@ -150,6 +164,7 @@ namespace datagram i2p::data::GzipInflator m_Inflator; i2p::data::GzipDeflator m_Deflator; + std::vector m_From, m_Signature; }; } } diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 98c4b323..cd447773 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -1,6 +1,17 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include +#include +#include +#include #include "Crypto.h" #include "Log.h" #include "FS.h" @@ -13,8 +24,8 @@ namespace i2p namespace client { LeaseSetDestination::LeaseSetDestination (boost::asio::io_service& service, - bool isPublic, const std::map * params): - m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0), + bool isPublic, const std::map * params): + m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0), m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service), m_PublishVerificationTimer (m_Service), m_PublishDelayTimer (m_Service), m_CleanupTimer (m_Service), m_LeaseSetType (DEFAULT_LEASESET_TYPE), m_AuthType (i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE) @@ -157,13 +168,12 @@ namespace client bool LeaseSetDestination::Reconfigure(std::map params) { - auto itr = params.find("i2cp.dontPublishLeaseSet"); if (itr != params.end()) { m_IsPublic = itr->second != "true"; } - + int inLen, outLen, inQuant, outQuant, numTags, minLatency, maxLatency; std::map intOpts = { {I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen}, @@ -182,7 +192,7 @@ namespace client outQuant = pool->GetNumOutboundTunnels(); minLatency = 0; maxLatency = 0; - + for (auto & opt : intOpts) { itr = params.find(opt.first); @@ -194,7 +204,7 @@ namespace client pool->RequireLatency(minLatency, maxLatency); return pool->Reconfigure(inLen, outLen, inQuant, outQuant); } - + std::shared_ptr LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident) { std::shared_ptr remoteLS; @@ -263,7 +273,7 @@ namespace client std::lock_guard l(m_LeaseSetMutex); return m_LeaseSet; } - + void LeaseSetDestination::SetLeaseSet (std::shared_ptr newLeaseSet) { { @@ -278,7 +288,7 @@ namespace client { s->m_PublishVerificationTimer.cancel (); s->Publish (); - }); + }); } } @@ -412,7 +422,8 @@ namespace client auto it2 = m_LeaseSetRequests.find (key); if (it2 != m_LeaseSetRequests.end () && it2->second->requestedBlindedKey) { - auto ls2 = std::make_shared (buf + offset, len - offset, it2->second->requestedBlindedKey, m_LeaseSetPrivKey ? *m_LeaseSetPrivKey : nullptr, GetPreferredCryptoType ()); + auto ls2 = std::make_shared (buf + offset, len - offset, + it2->second->requestedBlindedKey, m_LeaseSetPrivKey ? ((const uint8_t *)*m_LeaseSetPrivKey) : nullptr , GetPreferredCryptoType ()); if (ls2->IsValid ()) { m_RemoteLeaseSets[ls2->GetIdentHash ()] = ls2; // ident is not key @@ -561,12 +572,12 @@ namespace client m_PublishReplyToken = 0; if (GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) { - LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds, will try again"); + LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds, will try again"); Publish (); } else { - LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds from Java floodfill for crypto type ", (int)GetIdentity ()->GetCryptoKeyType ()); + LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds from Java floodfill for crypto type ", (int)GetIdentity ()->GetCryptoKeyType ()); // Java floodfill never sends confirmation back for unknown crypto type // assume it successive and try to verify m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); @@ -587,7 +598,7 @@ namespace client { LogPrint (eLogWarning, "Destination: couldn't verify LeaseSet for ", GetIdentHash().ToBase32()); return; - } + } auto s = shared_from_this (); // we must capture this for gcc 4.7 due the bug RequestLeaseSet (ls->GetStoreHash (), @@ -639,9 +650,9 @@ namespace client if (requestComplete) m_Service.post ([requestComplete](void){requestComplete (nullptr);}); return false; - } + } auto storeHash = dest->GetStoreHash (); - auto leaseSet = FindLeaseSet (storeHash); + auto leaseSet = FindLeaseSet (storeHash); if (leaseSet) { if (requestComplete) @@ -716,7 +727,7 @@ namespace client } bool LeaseSetDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest, - std::shared_ptr nextFloodfill, std::shared_ptr request) + std::shared_ptr nextFloodfill, std::shared_ptr request) { if (!request->replyTunnel || !request->replyTunnel->IsEstablished ()) request->replyTunnel = m_Pool->GetNextInboundTunnel (); @@ -779,7 +790,7 @@ namespace client } else { - LogPrint (eLogWarning, "Destination: ", dest.ToBase64 (), " was not found within ", MAX_LEASESET_REQUEST_TIMEOUT, " seconds"); + LogPrint (eLogWarning, "Destination: ", dest.ToBase64 (), " was not found within ", MAX_LEASESET_REQUEST_TIMEOUT, " seconds"); done = true; } @@ -824,14 +835,14 @@ namespace client i2p::data::CryptoKeyType LeaseSetDestination::GetPreferredCryptoType () const { - if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET)) - return i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET; - return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; - } - - ClientDestination::ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, + if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET)) + return i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET; + return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; + } + + ClientDestination::ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params): - LeaseSetDestination (service, isPublic, params), + LeaseSetDestination (service, isPublic, params), m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY), m_DatagramDestination (nullptr), m_RefCounter (0), m_ReadyChecker(service) @@ -839,28 +850,58 @@ namespace client if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // offline keys can be published with LS2 only - m_EncryptionKeyType = GetIdentity ()->GetCryptoKeyType (); // extract encryption type params for LS2 - if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2 && params) + std::set encryptionKeyTypes; + if ((GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2 || + GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) && params) { auto it = params->find (I2CP_PARAM_LEASESET_ENCRYPTION_TYPE); if (it != params->end ()) - m_EncryptionKeyType = std::stoi(it->second); - } + { + // comma-separated values + std::vector values; + boost::split(values, it->second, boost::is_any_of(",")); + for (auto& it1: values) + { + try + { + encryptionKeyTypes.insert (std::stoi(it1)); + } + catch (std::exception& ex) + { + LogPrint (eLogInfo, "Destination: Unexpected crypto type ", it1, ". ", ex.what ()); + continue; + } + } + } + } + // if no param or valid crypto type use from identity + bool isSingleKey = false; + if (encryptionKeyTypes.empty ()) + { + isSingleKey = true; + encryptionKeyTypes.insert (GetIdentity ()->GetCryptoKeyType ()); + } + + for (auto& it: encryptionKeyTypes) + { + auto encryptionKey = new EncryptionKey (it); + if (isPublic) + PersistTemporaryKeys (encryptionKey, isSingleKey); + else + encryptionKey->GenerateKeys (); + encryptionKey->CreateDecryptor (); + if (it == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET) + m_ECIESx25519EncryptionKey.reset (encryptionKey); + else + m_StandardEncryptionKey.reset (encryptionKey); + } - memset (m_EncryptionPrivateKey, 0, 256); - memset (m_EncryptionPublicKey, 0, 256); - if (isPublic) - PersistTemporaryKeys (); - else - i2p::data::PrivateKeys::GenerateCryptoKeyPair (m_EncryptionKeyType, m_EncryptionPrivateKey, m_EncryptionPublicKey); - - m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_EncryptionKeyType, m_EncryptionPrivateKey); if (isPublic) LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created"); try - { + { if (params) { // extract streaming params @@ -876,9 +917,9 @@ namespace client { m_AuthKeys = std::make_shared >(); if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_DH) - ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_DH, params); + ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_DH, params); else if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK) - ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_PSK, params); + ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_PSK, params); else LogPrint (eLogError, "Destination: Unexpected auth type ", authType); if (m_AuthKeys->size ()) @@ -886,7 +927,7 @@ namespace client else { LogPrint (eLogError, "Destination: No auth keys read for auth type ", authType); - m_AuthKeys = nullptr; + m_AuthKeys = nullptr; } } } @@ -968,7 +1009,7 @@ namespace client m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length, true); else LogPrint (eLogError, "Destination: Missing raw datagram destination"); - break; + break; default: LogPrint (eLogError, "Destination: Data: unexpected protocol ", buf[9]); } @@ -1071,10 +1112,10 @@ namespace client return dest; } - i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination () + i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination (bool gzip) { if (m_DatagramDestination == nullptr) - m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis ()); + m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis (), gzip); return m_DatagramDestination; } @@ -1092,27 +1133,29 @@ namespace client return ret; } - void ClientDestination::PersistTemporaryKeys () + void ClientDestination::PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey) { + if (!keys) return; std::string ident = GetIdentHash().ToBase32(); - std::string path = i2p::fs::DataDirPath("destinations", (ident + ".dat")); + std::string path = i2p::fs::DataDirPath("destinations", + isSingleKey ? (ident + ".dat") : (ident + "." + std::to_string (keys->keyType) + ".dat")); std::ifstream f(path, std::ifstream::binary); if (f) { - f.read ((char *)m_EncryptionPublicKey, 256); - f.read ((char *)m_EncryptionPrivateKey, 256); + f.read ((char *)keys->pub, 256); + f.read ((char *)keys->priv, 256); return; } LogPrint (eLogInfo, "Destination: Creating new temporary keys of type for address ", ident, ".b32.i2p"); - memset (m_EncryptionPrivateKey, 0, 256); - memset (m_EncryptionPublicKey, 0, 256); - i2p::data::PrivateKeys::GenerateCryptoKeyPair (m_EncryptionKeyType, m_EncryptionPrivateKey, m_EncryptionPublicKey); + memset (keys->priv, 0, 256); + memset (keys->pub, 0, 256); + keys->GenerateKeys (); // TODO:: persist crypto key type std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out); if (f1) { - f1.write ((char *)m_EncryptionPublicKey, 256); - f1.write ((char *)m_EncryptionPrivateKey, 256); + f1.write ((char *)keys->pub, 256); + f1.write ((char *)keys->priv, 256); return; } LogPrint(eLogError, "Destinations: Can't save keys to ", path); @@ -1123,18 +1166,27 @@ namespace client std::shared_ptr leaseSet; if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) { - leaseSet = std::make_shared (GetIdentity (), m_EncryptionPublicKey, tunnels); - // sign - Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ()); + if (m_StandardEncryptionKey) + { + leaseSet = std::make_shared (GetIdentity (), m_StandardEncryptionKey->pub, tunnels); + // sign + Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ()); + } + else + LogPrint (eLogError, "Destinations: Wrong encryption key type for LeaseSet type 1"); } else { // standard LS2 (type 3) first - uint16_t keyLen = m_Decryptor ? m_Decryptor->GetPublicKeyLen () : 256; - bool isPublishedEncrypted = GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; + i2p::data::LocalLeaseSet2::KeySections keySections; + if (m_ECIESx25519EncryptionKey) + keySections.push_back ({m_ECIESx25519EncryptionKey->keyType, 32, m_ECIESx25519EncryptionKey->pub} ); + if (m_StandardEncryptionKey) + keySections.push_back ({m_StandardEncryptionKey->keyType, (uint16_t)m_StandardEncryptionKey->decryptor->GetPublicKeyLen (), m_StandardEncryptionKey->pub} ); + + bool isPublishedEncrypted = GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; auto ls2 = std::make_shared (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2, - m_Keys, i2p::data::LocalLeaseSet2::KeySections { {m_EncryptionKeyType, keyLen, m_EncryptionPublicKey} }, - tunnels, IsPublic (), isPublishedEncrypted); + m_Keys, keySections, tunnels, IsPublic (), isPublishedEncrypted); if (isPublishedEncrypted) // encrypt if type 5 ls2 = std::make_shared (ls2, m_Keys, GetAuthType (), m_AuthKeys); leaseSet = ls2; @@ -1149,13 +1201,28 @@ namespace client bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const { - if (m_Decryptor) - return m_Decryptor->Decrypt (encrypted, data, ctx, true); + if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET) + if (m_ECIESx25519EncryptionKey && m_ECIESx25519EncryptionKey->decryptor) + return m_ECIESx25519EncryptionKey->decryptor->Decrypt (encrypted, data, ctx, true); + if (m_StandardEncryptionKey && m_StandardEncryptionKey->decryptor) + return m_StandardEncryptionKey->decryptor->Decrypt (encrypted, data, ctx, true); else LogPrint (eLogError, "Destinations: decryptor is not set"); return false; } + bool ClientDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const + { + return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET ? (bool)m_ECIESx25519EncryptionKey : (bool)m_StandardEncryptionKey; + } + + const uint8_t * ClientDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const + { + if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET) + return m_ECIESx25519EncryptionKey ? m_ECIESx25519EncryptionKey->pub : nullptr; + return m_StandardEncryptionKey ? m_StandardEncryptionKey->pub : nullptr; + } + void ClientDestination::ReadAuthKey (const std::string& group, const std::map * params) { for (auto it: *params) @@ -1169,7 +1236,7 @@ namespace client m_AuthKeys->push_back (pubKey); else LogPrint (eLogError, "Destination: Unexpected auth key ", it.second.substr (pos+1)); - } + } } } @@ -1181,10 +1248,10 @@ namespace client if (it.second->DeleteStream (recvStreamID)) return true; return false; - } - + } + RunnableClientDestination::RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params): - RunnableService ("Destination"), + RunnableService ("Destination"), ClientDestination (GetIOService (), keys, isPublic, params) { } @@ -1193,7 +1260,7 @@ namespace client { if (IsRunning ()) Stop (); - } + } void RunnableClientDestination::Start () { diff --git a/libi2pd/Destination.h b/libi2pd/Destination.h index 83a38816..139423c6 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -1,6 +1,15 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef DESTINATION_H__ #define DESTINATION_H__ +#include #include #include #include @@ -51,13 +60,13 @@ namespace client const char I2CP_PARAM_INBOUND_NICKNAME[] = "inbound.nickname"; const char I2CP_PARAM_OUTBOUND_NICKNAME[] = "outbound.nickname"; const char I2CP_PARAM_LEASESET_TYPE[] = "i2cp.leaseSetType"; - const int DEFAULT_LEASESET_TYPE = 1; + const int DEFAULT_LEASESET_TYPE = 1; const char I2CP_PARAM_LEASESET_ENCRYPTION_TYPE[] = "i2cp.leaseSetEncType"; const char I2CP_PARAM_LEASESET_PRIV_KEY[] = "i2cp.leaseSetPrivKey"; // PSK decryption key, base64 const char I2CP_PARAM_LEASESET_AUTH_TYPE[] = "i2cp.leaseSetAuthType"; const char I2CP_PARAM_LEASESET_CLIENT_DH[] = "i2cp.leaseSetClient.dh"; // group of i2cp.leaseSetClient.dh.nnn const char I2CP_PARAM_LEASESET_CLIENT_PSK[] = "i2cp.leaseSetClient.psk"; // group of i2cp.leaseSetClient.psk.nnn - + // latency const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min"; const int DEFAULT_MIN_TUNNEL_LATENCY = 0; @@ -93,7 +102,6 @@ namespace client } }; - public: LeaseSetDestination (boost::asio::io_service& service, bool isPublic, const std::map * params = nullptr); @@ -106,12 +114,12 @@ namespace client /** i2cp reconfigure */ virtual bool Reconfigure(std::map i2cpOpts); - + std::shared_ptr GetTunnelPool () { return m_Pool; }; bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; }; std::shared_ptr FindLeaseSet (const i2p::data::IdentHash& ident); bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr); - bool RequestDestinationWithEncryptedLeaseSet (std::shared_ptr dest, RequestComplete requestComplete = nullptr); + bool RequestDestinationWithEncryptedLeaseSet (std::shared_ptr dest, RequestComplete requestComplete = nullptr); void CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify = true); void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr dest, bool notify = true); @@ -154,7 +162,7 @@ namespace client void HandleDeliveryStatusMessage (uint32_t msgID); void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr requestedBlindedKey = nullptr); - bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr nextFloodfill, std::shared_ptr request); + bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr nextFloodfill, std::shared_ptr request); void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest); void HandleCleanupTimer (const boost::system::error_code& ecode); void CleanupRemoteLeaseSets (); @@ -192,9 +200,20 @@ namespace client class ClientDestination: public LeaseSetDestination { + struct EncryptionKey + { + uint8_t pub[256], priv[256]; + i2p::data::CryptoKeyType keyType; + std::shared_ptr decryptor; + + EncryptionKey (i2p::data::CryptoKeyType t):keyType(t) { memset (pub, 0, 256); memset (priv, 0, 256); }; + void GenerateKeys () { i2p::data::PrivateKeys::GenerateCryptoKeyPair (keyType, priv, pub); }; + void CreateDecryptor () { decryptor = i2p::data::PrivateKeys::CreateDecryptor (keyType, priv); }; + }; + public: - ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, + ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params = nullptr); ~ClientDestination (); @@ -223,14 +242,14 @@ namespace client int GetStreamingAckDelay () const { return m_StreamingAckDelay; } // datagram - i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; }; - i2p::datagram::DatagramDestination * CreateDatagramDestination (); + i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; }; + i2p::datagram::DatagramDestination * CreateDatagramDestination (bool gzip = true); // implements LocalDestination bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const; std::shared_ptr GetIdentity () const { return m_Keys.GetPublic (); }; - bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const { return m_EncryptionKeyType == keyType; }; - const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const { return m_EncryptionPublicKey; }; + bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const; + const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const; protected: @@ -244,15 +263,14 @@ namespace client std::shared_ptr GetSharedFromThis () { return std::static_pointer_cast(shared_from_this ()); } - void PersistTemporaryKeys (); + void PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey); void ReadAuthKey (const std::string& group, const std::map * params); private: i2p::data::PrivateKeys m_Keys; - uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256]; - i2p::data::CryptoKeyType m_EncryptionKeyType; - std::shared_ptr m_Decryptor; + std::unique_ptr m_StandardEncryptionKey; + std::unique_ptr m_ECIESx25519EncryptionKey; int m_StreamingAckDelay; std::shared_ptr m_StreamingDestination; // default @@ -262,7 +280,7 @@ namespace client boost::asio::deadline_timer m_ReadyChecker; - std::shared_ptr > m_AuthKeys; // we don't need them for I2CP + std::shared_ptr > m_AuthKeys; // we don't need them for I2CP public: @@ -276,7 +294,7 @@ namespace client public: RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params = nullptr); - ~RunnableClientDestination (); + ~RunnableClientDestination (); void Start (); void Stop (); diff --git a/libi2pd/ECIESX25519AEADRatchetSession.cpp b/libi2pd/ECIESX25519AEADRatchetSession.cpp index 4371124e..310d263e 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.cpp +++ b/libi2pd/ECIESX25519AEADRatchetSession.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include "Log.h" @@ -15,133 +23,159 @@ namespace i2p namespace garlic { - void RatchetTagSet::DHInitialize (const uint8_t * rootKey, const uint8_t * k) - { - // DH_INITIALIZE(rootKey, k) - uint8_t keydata[64]; - i2p::crypto::HKDF (rootKey, k, 32, "KDFDHRatchetStep", keydata); // keydata = HKDF(rootKey, k, "KDFDHRatchetStep", 64) - // nextRootKey = keydata[0:31] - i2p::crypto::HKDF (keydata + 32, nullptr, 0, "TagAndKeyGenKeys", m_KeyData.buf); - // [sessTag_ck, symmKey_ck] = HKDF(keydata[32:63], ZEROLEN, "TagAndKeyGenKeys", 64) + void RatchetTagSet::DHInitialize (const uint8_t * rootKey, const uint8_t * k) + { + // DH_INITIALIZE(rootKey, k) + uint8_t keydata[64]; + i2p::crypto::HKDF (rootKey, k, 32, "KDFDHRatchetStep", keydata); // keydata = HKDF(rootKey, k, "KDFDHRatchetStep", 64) + memcpy (m_NextRootKey, keydata, 32); // nextRootKey = keydata[0:31] + i2p::crypto::HKDF (keydata + 32, nullptr, 0, "TagAndKeyGenKeys", m_KeyData.buf); + // [sessTag_ck, symmKey_ck] = HKDF(keydata[32:63], ZEROLEN, "TagAndKeyGenKeys", 64) memcpy (m_SymmKeyCK, m_KeyData.buf + 32, 32); m_NextSymmKeyIndex = 0; - } + } - void RatchetTagSet::NextSessionTagRatchet () - { - i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), nullptr, 0, "STInitialization", m_KeyData.buf); // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64) - memcpy (m_SessTagConstant, m_KeyData.GetSessTagConstant (), 32); + void RatchetTagSet::NextSessionTagRatchet () + { + i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), nullptr, 0, "STInitialization", m_KeyData.buf); // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64) + memcpy (m_SessTagConstant, m_KeyData.GetSessTagConstant (), 32); m_NextIndex = 0; - } + } - uint64_t RatchetTagSet::GetNextSessionTag () - { - i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), m_SessTagConstant, 32, "SessionTagKeyGen", m_KeyData.buf); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64) - m_NextIndex++; - if (m_NextIndex >= 65535) m_NextIndex = 0; // TODO: dirty hack, should create new tagset - return m_KeyData.GetTag (); - } + uint64_t RatchetTagSet::GetNextSessionTag () + { + i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), m_SessTagConstant, 32, "SessionTagKeyGen", m_KeyData.buf); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64) + m_NextIndex++; + if (m_NextIndex >= 65535) + { + LogPrint (eLogError, "Garlic: Tagset ", GetTagSetID (), " is empty"); + return 0; + } + return m_KeyData.GetTag (); + } void RatchetTagSet::GetSymmKey (int index, uint8_t * key) { - if (m_NextSymmKeyIndex > 0 && index >= m_NextSymmKeyIndex) - { + if (index >= m_NextSymmKeyIndex) + { auto num = index + 1 - m_NextSymmKeyIndex; + if (!m_NextSymmKeyIndex) + { + i2p::crypto::HKDF (m_SymmKeyCK, nullptr, 0, "SymmetricRatchet", m_CurrentSymmKeyCK); // keydata_0 = HKDF(symmKey_ck, SYMMKEY_CONSTANT, "SymmetricRatchet", 64) + m_NextSymmKeyIndex = 1; + num--; + } for (int i = 0; i < num; i++) + { i2p::crypto::HKDF (m_CurrentSymmKeyCK, nullptr, 0, "SymmetricRatchet", m_CurrentSymmKeyCK); + if (i < num - 1) + m_ItermediateSymmKeys.emplace (m_NextSymmKeyIndex + i, m_CurrentSymmKeyCK + 32); + } m_NextSymmKeyIndex += num; memcpy (key, m_CurrentSymmKeyCK + 32, 32); } else - CalculateSymmKeyCK (index, key); - } - - void RatchetTagSet::CalculateSymmKeyCK (int index, uint8_t * key) - { - // TODO: store intermediate keys - uint8_t currentSymmKeyCK[64]; - i2p::crypto::HKDF (m_SymmKeyCK, nullptr, 0, "SymmetricRatchet", currentSymmKeyCK); // keydata_0 = HKDF(symmKey_ck, SYMMKEY_CONSTANT, "SymmetricRatchet", 64) - for (int i = 0; i < index; i++) - i2p::crypto::HKDF (currentSymmKeyCK, nullptr, 0, "SymmetricRatchet", currentSymmKeyCK); // keydata_n = HKDF(symmKey_chainKey_(n-1), SYMMKEY_CONSTANT, "SymmetricRatchet", 64) - memcpy (key, currentSymmKeyCK + 32, 32); + { + auto it = m_ItermediateSymmKeys.find (index); + if (it != m_ItermediateSymmKeys.end ()) + { + memcpy (key, it->second, 32); + m_ItermediateSymmKeys.erase (it); + } + else + LogPrint (eLogError, "Garlic: Missing symmetric key for index ", index); + } } - - ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner): - GarlicRoutingSession (owner, true) - { - ResetKeys (); - } - ECIESX25519AEADRatchetSession::~ECIESX25519AEADRatchetSession () - { - } + void RatchetTagSet::Expire () + { + if (!m_ExpirationTimestamp) + m_ExpirationTimestamp = i2p::util::GetSecondsSinceEpoch () + ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT; + } + + ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSet): + GarlicRoutingSession (owner, attachLeaseSet) + { + ResetKeys (); + } + + ECIESX25519AEADRatchetSession::~ECIESX25519AEADRatchetSession () + { + } void ECIESX25519AEADRatchetSession::ResetKeys () { - // TODO : use precalculated hashes - static const char protocolName[41] = "Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"; // 40 bytes - SHA256 ((const uint8_t *)protocolName, 40, m_H); - memcpy (m_CK, m_H, 32); - SHA256 (m_H, 32, m_H); - } - - void ECIESX25519AEADRatchetSession::MixHash (const uint8_t * buf, size_t len) - { - SHA256_CTX ctx; + static const uint8_t protocolNameHash[32] = + { + 0x4c, 0xaf, 0x11, 0xef, 0x2c, 0x8e, 0x36, 0x56, 0x4c, 0x53, 0xe8, 0x88, 0x85, 0x06, 0x4d, 0xba, + 0xac, 0xbe, 0x00, 0x54, 0xad, 0x17, 0x8f, 0x80, 0x79, 0xa6, 0x46, 0x82, 0x7e, 0x6e, 0xe4, 0x0c + }; // SHA256("Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"), 40 bytes + static const uint8_t hh[32] = + { + 0x9c, 0xcf, 0x85, 0x2c, 0xc9, 0x3b, 0xb9, 0x50, 0x44, 0x41, 0xe9, 0x50, 0xe0, 0x1d, 0x52, 0x32, + 0x2e, 0x0d, 0x47, 0xad, 0xd1, 0xe9, 0xa5, 0x55, 0xf7, 0x55, 0xb5, 0x69, 0xae, 0x18, 0x3b, 0x5c + }; // SHA256 (protocolNameHash) + memcpy (m_CK, protocolNameHash, 32); + memcpy (m_H, hh, 32); + } + + void ECIESX25519AEADRatchetSession::MixHash (const uint8_t * buf, size_t len) + { + SHA256_CTX ctx; SHA256_Init (&ctx); SHA256_Update (&ctx, m_H, 32); SHA256_Update (&ctx, buf, len); SHA256_Final (m_H, &ctx); - } - - void ECIESX25519AEADRatchetSession::CreateNonce (uint64_t seqn, uint8_t * nonce) - { - memset (nonce, 0, 4); - htole64buf (nonce + 4, seqn); } - bool ECIESX25519AEADRatchetSession::GenerateEphemeralKeysAndEncode (uint8_t * buf) - { - for (int i = 0; i < 10; i++) - { - m_EphemeralKeys.GenerateKeys (); - if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys.GetPublicKey (), buf)) - return true; // success - } - return false; - } + void ECIESX25519AEADRatchetSession::CreateNonce (uint64_t seqn, uint8_t * nonce) + { + memset (nonce, 0, 4); + htole64buf (nonce + 4, seqn); + } - uint64_t ECIESX25519AEADRatchetSession::CreateNewSessionTag () const - { - uint8_t tagsetKey[32]; - i2p::crypto::HKDF (m_CK, nullptr, 0, "SessionReplyTags", tagsetKey, 32); // tagsetKey = HKDF(chainKey, ZEROLEN, "SessionReplyTags", 32) - // Session Tag Ratchet - RatchetTagSet tagsetNsr; - tagsetNsr.DHInitialize (m_CK, tagsetKey); // tagset_nsr = DH_INITIALIZE(chainKey, tagsetKey) - tagsetNsr.NextSessionTagRatchet (); - return tagsetNsr.GetNextSessionTag (); - } - - bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession (const uint8_t * buf, size_t len) - { - if (!GetOwner ()) return false; - // we are Bob - // KDF1 - MixHash (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET), 32); // h = SHA256(h || bpk) - - if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk)) - { - LogPrint (eLogError, "Garlic: Can't decode elligator"); - return false; + bool ECIESX25519AEADRatchetSession::GenerateEphemeralKeysAndEncode (uint8_t * buf) + { + for (int i = 0; i < 10; i++) + { + m_EphemeralKeys.GenerateKeys (); + if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys.GetPublicKey (), buf)) + return true; // success } - buf += 32; len -= 32; - MixHash (m_Aepk, 32); // h = SHA256(h || aepk) - - uint8_t sharedSecret[32]; - GetOwner ()->Decrypt (m_Aepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET); // x25519(bsk, aepk) + return false; + } + + std::shared_ptr ECIESX25519AEADRatchetSession::CreateNewSessionTagset () + { + uint8_t tagsetKey[32]; + i2p::crypto::HKDF (m_CK, nullptr, 0, "SessionReplyTags", tagsetKey, 32); // tagsetKey = HKDF(chainKey, ZEROLEN, "SessionReplyTags", 32) + // Session Tag Ratchet + auto tagsetNsr = std::make_shared(shared_from_this ()); + tagsetNsr->DHInitialize (m_CK, tagsetKey); // tagset_nsr = DH_INITIALIZE(chainKey, tagsetKey) + tagsetNsr->NextSessionTagRatchet (); + return tagsetNsr; + } + + bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession (const uint8_t * buf, size_t len) + { + if (!GetOwner ()) return false; + // we are Bob + // KDF1 + MixHash (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET), 32); // h = SHA256(h || bpk) + + if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk)) + { + LogPrint (eLogError, "Garlic: Can't decode elligator"); + return false; + } + buf += 32; len -= 32; + MixHash (m_Aepk, 32); // h = SHA256(h || aepk) + + uint8_t sharedSecret[32]; + GetOwner ()->Decrypt (m_Aepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519(bsk, aepk) i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) - - // decrypt flags/static + + // decrypt flags/static uint8_t nonce[12], fs[32]; CreateNonce (0, nonce); if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 32, m_H, 32, m_CK + 32, nonce, fs, 32, false)) // decrypt @@ -152,36 +186,37 @@ namespace garlic MixHash (buf, 48); // h = SHA256(h || ciphertext) buf += 48; len -= 48; // 32 data + 16 poly - // decrypt payload - std::vector payload (len - 16); // KDF2 for payload - bool isStatic = !i2p::data::Tag<32> (fs).IsZero (); + bool isStatic = !i2p::data::Tag<32> (fs).IsZero (); if (isStatic) { // static key, fs is apk - memcpy (m_RemoteStaticKey, fs, 32); - GetOwner ()->Decrypt (fs, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET); // x25519(bsk, apk) + memcpy (m_RemoteStaticKey, fs, 32); + GetOwner ()->Decrypt (fs, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519(bsk, apk) i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) } else // all zeros flags CreateNonce (1, nonce); + + // decrypt payload + std::vector payload (len - 16); // we must save original ciphertext if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt { LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed"); return false; } if (isStatic) MixHash (buf, len); // h = SHA256(h || ciphertext) - m_State = eSessionStateNewSessionReceived; + m_State = eSessionStateNewSessionReceived; GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ()); - HandlePayload (payload.data (), len - 16); + HandlePayload (payload.data (), len - 16, nullptr, 0); - return true; - } + return true; + } - void ECIESX25519AEADRatchetSession::HandlePayload (const uint8_t * buf, size_t len, int index) - { - size_t offset = 0; + void ECIESX25519AEADRatchetSession::HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr& receiveTagset, int index) + { + size_t offset = 0; while (offset < len) { uint8_t blk = buf[offset]; @@ -197,60 +232,171 @@ namespace garlic switch (blk) { case eECIESx25519BlkGalicClove: - GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size); + if (GetOwner ()) + GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size); + break; + case eECIESx25519BlkNextKey: + LogPrint (eLogDebug, "Garlic: next key"); + HandleNextKey (buf + offset, size, receiveTagset); + break; + case eECIESx25519BlkAck: + { + LogPrint (eLogDebug, "Garlic: ack"); + int numAcks = size >> 2; // /4 + auto offset1 = offset; + for (auto i = 0; i < numAcks; i++) + { + offset1 += 2; // tagsetid + MessageConfirmed (bufbe16toh (buf + offset1)); offset1 += 2; // N + } + break; + } + case eECIESx25519BlkAckRequest: + { + LogPrint (eLogDebug, "Garlic: ack request"); + m_AckRequests.push_back ({receiveTagset->GetTagSetID (), index}); + break; + } + case eECIESx25519BlkTermination: + LogPrint (eLogDebug, "Garlic: termination"); + if (GetOwner ()) + GetOwner ()->RemoveECIESx25519Session (m_RemoteStaticKey); + if (receiveTagset) receiveTagset->Expire (); break; case eECIESx25519BlkDateTime: LogPrint (eLogDebug, "Garlic: datetime"); - break; + break; case eECIESx25519BlkOptions: LogPrint (eLogDebug, "Garlic: options"); break; case eECIESx25519BlkPadding: LogPrint (eLogDebug, "Garlic: padding"); break; - case eECIESx25519BlkAckRequest: - { - LogPrint (eLogDebug, "Garlic: ack request"); - m_AckRequests.push_back ({0, index}); // TODO: use actual tagsetid - break; - } default: LogPrint (eLogWarning, "Garlic: Unknown block type ", (int)blk); } offset += size; } - } + } - bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) - { + void ECIESX25519AEADRatchetSession::HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr& receiveTagset) + { + uint8_t flag = buf[0]; buf++; // flag + if (flag & ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG) + { + if (!m_SendForwardKey || !m_NextSendRatchet) return; + uint16_t keyID = bufbe16toh (buf); buf += 2; // keyID + if (((!m_NextSendRatchet->newKey || !m_NextSendRatchet->keyID) && keyID == m_NextSendRatchet->keyID) || + (m_NextSendRatchet->newKey && keyID == m_NextSendRatchet->keyID -1)) + { + if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG) + memcpy (m_NextSendRatchet->remote, buf, 32); + uint8_t sharedSecret[32], tagsetKey[32]; + m_NextSendRatchet->key.Agree (m_NextSendRatchet->remote, sharedSecret); + i2p::crypto::HKDF (sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32) + auto newTagset = std::make_shared (shared_from_this ()); + newTagset->SetTagSetID (1 + m_NextSendRatchet->keyID + keyID); + newTagset->DHInitialize (m_SendTagset->GetNextRootKey (), tagsetKey); + newTagset->NextSessionTagRatchet (); + m_SendTagset = newTagset; + m_SendForwardKey = false; + LogPrint (eLogDebug, "Garlic: next send tagset ", newTagset->GetTagSetID (), " created"); + } + else + LogPrint (eLogDebug, "Garlic: Unexpected next key ", keyID); + } + else + { + uint16_t keyID = bufbe16toh (buf); buf += 2; // keyID + bool newKey = flag & ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; + m_SendReverseKey = true; + if (!m_NextReceiveRatchet) + m_NextReceiveRatchet.reset (new DHRatchet ()); + else + { + if (keyID == m_NextReceiveRatchet->keyID && newKey == m_NextReceiveRatchet->newKey) + { + LogPrint (eLogDebug, "Garlic: Duplicate ", newKey ? "new" : "old", " key ", keyID, " received"); + return; + } + m_NextReceiveRatchet->keyID = keyID; + } + int tagsetID = 2*keyID; + if (newKey) + { + m_NextReceiveRatchet->key.GenerateKeys (); + m_NextReceiveRatchet->newKey = true; + tagsetID++; + } + else + m_NextReceiveRatchet->newKey = false; + if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG) + memcpy (m_NextReceiveRatchet->remote, buf, 32); + + uint8_t sharedSecret[32], tagsetKey[32]; + m_NextReceiveRatchet->key.Agree (m_NextReceiveRatchet->remote, sharedSecret); + i2p::crypto::HKDF (sharedSecret, nullptr, 0, "XDHRatchetTagSet", tagsetKey, 32); // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32) + auto newTagset = std::make_shared(shared_from_this ()); + newTagset->SetTagSetID (tagsetID); + newTagset->DHInitialize (receiveTagset->GetNextRootKey (), tagsetKey); + newTagset->NextSessionTagRatchet (); + GenerateMoreReceiveTags (newTagset, ECIESX25519_MAX_NUM_GENERATED_TAGS); + receiveTagset->Expire (); + LogPrint (eLogDebug, "Garlic: next receive tagset ", tagsetID, " created"); + } + } + + void ECIESX25519AEADRatchetSession::NewNextSendRatchet () + { + if (m_NextSendRatchet) + { + if (!m_NextSendRatchet->newKey || !m_NextSendRatchet->keyID) + { + m_NextSendRatchet->keyID++; + m_NextSendRatchet->newKey = true; + } + else + m_NextSendRatchet->newKey = false; + } + else + m_NextSendRatchet.reset (new DHRatchet ()); + if (m_NextSendRatchet->newKey) + m_NextSendRatchet->key.GenerateKeys (); + + m_SendForwardKey = true; + LogPrint (eLogDebug, "Garlic: new send ratchet ", m_NextSendRatchet->newKey ? "new" : "old", " key ", m_NextSendRatchet->keyID, " created"); + } + + bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) + { ResetKeys (); - // we are Alice, bpk is m_RemoteStaticKey - size_t offset = 0; - if (!GenerateEphemeralKeysAndEncode (out + offset)) - { + // we are Alice, bpk is m_RemoteStaticKey + size_t offset = 0; + if (!GenerateEphemeralKeysAndEncode (out + offset)) + { LogPrint (eLogError, "Garlic: Can't encode elligator"); - return false; - } - offset += 32; + return false; + } + offset += 32; - // KDF1 - MixHash (m_RemoteStaticKey, 32); // h = SHA256(h || bpk) - MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || aepk) - uint8_t sharedSecret[32]; + // KDF1 + MixHash (m_RemoteStaticKey, 32); // h = SHA256(h || bpk) + MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || aepk) + uint8_t sharedSecret[32]; m_EphemeralKeys.Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk) - i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) - // encrypt static key section - uint8_t nonce[12]; + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) + // encrypt static key section + uint8_t nonce[12]; CreateNonce (0, nonce); - if (!i2p::crypto::AEADChaCha20Poly1305 (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET), 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt + if (!i2p::crypto::AEADChaCha20Poly1305 (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET), 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt { LogPrint (eLogWarning, "Garlic: Static section AEAD encryption failed "); return false; } - MixHash (out + offset, 48); // h = SHA256(h || ciphertext) - offset += 48; - // KDF2 - GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET); // x25519 (ask, bpk) + MixHash (out + offset, 48); // h = SHA256(h || ciphertext) + offset += 48; + // KDF2 + GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519 (ask, bpk) i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) // encrypt payload if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt @@ -261,190 +407,239 @@ namespace garlic MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext) m_State = eSessionStateNewSessionSent; - if (GetOwner ()) - GetOwner ()->AddECIESx25519SessionTag (0, CreateNewSessionTag (), shared_from_this ()); + if (GetOwner ()) + GenerateMoreReceiveTags (CreateNewSessionTagset (), ECIESX25519_NSR_NUM_GENERATED_TAGS); - return true; - } + return true; + } - bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) - { - // we are Bob - uint64_t tag = CreateNewSessionTag (); - - size_t offset = 0; - memcpy (out + offset, &tag, 8); - offset += 8; - if (!GenerateEphemeralKeysAndEncode (out + offset)) // bepk - { + bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) + { + // we are Bob + m_NSRTagset = CreateNewSessionTagset (); + uint64_t tag = m_NSRTagset->GetNextSessionTag (); + + size_t offset = 0; + memcpy (out + offset, &tag, 8); + offset += 8; + if (!GenerateEphemeralKeysAndEncode (out + offset)) // bepk + { LogPrint (eLogError, "Garlic: Can't encode elligator"); - return false; + return false; } - offset += 32; - // KDF for Reply Key Section - MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag) - MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || bepk) - uint8_t sharedSecret[32]; - m_EphemeralKeys.Agree (m_Aepk, sharedSecret); // sharedSecret = x25519(besk, aepk) - i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32) - m_EphemeralKeys.Agree (m_RemoteStaticKey, sharedSecret); // sharedSecret = x25519(besk, apk) - i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) + memcpy (m_NSREncodedKey, out + offset, 56); // for possible next NSR + memcpy (m_NSRH, m_H, 32); + offset += 32; + // KDF for Reply Key Section + MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag) + MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || bepk) + uint8_t sharedSecret[32]; + m_EphemeralKeys.Agree (m_Aepk, sharedSecret); // sharedSecret = x25519(besk, aepk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32) + m_EphemeralKeys.Agree (m_RemoteStaticKey, sharedSecret); // sharedSecret = x25519(besk, apk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) uint8_t nonce[12]; CreateNonce (0, nonce); - // calulate hash for zero length - if (!i2p::crypto::AEADChaCha20Poly1305 (sharedSecret /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + offset, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad) + // calculate hash for zero length + if (!i2p::crypto::AEADChaCha20Poly1305 (nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + offset, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad) { LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed"); return false; } - MixHash (out + offset, 16); // h = SHA256(h || ciphertext) - offset += 16; - memcpy (m_NSRHeader, out, 56); // for possible next NSR - // KDF for payload - uint8_t keydata[64]; - i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) + MixHash (out + offset, 16); // h = SHA256(h || ciphertext) + offset += 16; + // KDF for payload + uint8_t keydata[64]; + i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) // k_ab = keydata[0:31], k_ba = keydata[32:63] - m_ReceiveTagset.DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab) - m_ReceiveTagset.NextSessionTagRatchet (); - m_SendTagset.DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) - m_SendTagset.NextSessionTagRatchet (); - GenerateMoreReceiveTags (GetOwner ()->GetNumTags ()); - i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", m_NSRKey, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) - // encrypt payload - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset, len + 16, true)) // encrypt + auto receiveTagset = std::make_shared(shared_from_this ()); + receiveTagset->DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab) + receiveTagset->NextSessionTagRatchet (); + m_SendTagset = std::make_shared(shared_from_this ()); + m_SendTagset->DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) + m_SendTagset->NextSessionTagRatchet (); + GenerateMoreReceiveTags (receiveTagset, ECIESX25519_MIN_NUM_GENERATED_TAGS); + i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", m_NSRKey, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) + // encrypt payload + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset, len + 16, true)) // encrypt { LogPrint (eLogWarning, "Garlic: NSR payload section AEAD encryption failed"); return false; } m_State = eSessionStateNewSessionReplySent; + m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch (); - return true; - } + return true; + } bool ECIESX25519AEADRatchetSession::NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) - { - // we are Bob and sent NSR already - memcpy (out, m_NSRHeader, 56); + { + // we are Bob and sent NSR already + uint64_t tag = m_NSRTagset->GetNextSessionTag (); // next tag + memcpy (out, &tag, 8); + memcpy (out + 8, m_NSREncodedKey, 32); + // recalculate h with new tag + memcpy (m_H, m_NSRH, 32); + MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag) + MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || bepk) uint8_t nonce[12]; CreateNonce (0, nonce); + if (!i2p::crypto::AEADChaCha20Poly1305 (nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + 40, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad) + { + LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed"); + return false; + } + MixHash (out + 40, 16); // h = SHA256(h || ciphertext) // encrypt payload - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + 56, len + 16, true)) // encrypt + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + 56, len + 16, true)) // encrypt { LogPrint (eLogWarning, "Garlic: Next NSR payload section AEAD encryption failed"); return false; } return true; - } - - bool ECIESX25519AEADRatchetSession::HandleNewOutgoingSessionReply (const uint8_t * buf, size_t len) - { + } + + bool ECIESX25519AEADRatchetSession::HandleNewOutgoingSessionReply (uint8_t * buf, size_t len) + { // we are Alice LogPrint (eLogDebug, "Garlic: reply received"); const uint8_t * tag = buf; buf += 8; len -= 8; // tag - uint8_t bepk[32]; // Bob's ephemeral key + uint8_t bepk[32]; // Bob's ephemeral key if (!i2p::crypto::GetElligator ()->Decode (buf, bepk)) - { + { LogPrint (eLogError, "Garlic: Can't decode elligator"); - return false; - } + return false; + } buf += 32; len -= 32; - // KDF for Reply Key Section - MixHash (tag, 8); // h = SHA256(h || tag) - MixHash (bepk, 32); // h = SHA256(h || bepk) - uint8_t sharedSecret[32]; - m_EphemeralKeys.Agree (bepk, sharedSecret); // sharedSecret = x25519(aesk, bepk) - i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32) - GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET); // x25519 (ask, bepk) - i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) + // KDF for Reply Key Section + uint8_t h[32]; memcpy (h, m_H, 32); // save m_H + MixHash (tag, 8); // h = SHA256(h || tag) + MixHash (bepk, 32); // h = SHA256(h || bepk) + uint8_t sharedSecret[32]; + if (m_State == eSessionStateNewSessionSent) + { + // only fist time, we assume ephemeral keys the same + m_EphemeralKeys.Agree (bepk, sharedSecret); // sharedSecret = x25519(aesk, bepk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32) + GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519 (ask, bepk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) + } uint8_t nonce[12]; CreateNonce (0, nonce); - // calulate hash for zero length - if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 0, m_H, 32, m_CK + 32, nonce, sharedSecret/* can be anyting */, 0, false)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only + // calculate hash for zero length + if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 0, m_H, 32, m_CK + 32, nonce, sharedSecret/* can be anything */, 0, false)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only { LogPrint (eLogWarning, "Garlic: Reply key section AEAD decryption failed"); return false; } - MixHash (buf, 16); // h = SHA256(h || ciphertext) + MixHash (buf, 16); // h = SHA256(h || ciphertext) buf += 16; len -= 16; // KDF for payload - uint8_t keydata[64]; - i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) - // k_ab = keydata[0:31], k_ba = keydata[32:63] - m_SendTagset.DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab) - m_SendTagset.NextSessionTagRatchet (); - m_ReceiveTagset.DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) - m_ReceiveTagset.NextSessionTagRatchet (); - GenerateMoreReceiveTags (GetOwner ()->GetNumTags ()); - i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) + uint8_t keydata[64]; + i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) + if (m_State == eSessionStateNewSessionSent) + { + // k_ab = keydata[0:31], k_ba = keydata[32:63] + m_SendTagset = std::make_shared(shared_from_this ()); + m_SendTagset->DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab) + m_SendTagset->NextSessionTagRatchet (); + auto receiveTagset = std::make_shared(shared_from_this ()); + receiveTagset->DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) + receiveTagset->NextSessionTagRatchet (); + GenerateMoreReceiveTags (receiveTagset, ECIESX25519_MIN_NUM_GENERATED_TAGS); + } + i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) // decrypt payload - std::vector payload (len - 16); - if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, keydata, nonce, payload.data (), len - 16, false)) // decrypt + if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, keydata, nonce, buf, len - 16, false)) // decrypt { LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed"); return false; } - - m_State = eSessionStateEstablished; - GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ()); - HandlePayload (payload.data (), len - 16); - return true; - } + if (m_State == eSessionStateNewSessionSent) + { + m_State = eSessionStateEstablished; + m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch (); + GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ()); + } + memcpy (m_H, h, 32); // restore m_H + HandlePayload (buf, len - 16, nullptr, 0); + + // we have received reply to NS with LeaseSet in it + SetLeaseSetUpdateStatus (eLeaseSetUpToDate); + SetLeaseSetUpdateMsgID (0); + + return true; + } bool ECIESX25519AEADRatchetSession::NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) { uint8_t nonce[12]; - auto index = m_SendTagset.GetNextIndex (); + auto index = m_SendTagset->GetNextIndex (); CreateNonce (index, nonce); // tag's index - uint64_t tag = m_SendTagset.GetNextSessionTag (); + uint64_t tag = m_SendTagset->GetNextSessionTag (); memcpy (out, &tag, 8); // ad = The session tag, 8 bytes // ciphertext = ENCRYPT(k, n, payload, ad) uint8_t key[32]; - m_SendTagset.GetSymmKey (index, key); + m_SendTagset->GetSymmKey (index, key); if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, out, 8, key, nonce, out + 8, outLen - 8, true)) // encrypt { LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); return false; - } + } + if (index >= ECIESX25519_TAGSET_MAX_NUM_TAGS && !m_SendForwardKey) + NewNextSendRatchet (); return true; } - bool ECIESX25519AEADRatchetSession::HandleExistingSessionMessage (const uint8_t * buf, size_t len, int index) + bool ECIESX25519AEADRatchetSession::HandleExistingSessionMessage (uint8_t * buf, size_t len, + std::shared_ptr receiveTagset, int index) { uint8_t nonce[12]; CreateNonce (index, nonce); // tag's index - len -= 8; // tag - std::vector payload (len - 16); + len -= 8; // tag + uint8_t * payload = buf + 8; uint8_t key[32]; - m_ReceiveTagset.GetSymmKey (index, key); - if (!i2p::crypto::AEADChaCha20Poly1305 (buf + 8, len - 16, buf, 8, key, nonce, payload.data (), len - 16, false)) // decrypt + receiveTagset->GetSymmKey (index, key); + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 16, buf, 8, key, nonce, payload, len - 16, false)) // decrypt { LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed"); return false; - } - HandlePayload (payload.data (), len - 16, index); - if (m_ReceiveTagset.GetNextIndex () - index <= GetOwner ()->GetNumTags ()*2/3) - GenerateMoreReceiveTags (GetOwner ()->GetNumTags ()); + } + HandlePayload (payload, len - 16, receiveTagset, index); + int moreTags = ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 2); // N/4 + if (moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS; + moreTags -= (receiveTagset->GetNextIndex () - index); + if (moreTags > 0 && GetOwner ()) + GenerateMoreReceiveTags (receiveTagset, moreTags); return true; } - bool ECIESX25519AEADRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len, int index) + bool ECIESX25519AEADRatchetSession::HandleNextMessage (uint8_t * buf, size_t len, + std::shared_ptr receiveTagset, int index) { m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); switch (m_State) { case eSessionStateNewSessionReplySent: m_State = eSessionStateEstablished; + m_NSRTagset = nullptr; #if (__cplusplus >= 201703L) // C++ 17 or higher - [[fallthrough]]; -#endif + [[fallthrough]]; +#endif case eSessionStateEstablished: - return HandleExistingSessionMessage (buf, len, index); + if (HandleExistingSessionMessage (buf, len, receiveTagset, index)) return true; + // check NSR just in case + LogPrint (eLogDebug, "Garlic: check for out of order NSR with index ", index); + if (receiveTagset->GetNextIndex () - index < ECIESX25519_NSR_NUM_GENERATED_TAGS/2) + GenerateMoreReceiveTags (receiveTagset, ECIESX25519_NSR_NUM_GENERATED_TAGS); + return HandleNewOutgoingSessionReply (buf, len); case eSessionStateNew: return HandleNewIncomingSession (buf, len); case eSessionStateNewSessionSent: + receiveTagset->Expire (); // NSR tagset return HandleNewOutgoingSessionReply (buf, len); default: return false; @@ -452,113 +647,188 @@ namespace garlic return true; } - std::shared_ptr ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr msg) - { - auto m = NewI2NPMessage (); + std::shared_ptr ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr msg) + { + auto payload = CreatePayload (msg, m_State != eSessionStateEstablished); + size_t len = payload.size (); + if (!len) return nullptr; + auto m = NewI2NPMessage (len + 100); // 96 + 4 m->Align (12); // in order to get buf aligned to 16 (12 + 4) uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length - auto payload = CreatePayload (msg); - size_t len = payload.size (); - switch (m_State) - { + switch (m_State) + { case eSessionStateEstablished: if (!NewExistingSessionMessage (payload.data (), payload.size (), buf, m->maxLen)) return nullptr; len += 24; - break; - case eSessionStateNew: - if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen)) - return nullptr; - len += 96; - break; - case eSessionStateNewSessionReceived: - if (!NewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen)) - return nullptr; - len += 72; - break; + break; + case eSessionStateNew: + if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen)) + return nullptr; + len += 96; + break; + case eSessionStateNewSessionReceived: + if (!NewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen)) + return nullptr; + len += 72; + break; case eSessionStateNewSessionReplySent: if (!NextNewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen)) - return nullptr; - len += 72; - break; - default: - return nullptr; - } - - htobe32buf (m->GetPayload (), len); + return nullptr; + len += 72; + break; + default: + return nullptr; + } + + htobe32buf (m->GetPayload (), len); m->len += len + 4; m->FillI2NPMessageHeader (eI2NPGarlic); return m; - } + } - std::vector ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr msg) - { + std::vector ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr msg, bool first) + { uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); - size_t payloadLen = 7; // datatime - if (msg && m_Destination) - payloadLen += msg->GetPayloadLength () + 13 + 32; - auto leaseSet = (GetLeaseSetUpdateStatus () == eLeaseSetUpdated) ? CreateDatabaseStoreMsg (GetOwner ()->GetLeaseSet ()) : nullptr; - std::shared_ptr deliveryStatus; + size_t payloadLen = 0; + if (first) payloadLen += 7;// datatime + if (msg && m_Destination) + payloadLen += msg->GetPayloadLength () + 13 + 32; + auto leaseSet = (GetLeaseSetUpdateStatus () == eLeaseSetUpdated || + (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && + ts > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT)) ? + GetOwner ()->GetLeaseSet () : nullptr; if (leaseSet) - { - payloadLen += leaseSet->GetPayloadLength () + 13; - deliveryStatus = CreateEncryptedDeliveryStatusMsg (leaseSet->GetMsgID ()); - payloadLen += deliveryStatus->GetPayloadLength () + 49; - if (GetLeaseSetUpdateMsgID ()) GetOwner ()->RemoveDeliveryStatusSession (GetLeaseSetUpdateMsgID ()); // remove previous - SetLeaseSetUpdateStatus (eLeaseSetSubmitted); - SetLeaseSetUpdateMsgID (leaseSet->GetMsgID ()); - SetLeaseSetSubmissionTime (ts); - GetOwner ()->DeliveryStatusSent (shared_from_this (), leaseSet->GetMsgID ()); - } + { + payloadLen += leaseSet->GetBufferLen () + DATABASE_STORE_HEADER_SIZE + 13; + if (!first) + { + // ack request + SetLeaseSetUpdateStatus (eLeaseSetSubmitted); + SetLeaseSetUpdateMsgID (m_SendTagset->GetNextIndex ()); + SetLeaseSetSubmissionTime (ts); + payloadLen += 4; + } + } if (m_AckRequests.size () > 0) payloadLen += m_AckRequests.size ()*4 + 3; - uint8_t paddingSize; - RAND_bytes (&paddingSize, 1); - paddingSize &= 0x0F; paddingSize++; // 1 - 16 - payloadLen += paddingSize + 3; - std::vector v(payloadLen); - size_t offset = 0; - // DateTime - v[offset] = eECIESx25519BlkDateTime; offset++; - htobe16buf (v.data () + offset, 4); offset += 2; - htobe32buf (v.data () + offset, ts/1000); offset += 4; // in seconds - // LeaseSet - if (leaseSet) - offset += CreateGarlicClove (leaseSet, v.data () + offset, payloadLen - offset); - // DeliveryStatus - if (deliveryStatus) - offset += CreateDeliveryStatusClove (deliveryStatus, v.data () + offset, payloadLen - offset); - // msg - if (msg && m_Destination) - offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset, true); + if (m_SendReverseKey) + { + payloadLen += 6; + if (m_NextReceiveRatchet->newKey) payloadLen += 32; + } + if (m_SendForwardKey) + { + payloadLen += 6; + if (m_NextSendRatchet->newKey) payloadLen += 32; + } + uint8_t paddingSize = 0; + if (payloadLen) + { + int delta = (int)ECIESX25519_OPTIMAL_PAYLOAD_SIZE - (int)payloadLen; + if (delta < 0 || delta > 3) // don't create padding if we are close to optimal size + { + RAND_bytes (&paddingSize, 1); + paddingSize &= 0x0F; // 0 - 15 + if (delta > 3) + { + delta -= 3; + if (paddingSize >= delta) paddingSize %= delta; + } + paddingSize++; + payloadLen += paddingSize + 3; + } + } + std::vector v(payloadLen); + size_t offset = 0; + // DateTime + if (first) + { + v[offset] = eECIESx25519BlkDateTime; offset++; + htobe16buf (v.data () + offset, 4); offset += 2; + htobe32buf (v.data () + offset, ts/1000); offset += 4; // in seconds + } + // LeaseSet + if (leaseSet) + { + offset += CreateLeaseSetClove (leaseSet, ts, v.data () + offset, payloadLen - offset); + if (!first) + { + // ack request + v[offset] = eECIESx25519BlkAckRequest; offset++; + htobe16buf (v.data () + offset, 1); offset += 2; + v[offset] = 0; offset++; // flags + } + } + // msg + if (msg && m_Destination) + offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset, true); // ack if (m_AckRequests.size () > 0) { v[offset] = eECIESx25519BlkAck; offset++; - htobe16buf (v.data () + offset, m_AckRequests.size ()*4); offset += 2; + htobe16buf (v.data () + offset, m_AckRequests.size () * 4); offset += 2; for (auto& it: m_AckRequests) { htobe16buf (v.data () + offset, it.first); offset += 2; htobe16buf (v.data () + offset, it.second); offset += 2; - } + } m_AckRequests.clear (); - } - // padding - v[offset] = eECIESx25519BlkPadding; offset++; - htobe16buf (v.data () + offset, paddingSize); offset += 2; - memset (v.data () + offset, 0, paddingSize); offset += paddingSize; - return v; - } + } + // next keys + if (m_SendReverseKey) + { + v[offset] = eECIESx25519BlkNextKey; offset++; + htobe16buf (v.data () + offset, m_NextReceiveRatchet->newKey ? 35 : 3); offset += 2; + v[offset] = ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG; + int keyID = m_NextReceiveRatchet->keyID - 1; + if (m_NextReceiveRatchet->newKey) + { + v[offset] |= ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG; + keyID++; + } + offset++; // flag + htobe16buf (v.data () + offset, keyID); offset += 2; // keyid + if (m_NextReceiveRatchet->newKey) + { + memcpy (v.data () + offset, m_NextReceiveRatchet->key.GetPublicKey (), 32); + offset += 32; // public key + } + m_SendReverseKey = false; + } + if (m_SendForwardKey) + { + v[offset] = eECIESx25519BlkNextKey; offset++; + htobe16buf (v.data () + offset, m_NextSendRatchet->newKey ? 35 : 3); offset += 2; + v[offset] = m_NextSendRatchet->newKey ? ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG : ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; + if (!m_NextSendRatchet->keyID) v[offset] |= ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; // for first key only + offset++; // flag + htobe16buf (v.data () + offset, m_NextSendRatchet->keyID); offset += 2; // keyid + if (m_NextSendRatchet->newKey) + { + memcpy (v.data () + offset, m_NextSendRatchet->key.GetPublicKey (), 32); + offset += 32; // public key + } + } + // padding + if (paddingSize) + { + v[offset] = eECIESx25519BlkPadding; offset++; + htobe16buf (v.data () + offset, paddingSize); offset += 2; + memset (v.data () + offset, 0, paddingSize); offset += paddingSize; + } + return v; + } - size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr msg, uint8_t * buf, size_t len, bool isDestination) - { - if (!msg) return 0; - uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1; + size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr msg, uint8_t * buf, size_t len, bool isDestination) + { + if (!msg) return 0; + uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1; if (isDestination) cloveSize += 32; - if ((int)len < cloveSize + 3) return 0; - buf[0] = eECIESx25519BlkGalicClove; // clove type - htobe16buf (buf + 1, cloveSize); // size + if ((int)len < cloveSize + 3) return 0; + buf[0] = eECIESx25519BlkGalicClove; // clove type + htobe16buf (buf + 1, cloveSize); // size buf += 3; if (isDestination) { @@ -566,63 +836,52 @@ namespace garlic memcpy (buf + 1, *m_Destination, 32); buf += 32; } else - *buf = 0; + *buf = 0; buf++; // flag and delivery instructions - *buf = msg->GetTypeID (); // I2NP msg type - htobe32buf (buf + 1, msg->GetMsgID ()); // msgID - htobe32buf (buf + 5, msg->GetExpiration ()/1000); // expiration in seconds - memcpy (buf + 9, msg->GetPayload (), msg->GetPayloadLength ()); - return cloveSize + 3; - } + *buf = msg->GetTypeID (); // I2NP msg type + htobe32buf (buf + 1, msg->GetMsgID ()); // msgID + htobe32buf (buf + 5, msg->GetExpiration () / 1000); // expiration in seconds + memcpy (buf + 9, msg->GetPayload (), msg->GetPayloadLength ()); + return cloveSize + 3; + } - size_t ECIESX25519AEADRatchetSession::CreateDeliveryStatusClove (std::shared_ptr msg, uint8_t * buf, size_t len) - { - uint16_t cloveSize = msg->GetPayloadLength () + 9 + 37 /* delivery instruction */; + size_t ECIESX25519AEADRatchetSession::CreateLeaseSetClove (std::shared_ptr ls, uint64_t ts, uint8_t * buf, size_t len) + { + if (!ls || ls->GetStoreType () != i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2) + { + LogPrint (eLogError, "Garlic: Incorrect LeasetSet type to send"); + return 0; + } + uint16_t cloveSize = 1 + 9 + DATABASE_STORE_HEADER_SIZE + ls->GetBufferLen (); // to local if ((int)len < cloveSize + 3) return 0; buf[0] = eECIESx25519BlkGalicClove; // clove type - htobe16buf (buf + 1, cloveSize); // size + htobe16buf (buf + 1, cloveSize); // size buf += 3; - if (GetOwner ()) - { - auto inboundTunnel = GetOwner ()->GetTunnelPool ()->GetNextInboundTunnel (); - if (inboundTunnel) - { - // delivery instructions - *buf = eGarlicDeliveryTypeTunnel << 5; buf++; // delivery instructions flag tunnel - // hash and tunnelID sequence is reversed for Garlic - memcpy (buf, inboundTunnel->GetNextIdentHash (), 32); buf += 32;// To Hash - htobe32buf (buf, inboundTunnel->GetNextTunnelID ()); buf += 4;// tunnelID - } - else - { - LogPrint (eLogError, "Garlic: No inbound tunnels in the pool for DeliveryStatus"); - return 0; - } - *buf = msg->GetTypeID (); // I2NP msg type - htobe32buf (buf + 1, msg->GetMsgID ()); // msgID - htobe32buf (buf + 5, msg->GetExpiration ()/1000); // expiration in seconds - memcpy (buf + 9, msg->GetPayload (), msg->GetPayloadLength ()); - } - else - return 0; + *buf = 0; buf++; // flag and delivery instructions + *buf = eI2NPDatabaseStore; buf++; // I2NP msg type + RAND_bytes (buf, 4); buf += 4; // msgID + htobe32buf (buf, (ts + I2NP_MESSAGE_EXPIRATION_TIMEOUT)/1000); buf += 4; // expiration + // payload + memcpy (buf + DATABASE_STORE_KEY_OFFSET, ls->GetStoreHash (), 32); + buf[DATABASE_STORE_TYPE_OFFSET] = i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2; + memset (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0, 4); // replyToken = 0 + buf += DATABASE_STORE_HEADER_SIZE; + memcpy (buf, ls->GetBuffer (), ls->GetBufferLen ()); + return cloveSize + 3; - } - - void ECIESX25519AEADRatchetSession::GenerateMoreReceiveTags (int numTags) + } + + void ECIESX25519AEADRatchetSession::GenerateMoreReceiveTags (std::shared_ptr receiveTagset, int numTags) { for (int i = 0; i < numTags; i++) - { - auto index = m_ReceiveTagset.GetNextIndex (); - uint64_t tag = m_ReceiveTagset.GetNextSessionTag (); - GetOwner ()->AddECIESx25519SessionTag (index, tag, shared_from_this ()); - } - } + GetOwner ()->AddECIESx25519SessionNextTag (receiveTagset); + } bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts) - { + { CleanupUnconfirmedLeaseSet (ts); - return ts > m_LastActivityTimestamp + ECIESX25519_EXPIRATION_TIMEOUT; - } + return ts > m_LastActivityTimestamp + ECIESX25519_EXPIRATION_TIMEOUT; + } std::shared_ptr WrapECIESX25519AEADRatchetMessage (std::shared_ptr msg, const uint8_t * key, uint64_t tag) { @@ -630,35 +889,33 @@ namespace garlic m->Align (12); // in order to get buf aligned to 16 (12 + 4) uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length uint8_t nonce[12]; - memset (nonce, 0, 12); // n = 0 + memset (nonce, 0, 12); // n = 0 size_t offset = 0; memcpy (buf + offset, &tag, 8); offset += 8; auto payload = buf + offset; uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1; size_t len = cloveSize + 3; - payload[0] = eECIESx25519BlkGalicClove; // clove type - htobe16buf (payload + 1, cloveSize); // size + payload[0] = eECIESx25519BlkGalicClove; // clove type + htobe16buf (payload + 1, cloveSize); // size payload += 3; - *payload = 0; payload++; // flag and delivery instructions - *payload = msg->GetTypeID (); // I2NP msg type - htobe32buf (payload + 1, msg->GetMsgID ()); // msgID - htobe32buf (payload + 5, msg->GetExpiration ()/1000); // expiration in seconds - memcpy (payload + 9, msg->GetPayload (), msg->GetPayloadLength ()); + *payload = 0; payload++; // flag and delivery instructions + *payload = msg->GetTypeID (); // I2NP msg type + htobe32buf (payload + 1, msg->GetMsgID ()); // msgID + htobe32buf (payload + 5, msg->GetExpiration () / 1000); // expiration in seconds + memcpy (payload + 9, msg->GetPayload (), msg->GetPayloadLength ()); if (!i2p::crypto::AEADChaCha20Poly1305 (buf + offset, len, buf, 8, key, nonce, buf + offset, len + 16, true)) // encrypt { LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); return nullptr; - } + } offset += len + 16; - + htobe32buf (m->GetPayload (), offset); m->len += offset + 4; m->FillI2NPMessageHeader (eI2NPGarlic); return m; } - + } } - - diff --git a/libi2pd/ECIESX25519AEADRatchetSession.h b/libi2pd/ECIESX25519AEADRatchetSession.h index 3185a7fc..108788d5 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.h +++ b/libi2pd/ECIESX25519AEADRatchetSession.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef ECIES_X25519_AEAD_RATCHET_SESSION_H__ #define ECIES_X25519_AEAD_RATCHET_SESSION_H__ @@ -7,127 +15,177 @@ #include #include #include +#include #include "Identity.h" #include "Crypto.h" #include "Garlic.h" +#include "Tag.h" namespace i2p { namespace garlic { - class RatchetTagSet - { - public: - - void DHInitialize (const uint8_t * rootKey, const uint8_t * k); - void NextSessionTagRatchet (); - uint64_t GetNextSessionTag (); - int GetNextIndex () const { return m_NextIndex; }; + const int ECIESX25519_RESTART_TIMEOUT = 120; // number of second since session creation we can restart session after + const int ECIESX25519_EXPIRATION_TIMEOUT = 480; // in seconds + const int ECIESX25519_INACTIVITY_TIMEOUT = 90; // number of second we receive nothing and should restart if we can + const int ECIESX25519_INCOMING_TAGS_EXPIRATION_TIMEOUT = 600; // in seconds + const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180 + const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 4096; // number of tags we request new tagset after + const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24; + const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 160; + const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12; + + const size_t ECIESX25519_OPTIMAL_PAYLOAD_SIZE = 1912; // 1912 = 1956 /* to fit 2 tunnel messages */ + // - 16 /* I2NP header */ - 16 /* poly hash */ - 8 /* tag */ - 4 /* garlic length */ + + class ECIESX25519AEADRatchetSession; + class RatchetTagSet + { + public: + + RatchetTagSet (std::shared_ptr session): m_Session (session) {}; + + void DHInitialize (const uint8_t * rootKey, const uint8_t * k); + void NextSessionTagRatchet (); + uint64_t GetNextSessionTag (); + const uint8_t * GetNextRootKey () const { return m_NextRootKey; }; + int GetNextIndex () const { return m_NextIndex; }; void GetSymmKey (int index, uint8_t * key); - private: + std::shared_ptr GetSession () { return m_Session.lock (); }; + int GetTagSetID () const { return m_TagSetID; }; + void SetTagSetID (int tagsetID) { m_TagSetID = tagsetID; }; + + void Expire (); + bool IsExpired (uint64_t ts) const { return m_ExpirationTimestamp && ts > m_ExpirationTimestamp; }; - void CalculateSymmKeyCK (int index, uint8_t * key); - private: - - union - { - uint64_t ll[8]; - uint8_t buf[64]; - const uint8_t * GetSessTagCK () const { return buf; }; // sessTag_chainKey = keydata[0:31] - const uint8_t * GetSessTagConstant () const { return buf + 32; }; // SESSTAG_CONSTANT = keydata[32:63] - uint64_t GetTag () const { return ll[4]; }; // tag = keydata[32:39] - - } m_KeyData; - uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64]; - int m_NextIndex, m_NextSymmKeyIndex; - }; + union + { + uint64_t ll[8]; + uint8_t buf[64]; - enum ECIESx25519BlockType + const uint8_t * GetSessTagCK () const { return buf; }; // sessTag_chainKey = keydata[0:31] + const uint8_t * GetSessTagConstant () const { return buf + 32; }; // SESSTAG_CONSTANT = keydata[32:63] + uint64_t GetTag () const { return ll[4]; }; // tag = keydata[32:39] + + } m_KeyData; + uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64], m_NextRootKey[32]; + int m_NextIndex, m_NextSymmKeyIndex; + std::unordered_map > m_ItermediateSymmKeys; + std::weak_ptr m_Session; + int m_TagSetID = 0; + uint64_t m_ExpirationTimestamp = 0; + }; + + enum ECIESx25519BlockType { - eECIESx25519BlkDateTime = 0, - eECIESx25519BlkSessionID = 1, + eECIESx25519BlkDateTime = 0, + eECIESx25519BlkSessionID = 1, eECIESx25519BlkTermination = 4, - eECIESx25519BlkOptions = 5, - eECIESx25519BlkNextSessionKey = 7, - eECIESx25519BlkAck = 8, - eECIESx25519BlkAckRequest = 9, - eECIESx25519BlkGalicClove = 11, - eECIESx25519BlkPadding = 254 - }; + eECIESx25519BlkOptions = 5, + eECIESx25519BlkNextKey = 7, + eECIESx25519BlkAck = 8, + eECIESx25519BlkAckRequest = 9, + eECIESx25519BlkGalicClove = 11, + eECIESx25519BlkPadding = 254 + }; + const uint8_t ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG = 0x01; + const uint8_t ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG = 0x02; + const uint8_t ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG = 0x04; - const int ECIESX25519_RESTART_TIMEOUT = 120; // number of second of inactivity we should restart after - const int ECIESX25519_EXPIRATION_TIMEOUT = 600; // in seconds - - class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, public std::enable_shared_from_this - { - enum SessionState - { - eSessionStateNew =0, - eSessionStateNewSessionReceived, + class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, public std::enable_shared_from_this + { + enum SessionState + { + eSessionStateNew = 0, + eSessionStateNewSessionReceived, eSessionStateNewSessionSent, eSessionStateNewSessionReplySent, - eSessionStateEstablished - }; + eSessionStateEstablished + }; - public: + struct DHRatchet + { + int keyID = 0; + i2p::crypto::X25519Keys key; + uint8_t remote[32]; // last remote public key + bool newKey = true; + }; - ECIESX25519AEADRatchetSession (GarlicDestination * owner); - ~ECIESX25519AEADRatchetSession (); + public: - bool HandleNextMessage (const uint8_t * buf, size_t len, int index = 0); - std::shared_ptr WrapSingleMessage (std::shared_ptr msg); + ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSet); + ~ECIESX25519AEADRatchetSession (); - const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; } + bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr receiveTagset, int index = 0); + std::shared_ptr WrapSingleMessage (std::shared_ptr msg); + + const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; } void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); } void SetDestination (const i2p::data::IdentHash& dest) // TODO: { if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest)); } - - bool CheckExpired (uint64_t ts); // true is expired - bool CanBeRestarted (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_RESTART_TIMEOUT; } - private: + bool CheckExpired (uint64_t ts); // true is expired + bool CanBeRestarted (uint64_t ts) const { return ts > m_SessionCreatedTimestamp + ECIESX25519_RESTART_TIMEOUT; } + bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); } + + bool IsRatchets () const { return true; }; + + private: void ResetKeys (); - void MixHash (const uint8_t * buf, size_t len); + void MixHash (const uint8_t * buf, size_t len); void CreateNonce (uint64_t seqn, uint8_t * nonce); - bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes - uint64_t CreateNewSessionTag () const; + bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes + std::shared_ptr CreateNewSessionTagset (); bool HandleNewIncomingSession (const uint8_t * buf, size_t len); - bool HandleNewOutgoingSessionReply (const uint8_t * buf, size_t len); - bool HandleExistingSessionMessage (const uint8_t * buf, size_t len, int index); - void HandlePayload (const uint8_t * buf, size_t len, int index = 0); + bool HandleNewOutgoingSessionReply (uint8_t * buf, size_t len); + bool HandleExistingSessionMessage (uint8_t * buf, size_t len, std::shared_ptr receiveTagset, int index); + void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr& receiveTagset, int index); + void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr& receiveTagset); - bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); - bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); + bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); + bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); bool NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); - - std::vector CreatePayload (std::shared_ptr msg); - size_t CreateGarlicClove (std::shared_ptr msg, uint8_t * buf, size_t len, bool isDestination = false); - size_t CreateDeliveryStatusClove (std::shared_ptr msg, uint8_t * buf, size_t len); - void GenerateMoreReceiveTags (int numTags); - - private: + std::vector CreatePayload (std::shared_ptr msg, bool first); + size_t CreateGarlicClove (std::shared_ptr msg, uint8_t * buf, size_t len, bool isDestination = false); + size_t CreateLeaseSetClove (std::shared_ptr ls, uint64_t ts, uint8_t * buf, size_t len); - uint8_t m_H[32], m_CK[64] /* [chainkey, key] */, m_RemoteStaticKey[32]; + void GenerateMoreReceiveTags (std::shared_ptr receiveTagset, int numTags); + void NewNextSendRatchet (); + + private: + + uint8_t m_H[32], m_CK[64] /* [chainkey, key] */, m_RemoteStaticKey[32]; uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only - uint8_t m_NSRHeader[56], m_NSRKey[32]; // new session reply, for incoming only - i2p::crypto::X25519Keys m_EphemeralKeys; - SessionState m_State = eSessionStateNew; - uint64_t m_LastActivityTimestamp = 0; // incoming - RatchetTagSet m_SendTagset, m_ReceiveTagset; - std::unique_ptr m_Destination;// TODO: might not need it + uint8_t m_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only + i2p::crypto::X25519Keys m_EphemeralKeys; + SessionState m_State = eSessionStateNew; + uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0; // incoming + std::shared_ptr m_SendTagset, m_NSRTagset; + std::unique_ptr m_Destination;// TODO: might not need it std::list > m_AckRequests; // (tagsetid, index) - }; + bool m_SendReverseKey = false, m_SendForwardKey = false; + std::unique_ptr m_NextReceiveRatchet, m_NextSendRatchet; + + public: + + // for HTTP only + int GetState () const { return (int)m_State; } + i2p::data::IdentHash GetDestination () const + { + return m_Destination ? *m_Destination : i2p::data::IdentHash (); + } + }; std::shared_ptr WrapECIESX25519AEADRatchetMessage (std::shared_ptr msg, const uint8_t * key, uint64_t tag); } diff --git a/libi2pd/Ed25519.cpp b/libi2pd/Ed25519.cpp index 69fec4e8..791bd685 100644 --- a/libi2pd/Ed25519.cpp +++ b/libi2pd/Ed25519.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include "Log.h" #include "Crypto.h" @@ -121,7 +129,7 @@ namespace crypto return passed; } - void Ed25519::Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, + void Ed25519::Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const { BN_CTX * bnCtx = BN_CTX_new (); @@ -153,7 +161,7 @@ namespace crypto BN_CTX_free (bnCtx); } - void Ed25519::SignRedDSA (const uint8_t * privateKey, const uint8_t * publicKeyEncoded, + void Ed25519::SignRedDSA (const uint8_t * privateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const { BN_CTX * bnCtx = BN_CTX_new (); @@ -164,16 +172,16 @@ namespace crypto SHA512_CTX ctx; SHA512_Init (&ctx); SHA512_Update (&ctx, T, 80); - SHA512_Update (&ctx, publicKeyEncoded, 32); + SHA512_Update (&ctx, publicKeyEncoded, 32); SHA512_Update (&ctx, buf, len); // data uint8_t digest[64]; SHA512_Final (digest, &ctx); - BIGNUM * r = DecodeBN<64> (digest); - BN_mod (r, r, l, bnCtx); // % l + BIGNUM * r = DecodeBN<64> (digest); + BN_mod (r, r, l, bnCtx); // % l EncodeBN (r, digest, 32); // calculate R uint8_t R[EDDSA25519_SIGNATURE_LENGTH/2]; // we must use separate buffer because signature might be inside buf - EncodePoint (Normalize (MulB (digest, bnCtx), bnCtx), R); + EncodePoint (Normalize (MulB (digest, bnCtx), bnCtx), R); // calculate S SHA512_Init (&ctx); SHA512_Update (&ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R @@ -182,7 +190,7 @@ namespace crypto SHA512_Final (digest, &ctx); BIGNUM * h = DecodeBN<64> (digest); // S = (r + h*a) % l - BIGNUM * a = DecodeBN (privateKey); + BIGNUM * a = DecodeBN (privateKey); BN_mod_mul (h, h, a, l, bnCtx); // %l BN_mod_add (h, h, r, l, bnCtx); // %l memcpy (signature, R, EDDSA25519_SIGNATURE_LENGTH/2); @@ -190,7 +198,7 @@ namespace crypto BN_free (r); BN_free (h); BN_free (a); BN_CTX_free (bnCtx); } - + EDDSAPoint Ed25519::Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const { // x3 = (x1*y2+y1*x2)*(z1*z2-d*t1*t2) @@ -467,7 +475,7 @@ namespace crypto --bits; auto k_t = BN_is_bit_set(k, bits) ? 1 : 0; swap ^= k_t; - if (swap) + if (swap) { std::swap (x2, x3); std::swap (z2, z3); @@ -492,7 +500,7 @@ namespace crypto BN_mod_mul(z3, x1, z2, q, ctx); BN_mod_mul(z2, tmp1, tmp0, q, ctx); } - if (swap) + if (swap) { std::swap (x2, x3); std::swap (z2, z3); @@ -533,9 +541,9 @@ namespace crypto { BN_CTX * ctx = BN_CTX_new (); // calculate alpha = seed mod l - BIGNUM * alpha = DecodeBN<64> (seed); // seed is in Little Endian + BIGNUM * alpha = DecodeBN<64> (seed); // seed is in Little Endian BN_mod (alpha, alpha, l, ctx); // % l - uint8_t priv[32]; + uint8_t priv[32]; EncodeBN (alpha, priv, 32); // back to Little Endian BN_free (alpha); // A' = BLIND_PUBKEY(A, alpha) = A + DERIVE_PUBLIC(alpha) @@ -548,16 +556,16 @@ namespace crypto { BN_CTX * ctx = BN_CTX_new (); // calculate alpha = seed mod l - BIGNUM * alpha = DecodeBN<64> (seed); // seed is in Little Endian + BIGNUM * alpha = DecodeBN<64> (seed); // seed is in Little Endian BN_mod (alpha, alpha, l, ctx); // % l - BIGNUM * p = DecodeBN<32> (priv); // priv is in Little Endian + BIGNUM * p = DecodeBN<32> (priv); // priv is in Little Endian BN_add (alpha, alpha, p); // alpha = alpha + priv - // a' = BLIND_PRIVKEY(a, alpha) = (a + alpha) mod L + // a' = BLIND_PRIVKEY(a, alpha) = (a + alpha) mod L BN_mod (alpha, alpha, l, ctx); // % l EncodeBN (alpha, blindedPriv, 32); - // A' = DERIVE_PUBLIC(a') + // A' = DERIVE_PUBLIC(a') auto A1 = MulB (blindedPriv, ctx); - EncodePublicKey (A1, blindedPub, ctx); + EncodePublicKey (A1, blindedPub, ctx); BN_free (alpha); BN_free (p); BN_CTX_free (ctx); } @@ -574,14 +582,14 @@ namespace crypto { uint8_t seed[32]; RAND_bytes (seed, 32); - BIGNUM * p = DecodeBN<32> (seed); + BIGNUM * p = DecodeBN<32> (seed); BN_CTX * ctx = BN_CTX_new (); BN_mod (p, p, l, ctx); // % l - EncodeBN (p, priv, 32); + EncodeBN (p, priv, 32); BN_CTX_free (ctx); BN_free (p); - } - + } + static std::unique_ptr g_Ed25519; std::unique_ptr& GetEd25519 () { @@ -597,4 +605,3 @@ namespace crypto } } } - diff --git a/libi2pd/Ed25519.h b/libi2pd/Ed25519.h index 84501b03..28d4e930 100644 --- a/libi2pd/Ed25519.h +++ b/libi2pd/Ed25519.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef ED25519_H__ #define ED25519_H__ @@ -86,10 +94,10 @@ namespace crypto bool Verify (const EDDSAPoint& publicKey, const uint8_t * digest, const uint8_t * signature) const; void Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const; void SignRedDSA (const uint8_t * privateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const; - + static void ExpandPrivateKey (const uint8_t * key, uint8_t * expandedKey); // key - 32 bytes, expandedKey - 64 bytes void CreateRedDSAPrivateKey (uint8_t * priv); // priv is 32 bytes - + private: EDDSAPoint Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const; @@ -97,8 +105,8 @@ namespace crypto EDDSAPoint Mul (const EDDSAPoint& p, const BIGNUM * e, BN_CTX * ctx) const; EDDSAPoint MulB (const uint8_t * e, BN_CTX * ctx) const; // B*e, e is 32 bytes Little Endian EDDSAPoint Normalize (const EDDSAPoint& p, BN_CTX * ctx) const; - - bool IsOnCurve (const EDDSAPoint& p, BN_CTX * ctx) const; + + bool IsOnCurve (const EDDSAPoint& p, BN_CTX * ctx) const; BIGNUM * RecoverX (const BIGNUM * y, BN_CTX * ctx) const; EDDSAPoint DecodePoint (const uint8_t * buf, BN_CTX * ctx) const; void EncodePoint (const EDDSAPoint& p, uint8_t * buf) const; @@ -109,7 +117,7 @@ namespace crypto #if !OPENSSL_X25519 // for x25519 - BIGNUM * ScalarMul (const BIGNUM * p, const BIGNUM * e, BN_CTX * ctx) const; + BIGNUM * ScalarMul (const BIGNUM * p, const BIGNUM * e, BN_CTX * ctx) const; #endif private: @@ -121,13 +129,11 @@ namespace crypto // if j > 128 we use 256 - j and carry 1 to next byte // Bi256[0][0] = B, base point EDDSAPoint Bi256Carry; // Bi256[32][0] - }; + }; std::unique_ptr& GetEd25519 (); } } - #endif - diff --git a/libi2pd/Elligator.cpp b/libi2pd/Elligator.cpp index 48a5a7ac..712e514a 100644 --- a/libi2pd/Elligator.cpp +++ b/libi2pd/Elligator.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include "Crypto.h" #include "Elligator.h" @@ -6,7 +14,7 @@ namespace i2p { namespace crypto { - + Elligator2::Elligator2 () { // TODO: share with Ed22519 @@ -21,32 +29,32 @@ namespace crypto A = BN_new (); BN_set_word (A, 486662); nA = BN_new (); BN_sub (nA, p, A); - BN_CTX * ctx = BN_CTX_new (); - // calculate sqrt(-1) + BN_CTX * ctx = BN_CTX_new (); + // calculate sqrt(-1) sqrtn1 = BN_new (); - BN_set_word (sqrtn1, 2); + BN_set_word (sqrtn1, 2); BN_mod_exp (sqrtn1, sqrtn1, p14, p, ctx); // 2^((p-1)/4 - + u = BN_new (); BN_set_word (u, 2); - iu = BN_new (); BN_mod_inverse (iu, u, p, ctx); - + iu = BN_new (); BN_mod_inverse (iu, u, p, ctx); + BN_CTX_free (ctx); } Elligator2::~Elligator2 () { BN_free (p); BN_free (p38); BN_free (p12); BN_free (p14); - BN_free (sqrtn1); BN_free (A); BN_free (nA); - BN_free (u); BN_free (iu); + BN_free (sqrtn1); BN_free (A); BN_free (nA); + BN_free (u); BN_free (iu); } bool Elligator2::Encode (const uint8_t * key, uint8_t * encoded, bool highY, bool random) const - { + { bool ret = true; BN_CTX * ctx = BN_CTX_new (); BN_CTX_start (ctx); - uint8_t key1[32]; + uint8_t key1[32]; for (size_t i = 0; i < 16; i++) // from Little Endian { key1[i] = key[31 - i]; @@ -59,35 +67,35 @@ namespace crypto BIGNUM * uxxA = BN_CTX_get (ctx); // u*x*xA BN_mod_mul (uxxA, u, x, p, ctx); - BN_mod_mul (uxxA, uxxA, xA, p, ctx); - + BN_mod_mul (uxxA, uxxA, xA, p, ctx); + if (Legendre (uxxA, ctx) != -1) - { - uint8_t randByte = 0; // random highest bits and high y + { + uint8_t randByte = 0; // random highest bits and high y if (random) - { - RAND_bytes (&randByte, 1); - highY = randByte & 0x01; + { + RAND_bytes (&randByte, 1); + highY = randByte & 0x01; } - + BIGNUM * r = BN_CTX_get (ctx); if (highY) { BN_mod_inverse (r, x, p, ctx); - BN_mod_mul (r, r, xA, p, ctx); + BN_mod_mul (r, r, xA, p, ctx); } else { - BN_mod_inverse (r, xA, p, ctx); + BN_mod_inverse (r, xA, p, ctx); BN_mod_mul (r, r, x, p, ctx); - } - BN_mod_mul (r, r, iu, p, ctx); - + } + BN_mod_mul (r, r, iu, p, ctx); + SquareRoot (r, r, ctx); bn2buf (r, encoded, 32); if (random) - encoded[0] |= (randByte & 0xC0); // copy two highest bits from randByte + encoded[0] |= (randByte & 0xC0); // copy two highest bits from randByte for (size_t i = 0; i < 16; i++) // To Little Endian { uint8_t tmp = encoded[i]; @@ -98,7 +106,7 @@ namespace crypto else ret = false; - BN_CTX_end (ctx); + BN_CTX_end (ctx); BN_CTX_free (ctx); return ret; } @@ -109,31 +117,31 @@ namespace crypto BN_CTX * ctx = BN_CTX_new (); BN_CTX_start (ctx); - uint8_t encoded1[32]; + uint8_t encoded1[32]; for (size_t i = 0; i < 16; i++) // from Little Endian { encoded1[i] = encoded[31 - i]; encoded1[31 - i] = encoded[i]; } - encoded1[0] &= 0x3F; // drop two highest bits + encoded1[0] &= 0x3F; // drop two highest bits BIGNUM * r = BN_CTX_get (ctx); BN_bin2bn (encoded1, 32, r); if (BN_cmp (r, p12) <= 0) // r < (p-1)/2 { // v = -A/(1+u*r^2) - BIGNUM * v = BN_CTX_get (ctx); BN_mod_sqr (v, r, p, ctx); + BIGNUM * v = BN_CTX_get (ctx); BN_mod_sqr (v, r, p, ctx); BN_mod_mul (v, v, u, p, ctx); BN_add_word (v, 1); - BN_mod_inverse (v, v, p, ctx); + BN_mod_inverse (v, v, p, ctx); BN_mod_mul (v, v, nA, p, ctx); BIGNUM * vpA = BN_CTX_get (ctx); BN_add (vpA, v, A); // v + A // t = v^3+A*v^2+v = v^2*(v+A)+v - BIGNUM * t = BN_CTX_get (ctx); BN_mod_sqr (t, v, p, ctx); - BN_mod_mul (t, t, vpA, p, ctx); - BN_mod_add (t, t, v, p, ctx); + BIGNUM * t = BN_CTX_get (ctx); BN_mod_sqr (t, v, p, ctx); + BN_mod_mul (t, t, vpA, p, ctx); + BN_mod_add (t, t, v, p, ctx); int legendre = Legendre (t, ctx); BIGNUM * x = BN_CTX_get (ctx); @@ -143,9 +151,9 @@ namespace crypto { BN_sub (x, p, v); BN_mod_sub (x, x, A, p, ctx); - } - - bn2buf (x, key, 32); + } + + bn2buf (x, key, 32); for (size_t i = 0; i < 16; i++) // To Little Endian { uint8_t tmp = key[i]; @@ -156,7 +164,7 @@ namespace crypto else ret = false; - BN_CTX_end (ctx); + BN_CTX_end (ctx); BN_CTX_free (ctx); return ret; @@ -170,21 +178,21 @@ namespace crypto BN_add_word (t, 1); if (!BN_cmp (t, p)) - BN_mod_mul (r, r, sqrtn1, p, ctx); + BN_mod_mul (r, r, sqrtn1, p, ctx); if (BN_cmp (r, p12) > 0) // r > (p-1)/2 BN_sub (r, p, r); - } - + } + int Elligator2::Legendre (const BIGNUM * a, BN_CTX * ctx) const { // assume a < p, so don't check for a % p = 0, but a = 0 only - if (BN_is_zero(a)) return 0; + if (BN_is_zero(a)) return 0; BIGNUM * r = BN_CTX_get (ctx); BN_mod_exp (r, a, p12, p, ctx); // r = a^((p-1)/2) mod p - if (BN_is_word(r, 1)) + if (BN_is_word(r, 1)) return 1; - else if (BN_is_zero(r)) + else if (BN_is_zero(r)) return 0; return -1; } @@ -204,4 +212,3 @@ namespace crypto } } } - diff --git a/libi2pd/Elligator.h b/libi2pd/Elligator.h index 7cdcbbfe..eacb03cd 100644 --- a/libi2pd/Elligator.h +++ b/libi2pd/Elligator.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef ELLIGATOR_H__ #define ELLIGATOR_H__ @@ -20,11 +28,11 @@ namespace crypto bool Encode (const uint8_t * key, uint8_t * encoded, bool highY = false, bool random = true) const; bool Decode (const uint8_t * encoded, uint8_t * key) const; - private: + private: void SquareRoot (const BIGNUM * x, BIGNUM * r, BN_CTX * ctx) const; int Legendre (const BIGNUM * a, BN_CTX * ctx) const; // a/p - + private: BIGNUM * p, * p38, * p12, * p14, * sqrtn1, * A, * nA, * u, * iu; @@ -35,5 +43,3 @@ namespace crypto } #endif - - diff --git a/libi2pd/FS.cpp b/libi2pd/FS.cpp index f8b3ed7e..32fc3ec0 100644 --- a/libi2pd/FS.cpp +++ b/libi2pd/FS.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2016, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * diff --git a/libi2pd/FS.h b/libi2pd/FS.h index 6f112218..698e9b6b 100644 --- a/libi2pd/FS.h +++ b/libi2pd/FS.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2016, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -17,133 +17,136 @@ namespace i2p { namespace fs { - extern std::string dirSep; + extern std::string dirSep; - /** - * @brief Class to work with NetDb & Router profiles - * - * Usage: - * - * const char alphabet[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}; - * auto h = HashedStorage("name", "y", "z-", ".txt"); - * h.SetPlace("/tmp/hs-test"); - * h.GetName() -> gives "name" - * h.GetRoot() -> gives "/tmp/hs-test/name" - * h.Init(alphabet, 8); <- creates needed dirs, 8 is size of alphabet - * h.Path("abcd"); <- returns /tmp/hs-test/name/ya/z-abcd.txt - * h.Remove("abcd"); <- removes /tmp/hs-test/name/ya/z-abcd.txt, if it exists - * std::vector files; - * h.Traverse(files); <- finds all files in storage and saves in given vector - */ - class HashedStorage { - protected: - std::string root; /**< path to storage with it's name included */ - std::string name; /**< name of the storage */ - std::string prefix1; /**< hashed directory prefix */ - std::string prefix2; /**< prefix of file in storage */ - std::string suffix; /**< suffix of file in storage (extension) */ + /** + * @brief Class to work with NetDb & Router profiles + * + * Usage: + * + * const char alphabet[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}; + * auto h = HashedStorage("name", "y", "z-", ".txt"); + * h.SetPlace("/tmp/hs-test"); + * h.GetName() -> gives "name" + * h.GetRoot() -> gives "/tmp/hs-test/name" + * h.Init(alphabet, 8); <- creates needed dirs, 8 is size of alphabet + * h.Path("abcd"); <- returns /tmp/hs-test/name/ya/z-abcd.txt + * h.Remove("abcd"); <- removes /tmp/hs-test/name/ya/z-abcd.txt, if it exists + * std::vector files; + * h.Traverse(files); <- finds all files in storage and saves in given vector + */ + class HashedStorage + { + protected: - public: - typedef std::function FilenameVisitor; - HashedStorage(const char *n, const char *p1, const char *p2, const char *s): - name(n), prefix1(p1), prefix2(p2), suffix(s) {}; + std::string root; /**< path to storage with it's name included */ + std::string name; /**< name of the storage */ + std::string prefix1; /**< hashed directory prefix */ + std::string prefix2; /**< prefix of file in storage */ + std::string suffix; /**< suffix of file in storage (extension) */ - /** create subdirs in storage */ - bool Init(const char* chars, size_t cnt); - const std::string & GetRoot() const { return root; } - const std::string & GetName() const { return name; } - /** set directory where to place storage directory */ - void SetPlace(const std::string & path); - /** path to file with given ident */ - std::string Path(const std::string & ident) const; - /** remove file by ident */ - void Remove(const std::string & ident); - /** find all files in storage and store list in provided vector */ - void Traverse(std::vector & files); - /** visit every file in this storage with a visitor */ - void Iterate(FilenameVisitor v); - }; + public: - /** @brief Returns current application name, default 'i2pd' */ + typedef std::function FilenameVisitor; + HashedStorage(const char *n, const char *p1, const char *p2, const char *s): + name(n), prefix1(p1), prefix2(p2), suffix(s) {}; + + /** create subdirs in storage */ + bool Init(const char* chars, size_t cnt); + const std::string & GetRoot() const { return root; } + const std::string & GetName() const { return name; } + /** set directory where to place storage directory */ + void SetPlace(const std::string & path); + /** path to file with given ident */ + std::string Path(const std::string & ident) const; + /** remove file by ident */ + void Remove(const std::string & ident); + /** find all files in storage and store list in provided vector */ + void Traverse(std::vector & files); + /** visit every file in this storage with a visitor */ + void Iterate(FilenameVisitor v); + }; + + /** @brief Returns current application name, default 'i2pd' */ const std::string & GetAppName (); - /** @brief Set application name, affects autodetection of datadir */ + /** @brief Set application name, affects autodetection of datadir */ void SetAppName (const std::string& name); - /** @brief Returns datadir path */ - const std::string & GetDataDir(); + /** @brief Returns datadir path */ + const std::string & GetDataDir(); - /** - * @brief Set datadir either from cmdline option or using autodetection - * @param cmdline_param Value of cmdline parameter --datadir= - * @param isService Value of cmdline parameter --service - * - * Examples of autodetected paths: - * - * Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd\ - * Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd\ - * Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/ - * Unix: /var/lib/i2pd/ (system=1) >> ~/.i2pd/ or /tmp/i2pd/ - */ - void DetectDataDir(const std::string & cmdline_datadir, bool isService = false); + /** + * @brief Set datadir either from cmdline option or using autodetection + * @param cmdline_param Value of cmdline parameter --datadir= + * @param isService Value of cmdline parameter --service + * + * Examples of autodetected paths: + * + * Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd\ + * Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd\ + * Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/ + * Unix: /var/lib/i2pd/ (system=1) >> ~/.i2pd/ or /tmp/i2pd/ + */ + void DetectDataDir(const std::string & cmdline_datadir, bool isService = false); - /** - * @brief Create subdirectories inside datadir - */ - bool Init(); + /** + * @brief Create subdirectories inside datadir + */ + bool Init(); - /** - * @brief Get list of files in directory - * @param path Path to directory - * @param files Vector to store found files - * @return true on success and false if directory not exists - */ - bool ReadDir(const std::string & path, std::vector & files); + /** + * @brief Get list of files in directory + * @param path Path to directory + * @param files Vector to store found files + * @return true on success and false if directory not exists + */ + bool ReadDir(const std::string & path, std::vector & files); - /** - * @brief Remove file with given path - * @param path Absolute path to file - * @return true on success, false if file not exists, throws exception on error - */ - bool Remove(const std::string & path); + /** + * @brief Remove file with given path + * @param path Absolute path to file + * @return true on success, false if file not exists, throws exception on error + */ + bool Remove(const std::string & path); - /** - * @brief Check existence of file - * @param path Absolute path to file - * @return true if file exists, false otherwise - */ - bool Exists(const std::string & path); + /** + * @brief Check existence of file + * @param path Absolute path to file + * @return true if file exists, false otherwise + */ + bool Exists(const std::string & path); - uint32_t GetLastUpdateTime (const std::string & path); // seconds since epoch + uint32_t GetLastUpdateTime (const std::string & path); // seconds since epoch - bool CreateDirectory (const std::string& path); + bool CreateDirectory (const std::string& path); - template - void _ExpandPath(std::stringstream & path, T c) { - path << i2p::fs::dirSep << c; - } + template + void _ExpandPath(std::stringstream & path, T c) { + path << i2p::fs::dirSep << c; + } - template - void _ExpandPath(std::stringstream & path, T c, Other ... other) { - _ExpandPath(path, c); - _ExpandPath(path, other ...); - } + template + void _ExpandPath(std::stringstream & path, T c, Other ... other) { + _ExpandPath(path, c); + _ExpandPath(path, other ...); + } - /** - * @brief Get path relative to datadir - * - * Examples (with datadir = "/tmp/i2pd"): - * - * i2p::fs::Path("test") -> '/tmp/i2pd/test' - * i2p::fs::Path("test", "file.txt") -> '/tmp/i2pd/test/file.txt' - */ - template - std::string DataDirPath(Other ... components) { - std::stringstream s(""); - s << i2p::fs::GetDataDir(); - _ExpandPath(s, components ...); + /** + * @brief Get path relative to datadir + * + * Examples (with datadir = "/tmp/i2pd"): + * + * i2p::fs::Path("test") -> '/tmp/i2pd/test' + * i2p::fs::Path("test", "file.txt") -> '/tmp/i2pd/test/file.txt' + */ + template + std::string DataDirPath(Other ... components) { + std::stringstream s(""); + s << i2p::fs::GetDataDir(); + _ExpandPath(s, components ...); - return s.str(); - } + return s.str(); + } template std::string StorageRootPath (const Storage& storage, Filename... filenames) diff --git a/libi2pd/Family.cpp b/libi2pd/Family.cpp index b7cbf7d4..fbb7b9ee 100644 --- a/libi2pd/Family.cpp +++ b/libi2pd/Family.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include @@ -113,9 +121,9 @@ namespace data bool Families::VerifyFamily (const std::string& family, const IdentHash& ident, const char * signature, const char * key) { - uint8_t buf[50], signatureBuf[64]; + uint8_t buf[100], signatureBuf[64]; size_t len = family.length (), signatureLen = strlen (signature); - if (len + 32 > 50) + if (len + 32 > 100) { LogPrint (eLogError, "Family: ", family, " is too long"); return false; @@ -179,4 +187,3 @@ namespace data } } } - diff --git a/libi2pd/Family.h b/libi2pd/Family.h index a1b5a789..2a9149ba 100644 --- a/libi2pd/Family.h +++ b/libi2pd/Family.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef FAMILY_H__ #define FAMILY_H__ diff --git a/libi2pd/Garlic.cpp b/libi2pd/Garlic.cpp index fd6e8fee..1170634d 100644 --- a/libi2pd/Garlic.cpp +++ b/libi2pd/Garlic.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include "I2PEndian.h" #include @@ -38,9 +46,9 @@ namespace garlic if (!m_SharedRoutingPath) return nullptr; uint32_t ts = i2p::util::GetSecondsSinceEpoch (); if (m_SharedRoutingPath->numTimesUsed >= ROUTING_PATH_MAX_NUM_TIMES_USED || - !m_SharedRoutingPath->outboundTunnel->IsEstablished () || + !m_SharedRoutingPath->outboundTunnel->IsEstablished () || ts*1000LL > m_SharedRoutingPath->remoteLease->endDate || - ts > m_SharedRoutingPath->updateTime + ROUTING_PATH_EXPIRATION_TIMEOUT) + ts > m_SharedRoutingPath->updateTime + ROUTING_PATH_EXPIRATION_TIMEOUT) m_SharedRoutingPath = nullptr; if (m_SharedRoutingPath) m_SharedRoutingPath->numTimesUsed++; return m_SharedRoutingPath; @@ -68,7 +76,7 @@ namespace garlic return true; } return false; - } + } void GarlicRoutingSession::CleanupUnconfirmedLeaseSet (uint64_t ts) { @@ -78,8 +86,8 @@ namespace garlic GetOwner ()->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); m_LeaseSetUpdateMsgID = 0; } - } - + } + std::shared_ptr GarlicRoutingSession::CreateEncryptedDeliveryStatusMsg (uint32_t msgID) { auto msg = CreateDeliveryStatusMsg (msgID); @@ -94,12 +102,12 @@ namespace garlic msg = garlic.WrapSingleMessage (msg); } return msg; - } - - ElGamalAESSession::ElGamalAESSession (GarlicDestination * owner, - std::shared_ptr destination, int numTags, bool attachLeaseSet): - GarlicRoutingSession (owner, attachLeaseSet), - m_Destination (destination), m_NumTags (numTags) + } + + ElGamalAESSession::ElGamalAESSession (GarlicDestination * owner, + std::shared_ptr destination, int numTags, bool attachLeaseSet): + GarlicRoutingSession (owner, attachLeaseSet), + m_Destination (destination), m_NumTags (numTags) { // create new session tags and session key RAND_bytes (m_SessionKey, 32); @@ -115,7 +123,7 @@ namespace garlic m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch (); } - std::shared_ptr ElGamalAESSession::WrapSingleMessage (std::shared_ptr msg) + std::shared_ptr ElGamalAESSession::WrapSingleMessage (std::shared_ptr msg) { auto m = NewI2NPMessage (); m->Align (12); // in order to get buf aligned to 16 (12 + 4) @@ -181,7 +189,7 @@ namespace garlic return m; } - size_t ElGamalAESSession::CreateAESBlock (uint8_t * buf, std::shared_ptr msg) + size_t ElGamalAESSession::CreateAESBlock (uint8_t * buf, std::shared_ptr msg) { size_t blockSize = 0; bool createNewTags = GetOwner () && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3); @@ -329,10 +337,10 @@ namespace garlic // create msg auto msg = CreateEncryptedDeliveryStatusMsg (msgID); if (msg) - { + { memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); size += msg->GetLength (); - } + } // fill clove uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec uint32_t cloveID; @@ -353,7 +361,7 @@ namespace garlic return size; } - ElGamalAESSession::UnconfirmedTags * ElGamalAESSession::GenerateSessionTags () + ElGamalAESSession::UnconfirmedTags * ElGamalAESSession::GenerateSessionTags () { auto tags = new UnconfirmedTags (m_NumTags); tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch (); @@ -466,8 +474,9 @@ namespace garlic LogPrint (eLogWarning, "Garlic: message length ", length, " exceeds I2NP message length ", msg->GetLength ()); return; } + auto mod = length & 0x0f; // %16 buf += 4; // length - auto it = m_Tags.find (SessionTag(buf)); + auto it = !mod ? m_Tags.find (SessionTag(buf)) : m_Tags.end (); // AES block is multiple of 16 // AES tag might be used even if encryption type is not ElGamal/AES if (it != m_Tags.end ()) { @@ -487,25 +496,48 @@ namespace garlic } else { - // tag not found. Handle depending on encryption type - if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET)) + bool found = false; + if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET)) { - HandleECIESx25519 (buf, length); - return; - } - // otherwise assume ElGamal/AES - ElGamalBlock elGamal; - if (length >= 514 && Decrypt (buf, (uint8_t *)&elGamal, m_Ctx, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)) - { - auto decryption = std::make_shared(elGamal.sessionKey); - uint8_t iv[32]; // IV is first 16 bytes - SHA256(elGamal.preIV, 32, iv); - decryption->SetIV (iv); - decryption->Decrypt(buf + 514, length - 514, buf + 514); - HandleAESBlock (buf + 514, length - 514, decryption, msg->from); + // try ECIESx25519 tag + uint64_t tag; + memcpy (&tag, buf, 8); + auto it1 = m_ECIESx25519Tags.find (tag); + if (it1 != m_ECIESx25519Tags.end ()) + { + found = true; + auto session = it1->second.tagset->GetSession (); + if (!session || !session->HandleNextMessage (buf, length, it1->second.tagset, it1->second.index)) + LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message"); + m_ECIESx25519Tags.erase (it1); + } + } + + if (!found) // assume new session + { + // AES tag not found. Handle depending on encryption type + // try ElGamal/AES first if leading block is 514 + ElGamalBlock elGamal; + if (mod == 2 && length >= 514 && SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) && + Decrypt (buf, (uint8_t *)&elGamal, m_Ctx, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)) + { + auto decryption = std::make_shared(elGamal.sessionKey); + uint8_t iv[32]; // IV is first 16 bytes + SHA256(elGamal.preIV, 32, iv); + decryption->SetIV (iv); + decryption->Decrypt(buf + 514, length - 514, buf + 514); + HandleAESBlock (buf + 514, length - 514, decryption, msg->from); + } + else if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET)) + { + // otherwise ECIESx25519 + auto session = std::make_shared (this, false); // incoming + if (!session->HandleNextMessage (buf, length, nullptr, 0)) + LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message"); + } + else + LogPrint (eLogError, "Garlic: Failed to decrypt message"); } - else - LogPrint (eLogError, "Garlic: Failed to decrypt message"); } } @@ -679,41 +711,48 @@ namespace garlic std::shared_ptr GarlicDestination::GetRoutingSession ( std::shared_ptr destination, bool attachLeaseSet) { - if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET && - SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET)) - { - ECIESX25519AEADRatchetSessionPtr session; - uint8_t staticKey[32]; - destination->Encrypt (nullptr, staticKey, nullptr); // we are supposed to get static key - auto it = m_ECIESx25519Sessions.find (staticKey); + if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET && + SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET)) + { + ECIESX25519AEADRatchetSessionPtr session; + uint8_t staticKey[32]; + destination->Encrypt (nullptr, staticKey, nullptr); // we are supposed to get static key + auto it = m_ECIESx25519Sessions.find (staticKey); if (it != m_ECIESx25519Sessions.end ()) - session = it->second; - if (!session) - { - session = std::make_shared (this); - session->SetRemoteStaticKey (staticKey); + { + session = it->second; + if (session->IsInactive (i2p::util::GetSecondsSinceEpoch ())) + { + LogPrint (eLogDebug, "Garlic: session restarted"); + session = nullptr; + } } + if (!session) + { + session = std::make_shared (this, true); + session->SetRemoteStaticKey (staticKey); + } session->SetDestination (destination->GetIdentHash ()); // TODO: remove - return session; - } - else - { - ElGamalAESSessionPtr session; - { - std::unique_lock l(m_SessionsMutex); - auto it = m_Sessions.find (destination->GetIdentHash ()); - if (it != m_Sessions.end ()) - session = it->second; - } - if (!session) - { - session = std::make_shared (this, destination, - attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests - std::unique_lock l(m_SessionsMutex); - m_Sessions[destination->GetIdentHash ()] = session; - } - return session; - } + return session; + } + else + { + ElGamalAESSessionPtr session; + { + std::unique_lock l(m_SessionsMutex); + auto it = m_Sessions.find (destination->GetIdentHash ()); + if (it != m_Sessions.end ()) + session = it->second; + } + if (!session) + { + session = std::make_shared (this, destination, + attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests + std::unique_lock l(m_SessionsMutex); + m_Sessions[destination->GetIdentHash ()] = session; + } + return session; + } } void GarlicDestination::CleanupExpiredTags () @@ -764,10 +803,10 @@ namespace garlic // ECIESx25519 for (auto it = m_ECIESx25519Tags.begin (); it != m_ECIESx25519Tags.end ();) { - if (ts > it->second.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) + if (it->second.tagset->IsExpired (ts) || ts > it->second.creationTime + ECIESX25519_INCOMING_TAGS_EXPIRATION_TIMEOUT) it = m_ECIESx25519Tags.erase (it); else - ++it; + ++it; } for (auto it = m_ECIESx25519Sessions.begin (); it != m_ECIESx25519Sessions.end ();) @@ -776,7 +815,7 @@ namespace garlic { it->second->SetOwner (nullptr); it = m_ECIESx25519Sessions.erase (it); - } + } else ++it; } @@ -901,48 +940,28 @@ namespace garlic std::vector files; i2p::fs::ReadDir (i2p::fs::DataDirPath("tags"), files); uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it: files) + for (auto it: files) if (ts >= i2p::fs::GetLastUpdateTime (it) + INCOMING_TAGS_EXPIRATION_TIMEOUT) i2p::fs::Remove (it); } - void GarlicDestination::HandleECIESx25519 (const uint8_t * buf, size_t len) + void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len) { - uint64_t tag; - memcpy (&tag, buf, 8); - ECIESX25519AEADRatchetSessionPtr session; - int index = 0; - auto it = m_ECIESx25519Tags.find (tag); - if (it != m_ECIESx25519Tags.end ()) - { - session = it->second.session; - index = it->second.index; - m_ECIESx25519Tags.erase (tag); - } - else - session = std::make_shared (this); // incoming - - if (!session->HandleNextMessage (buf, len, index)) - LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message"); - } - - void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len) - { - const uint8_t * buf1 = buf; + const uint8_t * buf1 = buf; uint8_t flag = buf[0]; buf++; // flag GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03); switch (deliveryType) { case eGarlicDeliveryTypeDestination: - LogPrint (eLogDebug, "Garlic: type destination"); + LogPrint (eLogDebug, "Garlic: type destination"); buf += 32; // TODO: check destination -#if (__cplusplus >= 201703L) // C++ 17 or higher - [[fallthrough]]; -#endif +#if (__cplusplus >= 201703L) // C++ 17 or higher + [[fallthrough]]; +#endif // no break here case eGarlicDeliveryTypeLocal: { - LogPrint (eLogDebug, "Garlic: type local"); + LogPrint (eLogDebug, "Garlic: type local"); I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid buf += (4 + 4); // msgID + expiration ptrdiff_t offset = buf - buf1; @@ -953,8 +972,8 @@ namespace garlic break; } case eGarlicDeliveryTypeTunnel: - { - LogPrint (eLogDebug, "Garlic: type tunnel"); + { + LogPrint (eLogDebug, "Garlic: type tunnel"); // gwHash and gwTunnel sequence is reverted const uint8_t * gwHash = buf; buf += 32; @@ -964,48 +983,62 @@ namespace garlic LogPrint (eLogError, "Garlic: message is too short"); break; } - uint32_t gwTunnel = bufbe32toh (buf); buf += 4; - I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid + uint32_t gwTunnel = bufbe32toh (buf); buf += 4; + I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid buf += (4 + 4); // msgID + expiration - offset += 13; + offset += 13; if (GetTunnelPool ()) - { - auto tunnel = GetTunnelPool ()->GetNextOutboundTunnel (); - if (tunnel) - tunnel->SendTunnelDataMsg (gwHash, gwTunnel, CreateI2NPMessage (typeID, buf, len - offset)); + { + auto tunnel = GetTunnelPool ()->GetNextOutboundTunnel (); + if (tunnel) + tunnel->SendTunnelDataMsg (gwHash, gwTunnel, CreateI2NPMessage (typeID, buf, len - offset)); else - LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove"); - } + LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove"); + } else LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel"); - break; - } + break; + } default: LogPrint (eLogWarning, "Garlic: unexpected delivery type ", (int)deliveryType); - } - } + } + } - void GarlicDestination::AddECIESx25519SessionTag (int index, uint64_t tag, ECIESX25519AEADRatchetSessionPtr session) - { - m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexSession{index, session, i2p::util::GetSecondsSinceEpoch ()}); - } + void GarlicDestination::AddECIESx25519SessionNextTag (RatchetTagSetPtr tagset) + { + auto index = tagset->GetNextIndex (); + uint64_t tag = tagset->GetNextSessionTag (); + m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexTagset{index, tagset, i2p::util::GetSecondsSinceEpoch ()}); + } void GarlicDestination::AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session) { i2p::data::Tag<32> staticKeyTag (staticKey); auto it = m_ECIESx25519Sessions.find (staticKeyTag); if (it != m_ECIESx25519Sessions.end ()) - { + { if (it->second->CanBeRestarted (i2p::util::GetSecondsSinceEpoch ())) + { + it->second->SetOwner (nullptr); // detach m_ECIESx25519Sessions.erase (it); + } else { - LogPrint (eLogInfo, "Garlic: ECIESx25519 session with static key ", staticKeyTag.ToBase64 (), " already exists"); + LogPrint (eLogInfo, "Garlic: ECIESx25519 session with static key ", staticKeyTag.ToBase64 (), " already exists"); return; - } + } } m_ECIESx25519Sessions.emplace (staticKeyTag, session); } + void GarlicDestination::RemoveECIESx25519Session (const uint8_t * staticKey) + { + auto it = m_ECIESx25519Sessions.find (staticKey); + if (it != m_ECIESx25519Sessions.end ()) + { + it->second->SetOwner (nullptr); + m_ECIESx25519Sessions.erase (it); + } + } } } diff --git a/libi2pd/Garlic.h b/libi2pd/Garlic.h index 06da679d..6b34231a 100644 --- a/libi2pd/Garlic.h +++ b/libi2pd/Garlic.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef GARLIC_H__ #define GARLIC_H__ @@ -87,8 +95,8 @@ namespace garlic class GarlicDestination; class GarlicRoutingSession { - protected: - + protected: + enum LeaseSetUpdateStatus { eLeaseSetUpToDate = 0, @@ -103,9 +111,10 @@ namespace garlic GarlicRoutingSession (); virtual ~GarlicRoutingSession (); virtual std::shared_ptr WrapSingleMessage (std::shared_ptr msg) = 0; - virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession + virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession virtual bool MessageConfirmed (uint32_t msgID); - + virtual bool IsRatchets () const { return false; }; + void SetLeaseSetUpdated () { if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated; @@ -114,23 +123,23 @@ namespace garlic bool IsLeaseSetUpdated () const { return m_LeaseSetUpdateStatus == eLeaseSetUpdated; }; uint64_t GetLeaseSetSubmissionTime () const { return m_LeaseSetSubmissionTime; } void CleanupUnconfirmedLeaseSet (uint64_t ts); - + std::shared_ptr GetSharedRoutingPath (); void SetSharedRoutingPath (std::shared_ptr path); GarlicDestination * GetOwner () const { return m_Owner; } void SetOwner (GarlicDestination * owner) { m_Owner = owner; } - - protected: - - LeaseSetUpdateStatus GetLeaseSetUpdateStatus () const { return m_LeaseSetUpdateStatus; } - void SetLeaseSetUpdateStatus (LeaseSetUpdateStatus status) { m_LeaseSetUpdateStatus = status; } - uint32_t GetLeaseSetUpdateMsgID () const { return m_LeaseSetUpdateMsgID; } - void SetLeaseSetUpdateMsgID (uint32_t msgID) { m_LeaseSetUpdateMsgID = msgID; } - void SetLeaseSetSubmissionTime (uint64_t ts) { m_LeaseSetSubmissionTime = ts; } + + protected: + + LeaseSetUpdateStatus GetLeaseSetUpdateStatus () const { return m_LeaseSetUpdateStatus; } + void SetLeaseSetUpdateStatus (LeaseSetUpdateStatus status) { m_LeaseSetUpdateStatus = status; } + uint32_t GetLeaseSetUpdateMsgID () const { return m_LeaseSetUpdateMsgID; } + void SetLeaseSetUpdateMsgID (uint32_t msgID) { m_LeaseSetUpdateMsgID = msgID; } + void SetLeaseSetSubmissionTime (uint64_t ts) { m_LeaseSetSubmissionTime = ts; } std::shared_ptr CreateEncryptedDeliveryStatusMsg (uint32_t msgID); - + private: GarlicDestination * m_Owner; @@ -142,38 +151,39 @@ namespace garlic std::shared_ptr m_SharedRoutingPath; public: + // for HTTP only virtual size_t GetNumOutgoingTags () const { return 0; }; }; - //using GarlicRoutingSessionPtr = std::shared_ptr; - typedef std::shared_ptr GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8 + //using GarlicRoutingSessionPtr = std::shared_ptr; + typedef std::shared_ptr GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8 - class ElGamalAESSession: public GarlicRoutingSession, public std::enable_shared_from_this - { - struct UnconfirmedTags - { - UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; }; - ~UnconfirmedTags () { delete[] sessionTags; }; - uint32_t msgID; - int numTags; - SessionTag * sessionTags; - uint32_t tagsCreationTime; - }; + class ElGamalAESSession: public GarlicRoutingSession, public std::enable_shared_from_this + { + struct UnconfirmedTags + { + UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; }; + ~UnconfirmedTags () { delete[] sessionTags; }; + uint32_t msgID; + int numTags; + SessionTag * sessionTags; + uint32_t tagsCreationTime; + }; - public: + public: - ElGamalAESSession (GarlicDestination * owner, std::shared_ptr destination, + ElGamalAESSession (GarlicDestination * owner, std::shared_ptr destination, int numTags, bool attachLeaseSet); ElGamalAESSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption ~ElGamalAESSession () {}; - std::shared_ptr WrapSingleMessage (std::shared_ptr msg); - - bool MessageConfirmed (uint32_t msgID); + std::shared_ptr WrapSingleMessage (std::shared_ptr msg); + + bool MessageConfirmed (uint32_t msgID); bool CleanupExpiredTags (); // returns true if something left bool CleanupUnconfirmedTags (); // returns true if something has been deleted - private: + private: size_t CreateAESBlock (uint8_t * buf, std::shared_ptr msg); size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr msg, UnconfirmedTags * newTags); @@ -182,30 +192,33 @@ namespace garlic void TagsConfirmed (uint32_t msgID); UnconfirmedTags * GenerateSessionTags (); - - private: - - std::shared_ptr m_Destination; - i2p::crypto::AESKey m_SessionKey; + private: + + std::shared_ptr m_Destination; + + i2p::crypto::AESKey m_SessionKey; std::list m_SessionTags; int m_NumTags; std::map > m_UnconfirmedTagsMsgs; // msgID->tags - i2p::crypto::CBCEncryption m_Encryption; + i2p::crypto::CBCEncryption m_Encryption; + + public: - public: // for HTTP only - size_t GetNumOutgoingTags () const { return m_SessionTags.size (); }; - }; - typedef std::shared_ptr ElGamalAESSessionPtr; + size_t GetNumOutgoingTags () const { return m_SessionTags.size (); }; + }; + typedef std::shared_ptr ElGamalAESSessionPtr; - class ECIESX25519AEADRatchetSession; - typedef std::shared_ptr ECIESX25519AEADRatchetSessionPtr; - struct ECIESX25519AEADRatchetIndexSession - { - int index; - ECIESX25519AEADRatchetSessionPtr session; + class ECIESX25519AEADRatchetSession; + typedef std::shared_ptr ECIESX25519AEADRatchetSessionPtr; + class RatchetTagSet; + typedef std::shared_ptr RatchetTagSetPtr; + struct ECIESX25519AEADRatchetIndexTagset + { + int index; + RatchetTagSetPtr tagset; uint64_t creationTime; // seconds since epoch }; @@ -223,13 +236,14 @@ namespace garlic void CleanupExpiredTags (); void RemoveDeliveryStatusSession (uint32_t msgID); std::shared_ptr WrapMessage (std::shared_ptr destination, - std::shared_ptr msg, bool attachLeaseSet = false); + std::shared_ptr msg, bool attachLeaseSet = false); void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID); - void AddECIESx25519SessionTag (int index, uint64_t tag, ECIESX25519AEADRatchetSessionPtr session); + void AddECIESx25519SessionNextTag (RatchetTagSetPtr tagset); void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session); + void RemoveECIESx25519Session (const uint8_t * staticKey); void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len); virtual void ProcessGarlicMessage (std::shared_ptr msg); @@ -255,9 +269,6 @@ namespace garlic std::shared_ptr from); void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr from); - // ECIES-X25519-AEAD-Ratchet - void HandleECIESx25519 (const uint8_t * buf, size_t len); - private: BN_CTX * m_Ctx; // incoming @@ -265,10 +276,10 @@ namespace garlic int m_NumTags; std::mutex m_SessionsMutex; std::unordered_map m_Sessions; - std::unordered_map, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session + std::unordered_map, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session // incoming std::unordered_map, std::hash > > m_Tags; - std::unordered_map m_ECIESx25519Tags; // session tag -> session + std::unordered_map m_ECIESx25519Tags; // session tag -> session // DeliveryStatus std::mutex m_DeliveryStatusSessionsMutex; std::unordered_map m_DeliveryStatusSessions; // msgID -> session @@ -277,7 +288,9 @@ namespace garlic // for HTTP only size_t GetNumIncomingTags () const { return m_Tags.size (); } + size_t GetNumIncomingECIESx25519Tags () const { return m_ECIESx25519Tags.size (); } const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; + const decltype(m_ECIESx25519Sessions)& GetECIESx25519Sessions () const { return m_ECIESx25519Sessions; } }; void CleanUpTagsFiles (); diff --git a/libi2pd/Gost.cpp b/libi2pd/Gost.cpp index c401f8be..5e84a95d 100644 --- a/libi2pd/Gost.cpp +++ b/libi2pd/Gost.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include diff --git a/libi2pd/Gost.h b/libi2pd/Gost.h index 30386104..0a79c30b 100644 --- a/libi2pd/Gost.h +++ b/libi2pd/Gost.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef GOST_H__ #define GOST_H__ @@ -13,11 +21,11 @@ namespace crypto enum GOSTR3410ParamSet { - eGOSTR3410CryptoProA = 0, // 1.2.643.2.2.35.1 + eGOSTR3410CryptoProA = 0, // 1.2.643.2.2.35.1 // XchA = A, XchB = C - //eGOSTR3410CryptoProXchA, // 1.2.643.2.2.36.0 - //eGOSTR3410CryptoProXchB, // 1.2.643.2.2.36.1 - eGOSTR3410TC26A512, // 1.2.643.7.1.2.1.2.1 + //eGOSTR3410CryptoProXchA, // 1.2.643.2.2.36.0 + //eGOSTR3410CryptoProXchB, // 1.2.643.2.2.36.1 + eGOSTR3410TC26A512, // 1.2.643.7.1.2.1.2.1 eGOSTR3410NumParamSets }; diff --git a/libi2pd/Gzip.cpp b/libi2pd/Gzip.cpp index 1c06e941..07c6a96e 100644 --- a/libi2pd/Gzip.cpp +++ b/libi2pd/Gzip.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2017, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -10,6 +10,7 @@ #include /* memset */ #include #include "Log.h" +#include "I2PEndian.h" #include "Gzip.h" namespace i2p @@ -31,18 +32,34 @@ namespace data size_t GzipInflator::Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen) { - if (m_IsDirty) inflateReset (&m_Inflator); - m_IsDirty = true; - m_Inflator.next_in = const_cast(in); - m_Inflator.avail_in = inLen; - m_Inflator.next_out = out; - m_Inflator.avail_out = outLen; - int err; - if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END) - return outLen - m_Inflator.avail_out; - // else - LogPrint (eLogError, "Gzip: Inflate error ", err); - return 0; + if (inLen < 23) return 0; + if (in[10] == 0x01) // non compressed + { + size_t len = bufle16toh (in + 11); + if (len + 23 < inLen) + { + LogPrint (eLogError, "Gzip: Incorrect length"); + return 0; + } + if (len > outLen) len = outLen; + memcpy (out, in + 15, len); + return len; + } + else + { + if (m_IsDirty) inflateReset (&m_Inflator); + m_IsDirty = true; + m_Inflator.next_in = const_cast(in); + m_Inflator.avail_in = inLen; + m_Inflator.next_out = out; + m_Inflator.avail_out = outLen; + int err; + if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END) + return outLen - m_Inflator.avail_out; + // else + LogPrint (eLogError, "Gzip: Inflate error ", err); + return 0; + } } void GzipInflator::Inflate (const uint8_t * in, size_t inLen, std::ostream& os) @@ -106,10 +123,79 @@ namespace data m_Deflator.avail_out = outLen; int err; if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END) + { + out[9] = 0xff; // OS is always unknown return outLen - m_Deflator.avail_out; + } // else LogPrint (eLogError, "Gzip: Deflate error ", err); return 0; } + + size_t GzipDeflator::Deflate (const std::vector >& bufs, uint8_t * out, size_t outLen) + { + if (m_IsDirty) deflateReset (&m_Deflator); + m_IsDirty = true; + size_t offset = 0; + int err; + for (const auto& it: bufs) + { + m_Deflator.next_in = const_cast(it.first); + m_Deflator.avail_in = it.second; + m_Deflator.next_out = out + offset; + m_Deflator.avail_out = outLen - offset; + auto flush = (it == bufs.back ()) ? Z_FINISH : Z_NO_FLUSH; + err = deflate (&m_Deflator, flush); + if (err) + { + if (flush && err == Z_STREAM_END) + { + out[9] = 0xff; // OS is always unknown + return outLen - m_Deflator.avail_out; + } + break; + } + offset = outLen - m_Deflator.avail_out; + } + // else + LogPrint (eLogError, "Gzip: Deflate error ", err); + return 0; + } + + size_t GzipNoCompression (const uint8_t * in, uint16_t inLen, uint8_t * out, size_t outLen) + { + static const uint8_t gzipHeader[11] = { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x01 }; + if (outLen < (size_t)inLen + 23) return 0; + memcpy (out, gzipHeader, 11); + htole16buf (out + 11, inLen); + htole16buf (out + 13, 0xffff - inLen); + memcpy (out + 15, in, inLen); + htole32buf (out + inLen + 15, crc32 (0, in, inLen)); + htole32buf (out + inLen + 19, inLen); + return inLen + 23; + } + + size_t GzipNoCompression (const std::vector >& bufs, uint8_t * out, size_t outLen) + { + static const uint8_t gzipHeader[11] = { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x01 }; + memcpy (out, gzipHeader, 11); + uint32_t crc = 0; + size_t len = 0, len1; + for (const auto& it: bufs) + { + len1 = len; + len += it.second; + if (outLen < len + 23) return 0; + memcpy (out + 15 + len1, it.first, it.second); + crc = crc32 (crc, it.first, it.second); + } + if (len > 0xffff) return 0; + htole32buf (out + len + 15, crc); + htole32buf (out + len + 19, len); + htole16buf (out + 11, len); + htole16buf (out + 13, 0xffff - len); + return len + 23; + } + } // data } // i2p diff --git a/libi2pd/Gzip.h b/libi2pd/Gzip.h index 35661abe..d0bb28ed 100644 --- a/libi2pd/Gzip.h +++ b/libi2pd/Gzip.h @@ -1,10 +1,21 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef GZIP_H__ #define GZIP_H__ #include +#include -namespace i2p { -namespace data { +namespace i2p +{ +namespace data +{ class GzipInflator { public: @@ -32,12 +43,16 @@ namespace data { void SetCompressionLevel (int level); size_t Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen); + size_t Deflate (const std::vector >& bufs, uint8_t * out, size_t outLen); private: z_stream m_Deflator; bool m_IsDirty; }; + + size_t GzipNoCompression (const uint8_t * in, uint16_t inLen, uint8_t * out, size_t outLen); // for < 64K + size_t GzipNoCompression (const std::vector >& bufs, uint8_t * out, size_t outLen); // for total size < 64K } // data } // i2p diff --git a/libi2pd/HTTP.cpp b/libi2pd/HTTP.cpp index 64acb6ea..4f7b03a1 100644 --- a/libi2pd/HTTP.cpp +++ b/libi2pd/HTTP.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2019, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * diff --git a/libi2pd/HTTP.h b/libi2pd/HTTP.h index f156fef0..c0cf1285 100644 --- a/libi2pd/HTTP.h +++ b/libi2pd/HTTP.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2019, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * diff --git a/libi2pd/I2NPProtocol.cpp b/libi2pd/I2NPProtocol.cpp index afdd8dc5..53afbca4 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include "Base.h" @@ -39,14 +47,14 @@ namespace i2p return (len < I2NP_MAX_SHORT_MESSAGE_SIZE - I2NP_HEADER_SIZE - 2) ? NewI2NPShortMessage () : NewI2NPMessage (); } - void I2NPMessage::FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID) + void I2NPMessage::FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID, bool checksum) { SetTypeID (msgType); if (!replyMsgID) RAND_bytes ((uint8_t *)&replyMsgID, 4); SetMsgID (replyMsgID); SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + I2NP_MESSAGE_EXPIRATION_TIMEOUT); UpdateSize (); - UpdateChks (); + if (checksum) UpdateChks (); } void I2NPMessage::RenewI2NPMessageHeader () @@ -166,7 +174,7 @@ namespace i2p std::shared_ptr replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag) { int cnt = excludedFloodfills.size (); - auto m = cnt > 0 ? NewI2NPMessage () : NewI2NPShortMessage (); + auto m = cnt > 7 ? NewI2NPMessage () : NewI2NPShortMessage (); uint8_t * buf = m->GetPayload (); memcpy (buf, dest, 32); // key buf += 32; @@ -178,6 +186,11 @@ namespace i2p buf += 4; // excluded + if (cnt > 512) + { + LogPrint (eLogWarning, "I2NP: Too many peers to exclude ", cnt, " for DatabaseLookup"); + cnt = 0; + } htobe16buf (buf, cnt); buf += 2; if (cnt > 0) @@ -190,7 +203,7 @@ namespace i2p } // encryption memcpy (buf, replyKey, 32); - buf[32] = uint8_t( 1 ); // 1 tag + buf[32] = 1; // 1 tag memcpy (buf + 33, replyTag, 32); buf += 65; @@ -200,7 +213,7 @@ namespace i2p } std::shared_ptr CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, - std::vector routers) + std::vector routers) { auto m = NewI2NPShortMessage (); uint8_t * buf = m->GetPayload (); @@ -244,8 +257,14 @@ namespace i2p uint8_t * sizePtr = buf; buf += 2; m->len += (buf - payload); // payload size - i2p::data::GzipDeflator deflator; - size_t size = deflator.Deflate (router->GetBuffer (), router->GetBufferLen (), buf, m->maxLen -m->len); + size_t size = 0; + if (router->GetBufferLen () + (buf - payload) <= 940) // fits one tunnel message + size = i2p::data::GzipNoCompression (router->GetBuffer (), router->GetBufferLen (), buf, m->maxLen -m->len); + else + { + i2p::data::GzipDeflator deflator; + size = deflator.Deflate (router->GetBuffer (), router->GetBufferLen (), buf, m->maxLen -m->len); + } if (size) { htobe16buf (sizePtr, size); // size @@ -264,7 +283,7 @@ namespace i2p auto m = NewI2NPShortMessage (); uint8_t * payload = m->GetPayload (); memcpy (payload + DATABASE_STORE_KEY_OFFSET, storeHash, 32); - payload[DATABASE_STORE_TYPE_OFFSET] = leaseSet->GetStoreType (); // 1 for LeaseSet + payload[DATABASE_STORE_TYPE_OFFSET] = leaseSet->GetStoreType (); // 1 for LeaseSet htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); size_t size = DATABASE_STORE_HEADER_SIZE; memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ()); @@ -274,7 +293,7 @@ namespace i2p return m; } - std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr leaseSet, uint32_t replyToken, std::shared_ptr replyTunnel) + std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr leaseSet, uint32_t replyToken, std::shared_ptr replyTunnel) { if (!leaseSet) return nullptr; auto m = NewI2NPShortMessage (); @@ -311,13 +330,18 @@ namespace i2p static uint16_t g_MaxNumTransitTunnels = DEFAULT_MAX_NUM_TRANSIT_TUNNELS; // TODO: void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels) { - if (maxNumTransitTunnels > 0 && maxNumTransitTunnels <= 10000 && g_MaxNumTransitTunnels != maxNumTransitTunnels) + if (maxNumTransitTunnels > 0 && g_MaxNumTransitTunnels != maxNumTransitTunnels) { - LogPrint (eLogDebug, "I2NP: Max number of transit tunnels set to ", maxNumTransitTunnels); + LogPrint (eLogDebug, "I2NP: Max number of transit tunnels set to ", maxNumTransitTunnels); g_MaxNumTransitTunnels = maxNumTransitTunnels; } } + uint16_t GetMaxNumTransitTunnels () + { + return g_MaxNumTransitTunnels; + } + bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText) { for (int i = 0; i < num; i++) @@ -338,11 +362,11 @@ namespace i2p auto transitTunnel = i2p::tunnel::CreateTransitTunnel ( bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, - clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, + clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80, - clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET ] & 0x40); + clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET ] & 0x40); i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 0; } @@ -377,7 +401,7 @@ namespace i2p return; } - auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID); + auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID); if (tunnel) { // endpoint of inbound tunnel @@ -405,7 +429,7 @@ namespace i2p transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), eI2NPVariableTunnelBuildReply, buf, len, - bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); + bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); } else transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, @@ -431,7 +455,7 @@ namespace i2p transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), eI2NPTunnelBuildReply, buf, len, - bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); + bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); } else transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, @@ -583,13 +607,13 @@ namespace i2p switch (typeID) { case eI2NPVariableTunnelBuild: - HandleVariableTunnelBuildMsg (msgID, buf, size); + HandleVariableTunnelBuildMsg (msgID, buf, size); break; case eI2NPVariableTunnelBuildReply: HandleVariableTunnelBuildReplyMsg (msgID, buf, size); break; case eI2NPTunnelBuild: - HandleTunnelBuildMsg (buf, size); + HandleTunnelBuildMsg (buf, size); break; case eI2NPTunnelBuildReply: // TODO: @@ -658,7 +682,7 @@ namespace i2p Flush (); } - void I2NPMessagesHandler::PutNextMessage (std::shared_ptr msg) + void I2NPMessagesHandler::PutNextMessage (std::shared_ptr msg) { if (msg) { diff --git a/libi2pd/I2NPProtocol.h b/libi2pd/I2NPProtocol.h index 5714afce..695de798 100644 --- a/libi2pd/I2NPProtocol.h +++ b/libi2pd/I2NPProtocol.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef I2NP_PROTOCOL_H__ #define I2NP_PROTOCOL_H__ @@ -27,7 +35,7 @@ namespace i2p 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; + const size_t I2NP_NTCP2_HEADER_SIZE = I2NP_HEADER_EXPIRATION_OFFSET + 4; // Tunnel Gateway header const size_t TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET = 0; @@ -75,7 +83,7 @@ namespace i2p enum I2NPMessageType { - eI2NPDummyMsg = 0, + eI2NPDummyMsg = 0, eI2NPDatabaseStore = 1, eI2NPDatabaseLookup = 2, eI2NPDatabaseSearchReply = 3, @@ -209,7 +217,7 @@ namespace tunnel SetExpiration (bufbe32toh (ntcp2 + I2NP_HEADER_EXPIRATION_OFFSET)*1000LL); SetSize (len - offset - I2NP_HEADER_SIZE); SetChks (0); - } + } void ToNTCP2 () { @@ -218,7 +226,7 @@ namespace tunnel memcpy (ntcp2 + I2NP_HEADER_TYPEID_OFFSET, GetHeader () + I2NP_HEADER_TYPEID_OFFSET, 5); // typeid + msgid } - void FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID = 0); + void FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID = 0, bool checksum = true); void RenewI2NPMessageHeader (); bool IsExpired () const; }; @@ -285,6 +293,7 @@ namespace tunnel const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 2500; void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels); + uint16_t GetMaxNumTransitTunnels (); } #endif diff --git a/libi2pd/I2PEndian.cpp b/libi2pd/I2PEndian.cpp index b8a041d8..32ca4e26 100644 --- a/libi2pd/I2PEndian.cpp +++ b/libi2pd/I2PEndian.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include "I2PEndian.h" // http://habrahabr.ru/post/121811/ diff --git a/libi2pd/I2PEndian.h b/libi2pd/I2PEndian.h index 0798f688..9ffc28d0 100644 --- a/libi2pd/I2PEndian.h +++ b/libi2pd/I2PEndian.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef I2PENDIAN_H__ #define I2PENDIAN_H__ #include @@ -128,5 +136,20 @@ inline void htole64buf(void *buf, uint64_t big64) htobuf64(buf, htole64(big64)); } +inline uint16_t bufle16toh(const void *buf) +{ + return le16toh(buf16toh(buf)); +} + +inline uint32_t bufle32toh(const void *buf) +{ + return le32toh(buf32toh(buf)); +} + +inline uint64_t bufle64toh(const void *buf) +{ + return le64toh(buf64toh(buf)); +} + #endif // I2PENDIAN_H__ diff --git a/libi2pd/Identity.cpp b/libi2pd/Identity.cpp index 1026eb02..b2b5f2b4 100644 --- a/libi2pd/Identity.cpp +++ b/libi2pd/Identity.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include "Crypto.h" #include "I2PEndian.h" #include "Log.h" @@ -356,7 +364,7 @@ namespace data } return nullptr; } - + void IdentityEx::CreateVerifier () const { if (m_Verifier) return; // don't create again @@ -369,15 +377,15 @@ namespace data else { // for P521 - uint8_t * signingKey = new uint8_t[keyLen]; + uint8_t * signingKey = new uint8_t[keyLen]; memcpy (signingKey, m_StandardIdentity.signingKey, 128); - size_t excessLen = keyLen - 128; + size_t excessLen = keyLen - 128; memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types verifier->SetPublicKey (signingKey); - delete[] signingKey; - } - } - UpdateVerifier (verifier); + delete[] signingKey; + } + } + UpdateVerifier (verifier); } void IdentityEx::UpdateVerifier (i2p::crypto::Verifier * verifier) const @@ -390,7 +398,7 @@ namespace data else del = true; } - if (del) + if (del) delete verifier; } @@ -412,7 +420,7 @@ namespace data case CRYPTO_KEY_TYPE_ELGAMAL: return std::make_shared(key); break; - case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET: + case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET: return std::make_shared(key); break; case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: @@ -426,7 +434,7 @@ namespace data LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)keyType); }; return nullptr; - } + } std::shared_ptr IdentityEx::CreateEncryptor (const uint8_t * key) const { @@ -460,9 +468,9 @@ namespace data return *this; } - size_t PrivateKeys::GetFullLen () const - { - size_t ret = m_Public->GetFullLen () + 256 + m_Public->GetSigningPrivateKeyLen (); + size_t PrivateKeys::GetFullLen () const + { + size_t ret = m_Public->GetFullLen () + 256 + m_Public->GetSigningPrivateKeyLen (); if (IsOfflineSignature ()) ret += m_OfflineSignature.size () + m_TransientSigningPrivateKeyLen; return ret; @@ -483,7 +491,7 @@ namespace data // check if signing private key is all zeros bool allzeros = true; for (size_t i = 0; i < signingPrivateKeySize; i++) - if (m_SigningPrivateKey[i]) + if (m_SigningPrivateKey[i]) { allzeros = false; break; @@ -500,7 +508,7 @@ namespace data if (keyLen + ret > len) return 0; transientVerifier->SetPublicKey (buf + ret); ret += keyLen; if (m_Public->GetSignatureLen () + ret > len) return 0; - if (!m_Public->Verify (offlineInfo, keyLen + 6, buf + ret)) + if (!m_Public->Verify (offlineInfo, keyLen + 6, buf + ret)) { LogPrint (eLogError, "Identity: offline signature verification failed"); return 0; @@ -511,7 +519,7 @@ namespace data size_t offlineInfoLen = buf + ret - offlineInfo; m_OfflineSignature.resize (offlineInfoLen); memcpy (m_OfflineSignature.data (), offlineInfo, offlineInfoLen); - // override signing private key + // override signing private key m_TransientSigningPrivateKeyLen = transientVerifier->GetPrivateKeyLen (); if (m_TransientSigningPrivateKeyLen + ret > len || m_TransientSigningPrivateKeyLen > 128) return 0; memcpy (m_SigningPrivateKey, buf + ret, m_TransientSigningPrivateKeyLen); @@ -586,10 +594,10 @@ namespace data else CreateSigner (m_Public->GetSigningKeyType ()); } - + void PrivateKeys::CreateSigner (SigningKeyType keyType) const { - if (m_Signer) return; + if (m_Signer) return; if (keyType == SIGNING_KEY_TYPE_DSA_SHA1) m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey, m_Public->GetStandardIdentity ().signingKey)); else if (keyType == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 && !IsOfflineSignature ()) @@ -599,7 +607,7 @@ namespace data // public key is not required auto signer = CreateSigner (keyType, m_SigningPrivateKey); if (signer) m_Signer.reset (signer); - } + } } i2p::crypto::Signer * PrivateKeys::CreateSigner (SigningKeyType keyType, const uint8_t * priv) @@ -630,8 +638,8 @@ namespace data return new i2p::crypto::GOSTR3410_512_Signer (i2p::crypto::eGOSTR3410TC26A512, priv); break; case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: - return new i2p::crypto::RedDSA25519Signer (priv); - break; + return new i2p::crypto::RedDSA25519Signer (priv); + break; default: LogPrint (eLogError, "Identity: Signing key type ", (int)keyType, " is not supported"); } @@ -672,7 +680,7 @@ namespace data case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC: return std::make_shared(key); break; - case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET: + case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET: return std::make_shared(key); break; default: @@ -719,8 +727,8 @@ namespace data case SIGNING_KEY_TYPE_RSA_SHA512_4096: LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA"); #if (__cplusplus >= 201703L) // C++ 17 or higher - [[fallthrough]]; -#endif + [[fallthrough]]; +#endif // no break here case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: i2p::crypto::CreateEDDSA25519RandomKeys (priv, pub); @@ -733,7 +741,7 @@ namespace data break; case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: i2p::crypto::CreateRedDSA25519RandomKeys (priv, pub); - break; + break; default: LogPrint (eLogWarning, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1"); i2p::crypto::CreateDSARandomKeys (priv, pub); // DSA-SHA1 @@ -754,7 +762,7 @@ namespace data case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC: i2p::crypto::CreateECIESGOSTR3410RandomKeys (priv, pub); break; - case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET: + case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET: i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys (priv, pub); break; default: @@ -765,7 +773,7 @@ namespace data PrivateKeys PrivateKeys::CreateOfflineKeys (SigningKeyType type, uint32_t expires) const { PrivateKeys keys (*this); - std::unique_ptr verifier (IdentityEx::CreateVerifier (type)); + std::unique_ptr verifier (IdentityEx::CreateVerifier (type)); if (verifier) { size_t pubKeyLen = verifier->GetPublicKeyLen (); @@ -775,7 +783,7 @@ namespace data htobe32buf (keys.m_OfflineSignature.data (), expires); // expires htobe16buf (keys.m_OfflineSignature.data () + 4, type); // type GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, keys.m_OfflineSignature.data () + 6); // public key - Sign (keys.m_OfflineSignature.data (), pubKeyLen + 6, keys.m_OfflineSignature.data () + 6 + pubKeyLen); // signature + Sign (keys.m_OfflineSignature.data (), pubKeyLen + 6, keys.m_OfflineSignature.data () + 6 + pubKeyLen); // signature // recreate signer keys.m_Signer = nullptr; keys.CreateSigner (type); diff --git a/libi2pd/Identity.h b/libi2pd/Identity.h index 663f46b5..534b8f4c 100644 --- a/libi2pd/Identity.h +++ b/libi2pd/Identity.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef IDENTITY_H__ #define IDENTITY_H__ @@ -56,7 +64,7 @@ namespace data const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0; const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC = 1; - const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET = 4; + const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET = 4; const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST = 65280; // TODO: remove later const uint16_t CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC = 65281; // TODO: use GOST R 34.11 instead SHA256 and GOST 28147-89 instead AES @@ -115,7 +123,7 @@ namespace data void RecalculateIdentHash(uint8_t * buff=nullptr); static i2p::crypto::Verifier * CreateVerifier (SigningKeyType keyType); - static std::shared_ptr CreateEncryptor (CryptoKeyType keyType, const uint8_t * key); + static std::shared_ptr CreateEncryptor (CryptoKeyType keyType, const uint8_t * key); private: @@ -148,7 +156,7 @@ namespace data const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey; }; size_t GetSignatureLen () const; // might not match identity bool IsOfflineSignature () const { return m_TransientSignatureLen > 0; }; - uint8_t * GetPadding(); + uint8_t * GetPadding(); void RecalculateIdentHash(uint8_t * buf=nullptr) { m_Public->RecalculateIdentHash(buf); } void Sign (const uint8_t * buf, int len, uint8_t * signature) const; @@ -163,12 +171,12 @@ namespace data static std::shared_ptr CreateDecryptor (CryptoKeyType cryptoType, const uint8_t * key); static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1, CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL); - static void GenerateSigningKeyPair (SigningKeyType type, uint8_t * priv, uint8_t * pub); + static void GenerateSigningKeyPair (SigningKeyType type, uint8_t * priv, uint8_t * pub); static void GenerateCryptoKeyPair (CryptoKeyType type, uint8_t * priv, uint8_t * pub); // priv and pub are 256 bytes long static i2p::crypto::Signer * CreateSigner (SigningKeyType keyType, const uint8_t * priv); // offline keys - PrivateKeys CreateOfflineKeys (SigningKeyType type, uint32_t expires) const; + PrivateKeys CreateOfflineKeys (SigningKeyType type, uint32_t expires) const; const std::vector& GetOfflineSignature () const { return m_OfflineSignature; }; private: @@ -204,7 +212,7 @@ namespace data IdentHash CreateRoutingKey (const IdentHash& ident); XORMetric operator^(const IdentHash& key1, const IdentHash& key2); - // destination for delivery instuctions + // destination for delivery instructions class RoutingDestination { public: @@ -235,5 +243,4 @@ namespace data } } - #endif diff --git a/libi2pd/LeaseSet.cpp b/libi2pd/LeaseSet.cpp index b411ca0e..b90d6d85 100644 --- a/libi2pd/LeaseSet.cpp +++ b/libi2pd/LeaseSet.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include "I2PEndian.h" #include "Crypto.h" @@ -12,9 +20,8 @@ namespace i2p { namespace data { - LeaseSet::LeaseSet (bool storeLeases): - m_IsValid (false), m_StoreLeases (storeLeases), m_ExpirationTime (0), m_EncryptionKey (nullptr), + m_IsValid (false), m_StoreLeases (storeLeases), m_ExpirationTime (0), m_EncryptionKey (nullptr), m_Buffer (nullptr), m_BufferLen (0) { } @@ -62,7 +69,7 @@ namespace data { if (!m_EncryptionKey) m_EncryptionKey = new uint8_t[256]; memcpy (m_EncryptionKey, m_Buffer + size, 256); - } + } size += 256; // encryption key size += m_Identity->GetSigningPublicKeyLen (); // unused signing key uint8_t num = m_Buffer[size]; @@ -149,20 +156,13 @@ namespace data auto ret = m_Leases.insert (std::make_shared(lease)); if (!ret.second) (*ret.first)->endDate = lease.endDate; // update existing (*ret.first)->isUpdated = true; - // check if lease's gateway is in our netDb - if (!netdb.FindRouter (lease.tunnelGateway)) - { - // if not found request it - LogPrint (eLogInfo, "LeaseSet: Lease's tunnel gateway not found, requesting"); - netdb.RequestDestination (lease.tunnelGateway); - } } } else LogPrint (eLogWarning, "LeaseSet: Lease is expired already"); } - uint64_t LeaseSet::ExtractTimestamp (const uint8_t * buf, size_t len) const + uint64_t LeaseSet::ExtractExpirationTimestamp (const uint8_t * buf, size_t len) const { if (!m_Identity) return 0; size_t size = m_Identity->GetFullLen (); @@ -187,7 +187,7 @@ namespace data bool LeaseSet::IsNewer (const uint8_t * buf, size_t len) const { - return ExtractTimestamp (buf, len) > ExtractTimestamp (m_Buffer, m_BufferLen); + return ExtractExpirationTimestamp (buf, len) > ExtractExpirationTimestamp (m_Buffer, m_BufferLen); } bool LeaseSet::ExpiresSoon(const uint64_t dlt, const uint64_t fudge) const @@ -198,10 +198,10 @@ namespace data return m_ExpirationTime - now <= dlt; } - const std::vector > LeaseSet::GetNonExpiredLeases (bool withThreshold) const - { - return GetNonExpiredLeasesExcluding( [] (const Lease & l) -> bool { return false; }, withThreshold); - } + const std::vector > LeaseSet::GetNonExpiredLeases (bool withThreshold) const + { + return GetNonExpiredLeasesExcluding( [] (const Lease & l) -> bool { return false; }, withThreshold); + } const std::vector > LeaseSet::GetNonExpiredLeasesExcluding (LeaseInspectFunc exclude, bool withThreshold) const { @@ -256,19 +256,19 @@ namespace data if (len <= m_BufferLen) m_BufferLen = len; else LogPrint (eLogError, "LeaseSet2: actual buffer size ", len , " exceeds full buffer size ", m_BufferLen); - } - + } + LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases, CryptoKeyType preferredCrypto): LeaseSet (storeLeases), m_StoreType (storeType), m_EncryptionType (preferredCrypto) - { - SetBuffer (buf, len); + { + SetBuffer (buf, len); if (storeType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) ReadFromBufferEncrypted (buf, len, nullptr, nullptr); else ReadFromBuffer (buf, len); } - LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr key, + LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr key, const uint8_t * secret, CryptoKeyType preferredCrypto): LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2), m_EncryptionType (preferredCrypto) { @@ -276,19 +276,25 @@ namespace data } void LeaseSet2::Update (const uint8_t * buf, size_t len, bool verifySignature) - { + { SetBuffer (buf, len); if (GetStoreType () != NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) - ReadFromBuffer (buf, len, false, verifySignature); + ReadFromBuffer (buf, len, false, verifySignature); // TODO: implement encrypted } - + + bool LeaseSet2::IsNewer (const uint8_t * buf, size_t len) const + { + uint64_t expiration; + return ExtractPublishedTimestamp (buf, len, expiration) > m_PublishedTimestamp; + } + void LeaseSet2::ReadFromBuffer (const uint8_t * buf, size_t len, bool readIdentity, bool verifySignature) { // standard LS2 header std::shared_ptr identity; if (readIdentity) - { + { identity = std::make_shared(buf, len); SetIdentity (identity); } @@ -305,7 +311,7 @@ namespace data // transient key m_TransientVerifier = ProcessOfflineSignature (identity, buf, len, offset); if (!m_TransientVerifier) - { + { LogPrint (eLogError, "LeaseSet2: offline signature failed"); return; } @@ -315,7 +321,7 @@ namespace data { m_IsPublishedEncrypted = true; m_IsPublic = true; - } + } // type specific part size_t s = 0; switch (m_StoreType) @@ -332,11 +338,11 @@ namespace data if (!s) return; offset += s; if (verifySignature || m_TransientVerifier) - { + { // verify signature bool verified = m_TransientVerifier ? VerifySignature (m_TransientVerifier, buf, len, offset) : - VerifySignature (identity, buf, len, offset); - SetIsValid (verified); + VerifySignature (identity, buf, len, offset); + SetIsValid (verified); } offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : identity->GetSignatureLen (); SetBufferLen (offset); @@ -347,10 +353,10 @@ namespace data { if (signatureOffset + verifier->GetSignatureLen () > len) return false; // we assume buf inside DatabaseStore message, so buf[-1] is valid memory - // change it for signature verification, and restore back + // change it for signature verification, and restore back uint8_t c = buf[-1]; const_cast(buf)[-1] = m_StoreType; - bool verified = verifier->Verify (buf - 1, signatureOffset + 1, buf + signatureOffset); + bool verified = verifier->Verify (buf - 1, signatureOffset + 1, buf + signatureOffset); const_cast(buf)[-1] = c; if (!verified) LogPrint (eLogWarning, "LeaseSet2: verification failed"); @@ -361,7 +367,7 @@ namespace data { size_t offset = 0; // properties - uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2; + uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2; offset += propertiesLen; // skip for now. TODO: implement properties if (offset + 1 >= len) return 0; // key sections @@ -372,7 +378,7 @@ namespace data { uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption key type if (offset + 2 >= len) return 0; - uint16_t encryptionKeyLen = bufbe16toh (buf + offset); offset += 2; + uint16_t encryptionKeyLen = bufbe16toh (buf + offset); offset += 2; if (offset + encryptionKeyLen >= len) return 0; if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only { @@ -385,10 +391,10 @@ namespace data if (keyType == preferredKeyType) preferredKeyFound = true; } } - offset += encryptionKeyLen; - } + offset += encryptionKeyLen; + } // leases - if (offset + 1 >= len) return 0; + if (offset + 1 >= len) return 0; int numLeases = buf[offset]; offset++; auto ts = i2p::util::GetMillisecondsSinceEpoch (); if (IsStoreLeases ()) @@ -414,9 +420,9 @@ namespace data { size_t offset = 0; // properties - uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2; + uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2; offset += propertiesLen; // skip for now. TODO: implement properties - // entries + // entries if (offset + 1 >= len) return 0; int numEntries = buf[offset]; offset++; for (int i = 0; i < numEntries; i++) @@ -424,12 +430,12 @@ namespace data if (offset + 40 >= len) return 0; offset += 32; // hash offset += 3; // flags - offset += 1; // cost + offset += 1; // cost offset += 4; // expires } // revocations if (offset + 1 >= len) return 0; - int numRevocations = buf[offset]; offset++; + int numRevocations = buf[offset]; offset++; for (int i = 0; i < numRevocations; i++) { if (offset + 32 > len) return 0; @@ -447,7 +453,7 @@ namespace data uint16_t blindedKeyType = bufbe16toh (stA1); offset += 2; std::unique_ptr blindedVerifier (i2p::data::IdentityEx::CreateVerifier (blindedKeyType)); if (!blindedVerifier) return; - auto blindedKeyLen = blindedVerifier->GetPublicKeyLen (); + auto blindedKeyLen = blindedVerifier->GetPublicKeyLen (); if (offset + blindedKeyLen >= len) return; const uint8_t * blindedPublicKey = buf + offset; blindedVerifier->SetPublicKey (blindedPublicKey); offset += blindedKeyLen; @@ -462,7 +468,7 @@ namespace data { // transient key m_TransientVerifier = ProcessOfflineSignature (blindedVerifier, buf, len, offset); - if (!m_TransientVerifier) + if (!m_TransientVerifier) { LogPrint (eLogError, "LeaseSet2: offline signature failed"); return; @@ -471,16 +477,16 @@ namespace data // outer ciphertext if (offset + 2 > len) return; uint16_t lenOuterCiphertext = bufbe16toh (buf + offset); offset += 2; - const uint8_t * outerCiphertext = buf + offset; - offset += lenOuterCiphertext; + const uint8_t * outerCiphertext = buf + offset; + offset += lenOuterCiphertext; // verify signature bool verified = m_TransientVerifier ? VerifySignature (m_TransientVerifier, buf, len, offset) : - VerifySignature (blindedVerifier, buf, len, offset); + VerifySignature (blindedVerifier, buf, len, offset); SetIsValid (verified); // handle ciphertext if (verified && key && lenOuterCiphertext >= 32) { - SetIsValid (false); // we must verify it again in Layer 2 + SetIsValid (false); // we must verify it again in Layer 2 if (blindedKeyType == key->GetBlindedSigType ()) { // verify blinding @@ -492,13 +498,13 @@ namespace data { LogPrint (eLogError, "LeaseSet2: blinded public key doesn't match"); return; - } - } + } + } else { LogPrint (eLogError, "LeaseSet2: Unexpected blinded key type ", blindedKeyType, " instead ", key->GetBlindedSigType ()); return; - } + } // outer key // outerInput = subcredential || publishedTimestamp uint8_t subcredential[36]; @@ -519,10 +525,10 @@ namespace data // innerSalt = innerCiphertext[0:32] // keys = HKDF(innerSalt, innerInput, "ELS2_L2K", 44) uint8_t innerInput[68]; - size_t authDataLen = ExtractClientAuthData (outerPlainText.data (), lenOuterPlaintext, secret, subcredential, innerInput); + size_t authDataLen = ExtractClientAuthData (outerPlainText.data (), lenOuterPlaintext, secret, subcredential, innerInput); if (authDataLen > 0) { - memcpy (innerInput + 32, subcredential, 36); + memcpy (innerInput + 32, subcredential, 36); i2p::crypto::HKDF (outerPlainText.data () + 1 + authDataLen, innerInput, 68, "ELS2_L2K", keys); } else @@ -538,20 +544,20 @@ namespace data if (innerPlainText[0] == NETDB_STORE_TYPE_STANDARD_LEASESET2 || innerPlainText[0] == NETDB_STORE_TYPE_META_LEASESET2) { // override store type and buffer - m_StoreType = innerPlainText[0]; + m_StoreType = innerPlainText[0]; SetBuffer (innerPlainText.data () + 1, lenInnerPlaintext - 1); // parse and verify Layer 2 ReadFromBuffer (innerPlainText.data () + 1, lenInnerPlaintext - 1); } else LogPrint (eLogError, "LeaseSet2: unexpected LeaseSet type ", (int)innerPlainText[0], " inside encrypted LeaseSet"); - } + } else { // we set actual length of encrypted buffer offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : blindedVerifier->GetSignatureLen (); SetBufferLen (offset); - } + } } // helper for ExtractClientAuthData @@ -564,12 +570,12 @@ namespace data { // clientKey_i = okm[0:31] // clientIV_i = okm[32:43] - i2p::crypto::ChaCha20 (authClients + i*40 + 8, 32, okm, okm + 32, authCookie); // clientCookie_i + i2p::crypto::ChaCha20 (authClients + i*40 + 8, 32, okm, okm + 32, authCookie); // clientCookie_i return true; - } + } } - return false; - } + return false; + } size_t LeaseSet2::ExtractClientAuthData (const uint8_t * buf, size_t len, const uint8_t * secret, const uint8_t * subcredential, uint8_t * authCookie) const { @@ -582,7 +588,7 @@ namespace data const uint8_t * ephemeralPublicKey = buf + offset; offset += 32; // ephemeralPublicKey uint16_t numClients = bufbe16toh (buf + offset); offset += 2; // clients const uint8_t * authClients = buf + offset; offset += numClients*40; // authClients - if (offset > len) + if (offset > len) { LogPrint (eLogError, "LeaseSet2: Too many clients ", numClients, " in DH auth data"); return 0; @@ -596,19 +602,19 @@ namespace data memcpy (authInput + 32, ck.GetPublicKey (), 32); // cpk_i memcpy (authInput + 64, subcredential, 36); uint8_t okm[64]; // 52 actual data - i2p::crypto::HKDF (ephemeralPublicKey, authInput, 100, "ELS2_XCA", okm); + i2p::crypto::HKDF (ephemeralPublicKey, authInput, 100, "ELS2_XCA", okm); if (!GetAuthCookie (authClients, numClients, okm, authCookie)) - LogPrint (eLogError, "LeaseSet2: Client cookie DH not found"); + LogPrint (eLogError, "LeaseSet2: Client cookie DH not found"); } else - LogPrint (eLogError, "LeaseSet2: Can't calculate authCookie: csk_i is not provided"); + LogPrint (eLogError, "LeaseSet2: Can't calculate authCookie: csk_i is not provided"); } else if (flag & 0x02) // PSK, bit 1 is set to 1 { const uint8_t * authSalt = buf + offset; offset += 32; // authSalt uint16_t numClients = bufbe16toh (buf + offset); offset += 2; // clients const uint8_t * authClients = buf + offset; offset += numClients*40; // authClients - if (offset > len) + if (offset > len) { LogPrint (eLogError, "LeaseSet2: Too many clients ", numClients, " in PSK auth data"); return 0; @@ -620,7 +626,7 @@ namespace data memcpy (authInput, secret, 32); memcpy (authInput + 32, subcredential, 36); uint8_t okm[64]; // 52 actual data - i2p::crypto::HKDF (authSalt, authInput, 68, "ELS2PSKA", okm); + i2p::crypto::HKDF (authSalt, authInput, 68, "ELS2PSKA", okm); if (!GetAuthCookie (authClients, numClients, okm, authCookie)) LogPrint (eLogError, "LeaseSet2: Client cookie PSK not found"); } @@ -628,7 +634,7 @@ namespace data LogPrint (eLogError, "LeaseSet2: Can't calculate authCookie: psk_i is not provided"); } else - LogPrint (eLogError, "LeaseSet2: unknown client auth type ", (int)flag); + LogPrint (eLogError, "LeaseSet2: unknown client auth type ", (int)flag); } return offset - 1; } @@ -637,12 +643,19 @@ namespace data { auto encryptor = m_Encryptor; // TODO: atomic if (encryptor) - encryptor->Encrypt (data, encrypted, ctx, true); + encryptor->Encrypt (data, encrypted, ctx, true); } - uint64_t LeaseSet2::ExtractTimestamp (const uint8_t * buf, size_t len) const + uint64_t LeaseSet2::ExtractExpirationTimestamp (const uint8_t * buf, size_t len) const { - if (len < 8) return 0; + uint64_t expiration = 0; + ExtractPublishedTimestamp (buf, len, expiration); + return expiration; + } + + uint64_t LeaseSet2::ExtractPublishedTimestamp (const uint8_t * buf, size_t len, uint64_t& expiration) const + { + if (len < 8) return 0; if (m_StoreType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) { // encrypted LS2 @@ -650,12 +663,13 @@ namespace data uint16_t blindedKeyType = bufbe16toh (buf + offset); offset += 2; std::unique_ptr blindedVerifier (i2p::data::IdentityEx::CreateVerifier (blindedKeyType)); if (!blindedVerifier) return 0 ; - auto blindedKeyLen = blindedVerifier->GetPublicKeyLen (); + auto blindedKeyLen = blindedVerifier->GetPublicKeyLen (); if (offset + blindedKeyLen + 6 >= len) return 0; offset += blindedKeyLen; - uint32_t timestamp = bufbe32toh (buf + offset); offset += 4; - uint16_t expires = bufbe16toh (buf + offset); offset += 2; - return (timestamp + expires)* 1000LL; + uint32_t timestamp = bufbe32toh (buf + offset); offset += 4; + uint16_t expires = bufbe16toh (buf + offset); offset += 2; + expiration = (timestamp + expires)* 1000LL; + return timestamp; } else { @@ -663,9 +677,10 @@ namespace data if (!identity) return 0; size_t offset = identity->GetFullLen (); if (offset + 6 >= len) return 0; - uint32_t timestamp = bufbe32toh (buf + offset); offset += 4; - uint16_t expires = bufbe16toh (buf + offset); offset += 2; - return (timestamp + expires)* 1000LL; + uint32_t timestamp = bufbe32toh (buf + offset); offset += 4; + uint16_t expires = bufbe16toh (buf + offset); offset += 2; + expiration = (timestamp + expires)* 1000LL; + return timestamp; } } @@ -762,36 +777,35 @@ namespace data return ident.Verify(ptr, leases - ptr, leases); } - LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, - const KeySections& encryptionKeys, - std::vector > tunnels, + LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, + const KeySections& encryptionKeys, std::vector > tunnels, bool isPublic, bool isPublishedEncrypted): LocalLeaseSet (keys.GetPublic (), nullptr, 0) { auto identity = keys.GetPublic (); - // assume standard LS2 + // assume standard LS2 int num = tunnels.size (); if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES; size_t keySectionsLen = 0; for (const auto& it: encryptionKeys) - keySectionsLen += 2/*key type*/ + 2/*key len*/ + it.keyLen/*key*/; + keySectionsLen += 2/*key type*/ + 2/*key len*/ + it.keyLen/*key*/; m_BufferLen = identity->GetFullLen () + 4/*published*/ + 2/*expires*/ + 2/*flag*/ + 2/*properties len*/ + 1/*num keys*/ + keySectionsLen + 1/*num leases*/ + num*LEASE2_SIZE + keys.GetSignatureLen (); uint16_t flags = 0; - if (keys.IsOfflineSignature ()) + if (keys.IsOfflineSignature ()) { flags |= LEASESET2_FLAG_OFFLINE_KEYS; - m_BufferLen += keys.GetOfflineSignature ().size (); + m_BufferLen += keys.GetOfflineSignature ().size (); } - if (isPublishedEncrypted) + if (isPublishedEncrypted) { flags |= LEASESET2_FLAG_PUBLISHED_ENCRYPTED; - isPublic = true; + isPublic = true; } if (!isPublic) flags |= LEASESET2_FLAG_UNPUBLISHED_LEASESET; m_Buffer = new uint8_t[m_BufferLen + 1]; - m_Buffer[0] = storeType; + m_Buffer[0] = storeType; // LS2 header auto offset = identity->ToBuffer (m_Buffer + 1, m_BufferLen) + 1; auto timestamp = i2p::util::GetSecondsSinceEpoch (); @@ -806,14 +820,14 @@ namespace data offset += offlineSignature.size (); } htobe16buf (m_Buffer + offset, 0); offset += 2; // properties len - // keys + // keys m_Buffer[offset] = encryptionKeys.size (); offset++; // 1 key for (const auto& it: encryptionKeys) - { - htobe16buf (m_Buffer + offset, it.keyType); offset += 2; // key type - htobe16buf (m_Buffer + offset, it.keyLen); offset += 2; // key len + { + htobe16buf (m_Buffer + offset, it.keyType); offset += 2; // key type + htobe16buf (m_Buffer + offset, it.keyLen); offset += 2; // key len memcpy (m_Buffer + offset, it.encryptionPublicKey, it.keyLen); offset += it.keyLen; // key - } + } // leases uint32_t expirationTime = 0; // in seconds m_Buffer[offset] = num; offset++; // num leases @@ -827,11 +841,11 @@ namespace data if (ts > expirationTime) expirationTime = ts; htobe32buf (m_Buffer + offset, ts); offset += 4; // end date - } + } // update expiration - SetExpirationTime (expirationTime*1000LL); + SetExpirationTime (expirationTime*1000LL); auto expires = expirationTime - timestamp; - htobe16buf (expiresBuf, expires > 0 ? expires : 0); + htobe16buf (expiresBuf, expires > 0 ? expires : 0); // sign keys.Sign (m_Buffer, offset, m_Buffer + offset); // LS + leading store type } @@ -845,7 +859,7 @@ namespace data m_Buffer[0] = storeType; } - LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr ls, const i2p::data::PrivateKeys& keys, + LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr ls, const i2p::data::PrivateKeys& keys, int authType, std::shared_ptr > authKeys): LocalLeaseSet2 (ls->GetIdentity ()), m_InnerLeaseSet (ls) { @@ -855,16 +869,16 @@ namespace data { if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_DH) layer1Flags |= 0x01; // DH, authentication scheme 0, auth bit 1 else if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_PSK) layer1Flags |= 0x03; // PSK, authentication scheme 1, auth bit 1 - if (layer1Flags) + if (layer1Flags) lenOuterPlaintext += 32 + 2 + authKeys->size ()*40; // auth data len - } + } size_t lenOuterCiphertext = lenOuterPlaintext + 32; - - m_BufferLen = 2/*blinded sig type*/ + 32/*blinded pub key*/ + 4/*published*/ + 2/*expires*/ + 2/*flags*/ + 2/*lenOuterCiphertext*/ + lenOuterCiphertext + 64/*signature*/; - m_Buffer = new uint8_t[m_BufferLen + 1]; + + m_BufferLen = 2/*blinded sig type*/ + 32/*blinded pub key*/ + 4/*published*/ + 2/*expires*/ + 2/*flags*/ + 2/*lenOuterCiphertext*/ + lenOuterCiphertext + 64/*signature*/; + m_Buffer = new uint8_t[m_BufferLen + 1]; m_Buffer[0] = NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; BlindedPublicKey blindedKey (ls->GetIdentity ()); - auto timestamp = i2p::util::GetSecondsSinceEpoch (); + auto timestamp = i2p::util::GetSecondsSinceEpoch (); char date[9]; i2p::util::GetDateString (timestamp, date); uint8_t blindedPriv[64], blindedPub[128]; // 64 and 128 max @@ -875,25 +889,25 @@ namespace data memcpy (m_Buffer + offset, blindedPub, publicKeyLen); offset += publicKeyLen; // Blinded Public Key htobe32buf (m_Buffer + offset, timestamp); offset += 4; // published timestamp (seconds) auto nextMidnight = (timestamp/86400LL + 1)*86400LL; // 86400 = 24*3600 seconds - auto expirationTime = ls->GetExpirationTime ()/1000LL; + auto expirationTime = ls->GetExpirationTime ()/1000LL; if (expirationTime > nextMidnight) expirationTime = nextMidnight; SetExpirationTime (expirationTime*1000LL); htobe16buf (m_Buffer + offset, expirationTime > timestamp ? expirationTime - timestamp : 0); offset += 2; // expires uint16_t flags = 0; - htobe16buf (m_Buffer + offset, flags); offset += 2; // flags + htobe16buf (m_Buffer + offset, flags); offset += 2; // flags htobe16buf (m_Buffer + offset, lenOuterCiphertext); offset += 2; // lenOuterCiphertext // outerChipherText - // Layer 1 + // Layer 1 uint8_t subcredential[36]; blindedKey.GetSubcredential (blindedPub, 32, subcredential); htobe32buf (subcredential + 32, timestamp); // outerInput = subcredential || publishedTimestamp // keys = HKDF(outerSalt, outerInput, "ELS2_L1K", 44) uint8_t keys1[64]; // 44 bytes actual data - RAND_bytes (m_Buffer + offset, 32); // outerSalt = CSRNG(32) + RAND_bytes (m_Buffer + offset, 32); // outerSalt = CSRNG(32) i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L1K", keys1); offset += 32; // outerSalt - uint8_t * outerPlainText = m_Buffer + offset; - m_Buffer[offset] = layer1Flags; offset++; // layer 1 flags + uint8_t * outerPlainText = m_Buffer + offset; + m_Buffer[offset] = layer1Flags; offset++; // layer 1 flags // auth data uint8_t innerInput[68]; // authCookie || subcredential || publishedTimestamp if (layer1Flags) @@ -901,20 +915,20 @@ namespace data RAND_bytes (innerInput, 32); // authCookie CreateClientAuthData (subcredential, authType, authKeys, innerInput, m_Buffer + offset); offset += 32 + 2 + authKeys->size ()*40; // auth clients - } + } // Layer 2 // keys = HKDF(outerSalt, outerInput, "ELS2_L2K", 44) uint8_t keys2[64]; // 44 bytes actual data - RAND_bytes (m_Buffer + offset, 32); // innerSalt = CSRNG(32) + RAND_bytes (m_Buffer + offset, 32); // innerSalt = CSRNG(32) if (layer1Flags) { memcpy (innerInput + 32, subcredential, 36); // + subcredential || publishedTimestamp - i2p::crypto::HKDF (m_Buffer + offset, innerInput, 68, "ELS2_L2K", keys2); + i2p::crypto::HKDF (m_Buffer + offset, innerInput, 68, "ELS2_L2K", keys2); } else i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L2K", keys2); // no authCookie - offset += 32; // innerSalt - m_Buffer[offset] = ls->GetStoreType (); + offset += 32; // innerSalt + m_Buffer[offset] = ls->GetStoreType (); memcpy (m_Buffer + offset + 1, ls->GetBuffer (), ls->GetBufferLen ()); i2p::crypto::ChaCha20 (m_Buffer + offset, lenInnerPlaintext, keys2, keys2 + 32, m_Buffer + offset); // encrypt Layer 2 offset += lenInnerPlaintext; @@ -922,14 +936,14 @@ namespace data // signature blindedSigner->Sign (m_Buffer, offset, m_Buffer + offset); // store hash - m_StoreHash = blindedKey.GetStoreHash (date); + m_StoreHash = blindedKey.GetStoreHash (date); } LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr identity, const uint8_t * buf, size_t len): - LocalLeaseSet2 (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2, identity, buf, len) + LocalLeaseSet2 (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2, identity, buf, len) { - // fill inner LeaseSet2 - auto blindedKey = std::make_shared(identity); + // fill inner LeaseSet2 + auto blindedKey = std::make_shared(identity); i2p::data::LeaseSet2 ls (buf, len, blindedKey); // inner layer if (ls.IsValid ()) { @@ -937,10 +951,10 @@ namespace data m_StoreHash = blindedKey->GetStoreHash (); } else - LogPrint (eLogError, "LeaseSet2: couldn't extract inner layer"); + LogPrint (eLogError, "LeaseSet2: couldn't extract inner layer"); } - void LocalEncryptedLeaseSet2::CreateClientAuthData (const uint8_t * subcredential, int authType, std::shared_ptr > authKeys, const uint8_t * authCookie, uint8_t * authData) const + void LocalEncryptedLeaseSet2::CreateClientAuthData (const uint8_t * subcredential, int authType, std::shared_ptr > authKeys, const uint8_t * authCookie, uint8_t * authData) const { if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_DH) { @@ -955,9 +969,9 @@ namespace data ek.Agree (it, authInput); // sharedSecret = DH(esk, cpk_i) memcpy (authInput + 32, it, 32); uint8_t okm[64]; // 52 actual data - i2p::crypto::HKDF (ek.GetPublicKey (), authInput, 100, "ELS2_XCA", okm); + i2p::crypto::HKDF (ek.GetPublicKey (), authInput, 100, "ELS2_XCA", okm); memcpy (authData, okm + 44, 8); authData += 8; // clientID_i - i2p::crypto::ChaCha20 (authCookie, 32, okm, okm + 32, authData); authData += 32; // clientCookie_i + i2p::crypto::ChaCha20 (authCookie, 32, okm, okm + 32, authData); authData += 32; // clientCookie_i } } else // assume PSK @@ -972,10 +986,10 @@ namespace data { memcpy (authInput, it, 32); uint8_t okm[64]; // 52 actual data - i2p::crypto::HKDF (authSalt, authInput, 68, "ELS2PSKA", okm); + i2p::crypto::HKDF (authSalt, authInput, 68, "ELS2PSKA", okm); memcpy (authData, okm + 44, 8); authData += 8; // clientID_i - i2p::crypto::ChaCha20 (authCookie, 32, okm, okm + 32, authData); authData += 32; // clientCookie_i - } + i2p::crypto::ChaCha20 (authCookie, 32, okm, okm + 32, authData); authData += 32; // clientCookie_i + } } } } diff --git a/libi2pd/LeaseSet.h b/libi2pd/LeaseSet.h index 0d255644..cd31bf30 100644 --- a/libi2pd/LeaseSet.h +++ b/libi2pd/LeaseSet.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef LEASE_SET_H__ #define LEASE_SET_H__ @@ -48,11 +56,11 @@ namespace data }; }; - typedef std::function LeaseInspectFunc; + typedef std::function LeaseInspectFunc; const size_t MAX_LS_BUFFER_SIZE = 3072; const size_t LEASE_SIZE = 44; // 32 + 4 + 8 - const size_t LEASE2_SIZE = 40; // 32 + 4 + 4 + const size_t LEASE2_SIZE = 40; // 32 + 4 + 4 const uint8_t MAX_NUM_LEASES = 16; const uint8_t NETDB_STORE_TYPE_LEASESET = 1; @@ -63,14 +71,14 @@ namespace data LeaseSet (const uint8_t * buf, size_t len, bool storeLeases = true); virtual ~LeaseSet () { delete[] m_EncryptionKey; delete[] m_Buffer; }; virtual void Update (const uint8_t * buf, size_t len, bool verifySignature = true); - bool IsNewer (const uint8_t * buf, size_t len) const; + virtual bool IsNewer (const uint8_t * buf, size_t len) const; void PopulateLeases (); // from buffer const uint8_t * GetBuffer () const { return m_Buffer; }; size_t GetBufferLen () const { return m_BufferLen; }; bool IsValid () const { return m_IsValid; }; const std::vector > GetNonExpiredLeases (bool withThreshold = true) const; - const std::vector > GetNonExpiredLeasesExcluding (LeaseInspectFunc exclude, bool withThreshold = true) const; + const std::vector > GetNonExpiredLeasesExcluding (LeaseInspectFunc exclude, bool withThreshold = true) const; bool HasExpiredLeases () const; bool IsExpired () const; bool IsEmpty () const { return m_Leases.empty (); }; @@ -80,7 +88,7 @@ namespace data { return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); }; virtual uint8_t GetStoreType () const { return NETDB_STORE_TYPE_LEASESET; }; virtual uint32_t GetPublishedTimestamp () const { return 0; }; // should be set for LeaseSet2 only - virtual std::shared_ptr GetTransientVerifier () const { return nullptr; }; + virtual std::shared_ptr GetTransientVerifier () const { return nullptr; }; virtual bool IsPublishedEncrypted () const { return false; }; // implements RoutingDestination @@ -97,7 +105,7 @@ namespace data // called from LeaseSet2 LeaseSet (bool storeLeases); void SetBuffer (const uint8_t * buf, size_t len); - void SetBufferLen (size_t len); + void SetBufferLen (size_t len); void SetIdentity (std::shared_ptr identity) { m_Identity = identity; }; void SetExpirationTime (uint64_t t) { m_ExpirationTime = t; }; void SetIsValid (bool isValid) { m_IsValid = isValid; }; @@ -106,7 +114,7 @@ namespace data private: void ReadFromBuffer (bool readIdentity = true, bool verifySignature = true); - virtual uint64_t ExtractTimestamp (const uint8_t * buf, size_t len) const; // returns max expiration time + virtual uint64_t ExtractExpirationTimestamp (const uint8_t * buf, size_t len) const; // returns max expiration time private: @@ -130,7 +138,7 @@ namespace data const uint8_t NETDB_STORE_TYPE_META_LEASESET2 = 7; const uint16_t LEASESET2_FLAG_OFFLINE_KEYS = 0x0001; - const uint16_t LEASESET2_FLAG_UNPUBLISHED_LEASESET = 0x0002; + const uint16_t LEASESET2_FLAG_UNPUBLISHED_LEASESET = 0x0002; const uint16_t LEASESET2_FLAG_PUBLISHED_ENCRYPTED = 0x0004; class LeaseSet2: public LeaseSet @@ -145,6 +153,7 @@ namespace data bool IsPublishedEncrypted () const { return m_IsPublishedEncrypted; }; std::shared_ptr GetTransientVerifier () const { return m_TransientVerifier; }; void Update (const uint8_t * buf, size_t len, bool verifySignature); + bool IsNewer (const uint8_t * buf, size_t len) const; // implements RoutingDestination void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const; @@ -160,12 +169,13 @@ namespace data template bool VerifySignature (Verifier& verifier, const uint8_t * buf, size_t len, size_t signatureOffset); - uint64_t ExtractTimestamp (const uint8_t * buf, size_t len) const; + uint64_t ExtractExpirationTimestamp (const uint8_t * buf, size_t len) const; + uint64_t ExtractPublishedTimestamp (const uint8_t * buf, size_t len, uint64_t& expiration) const; size_t ExtractClientAuthData (const uint8_t * buf, size_t len, const uint8_t * secret, const uint8_t * subcredential, uint8_t * authCookie) const; // subcredential is subcredential + timestamp, return length of autData without flag private: - uint8_t m_StoreType; + uint8_t m_StoreType; uint32_t m_PublishedTimestamp = 0; bool m_IsPublic = true, m_IsPublishedEncrypted = false; std::shared_ptr m_TransientVerifier; @@ -173,7 +183,7 @@ namespace data std::shared_ptr m_Encryptor; // for standardLS2 }; - // also called from Streaming.cpp + // also called from Streaming.cpp template std::shared_ptr ProcessOfflineSignature (const Verifier& verifier, const uint8_t * buf, size_t len, size_t& offset) { @@ -189,7 +199,7 @@ namespace data transientVerifier->SetPublicKey (buf + offset); offset += keyLen; if (offset + verifier->GetSignatureLen () >= len) return nullptr; if (!verifier->Verify (signedData, keyLen + 6, buf + offset)) return nullptr; - offset += verifier->GetSignatureLen (); + offset += verifier->GetSignatureLen (); return transientVerifier; } @@ -236,17 +246,18 @@ namespace data { uint16_t keyType, keyLen; const uint8_t * encryptionPublicKey; - }; + }; typedef std::vector KeySections; - - LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, - const KeySections& encryptionKeys, - std::vector > tunnels, + + LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, + const KeySections& encryptionKeys, + std::vector > tunnels, bool isPublic, bool isPublishedEncrypted = false); - LocalLeaseSet2 (uint8_t storeType, std::shared_ptr identity, const uint8_t * buf, size_t len); // from I2CP - + + LocalLeaseSet2 (uint8_t storeType, std::shared_ptr identity, const uint8_t * buf, size_t len); // from I2CP + virtual ~LocalLeaseSet2 () { delete[] m_Buffer; }; - + uint8_t * GetBuffer () const { return m_Buffer + 1; }; size_t GetBufferLen () const { return m_BufferLen; }; @@ -267,13 +278,13 @@ namespace data const int ENCRYPTED_LEASESET_AUTH_TYPE_DH = 1; const int ENCRYPTED_LEASESET_AUTH_TYPE_PSK = 2; - typedef i2p::data::Tag<32> AuthPublicKey; + typedef i2p::data::Tag<32> AuthPublicKey; class LocalEncryptedLeaseSet2: public LocalLeaseSet2 { public: - LocalEncryptedLeaseSet2 (std::shared_ptr ls, const i2p::data::PrivateKeys& keys, int authType = ENCRYPTED_LEASESET_AUTH_TYPE_NONE, std::shared_ptr > authKeys = nullptr); + LocalEncryptedLeaseSet2 (std::shared_ptr ls, const i2p::data::PrivateKeys& keys, int authType = ENCRYPTED_LEASESET_AUTH_TYPE_NONE, std::shared_ptr > authKeys = nullptr); LocalEncryptedLeaseSet2 (std::shared_ptr identity, const uint8_t * buf, size_t len); // from I2CP diff --git a/libi2pd/LittleBigEndian.h b/libi2pd/LittleBigEndian.h index 69f10ee9..8c081187 100644 --- a/libi2pd/LittleBigEndian.h +++ b/libi2pd/LittleBigEndian.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + // LittleBigEndian.h fixed for 64-bits added union // @@ -29,35 +37,35 @@ struct BigEndian; template struct LittleEndian { - union - { - unsigned char bytes[sizeof(T)]; - T raw_value; - }; + union + { + unsigned char bytes[sizeof(T)]; + T raw_value; + }; - LittleEndian(T t = T()) - { - operator =(t); - } + LittleEndian(T t = T()) + { + operator =(t); + } - LittleEndian(const LittleEndian & t) - { - raw_value = t.raw_value; - } + LittleEndian(const LittleEndian & t) + { + raw_value = t.raw_value; + } - LittleEndian(const BigEndian & t) - { - for (unsigned i = 0; i < sizeof(T); i++) - bytes[i] = t.bytes[sizeof(T)-1-i]; - } + LittleEndian(const BigEndian & t) + { + for (unsigned i = 0; i < sizeof(T); i++) + bytes[i] = t.bytes[sizeof(T)-1-i]; + } - operator const T() const - { - T t = T(); - for (unsigned i = 0; i < sizeof(T); i++) - t |= T(bytes[i]) << (i << 3); - return t; - } + operator const T() const + { + T t = T(); + for (unsigned i = 0; i < sizeof(T); i++) + t |= T(bytes[i]) << (i << 3); + return t; + } const T operator = (const T t) { @@ -66,68 +74,68 @@ struct LittleEndian return t; } - // operators + // operators - const T operator += (const T t) - { - return (*this = *this + t); - } + const T operator += (const T t) + { + return (*this = *this + t); + } - const T operator -= (const T t) - { - return (*this = *this - t); - } + const T operator -= (const T t) + { + return (*this = *this - t); + } - const T operator *= (const T t) - { - return (*this = *this * t); - } + const T operator *= (const T t) + { + return (*this = *this * t); + } - const T operator /= (const T t) - { - return (*this = *this / t); - } + const T operator /= (const T t) + { + return (*this = *this / t); + } - const T operator %= (const T t) - { - return (*this = *this % t); - } + const T operator %= (const T t) + { + return (*this = *this % t); + } - LittleEndian operator ++ (int) - { - LittleEndian tmp(*this); - operator ++ (); - return tmp; - } + LittleEndian operator ++ (int) + { + LittleEndian tmp(*this); + operator ++ (); + return tmp; + } - LittleEndian & operator ++ () - { - for (unsigned i = 0; i < sizeof(T); i++) - { - ++bytes[i]; - if (bytes[i] != 0) - break; - } - return (*this); - } + LittleEndian & operator ++ () + { + for (unsigned i = 0; i < sizeof(T); i++) + { + ++bytes[i]; + if (bytes[i] != 0) + break; + } + return (*this); + } - LittleEndian operator -- (int) - { - LittleEndian tmp(*this); - operator -- (); - return tmp; - } + LittleEndian operator -- (int) + { + LittleEndian tmp(*this); + operator -- (); + return tmp; + } - LittleEndian & operator -- () - { - for (unsigned i = 0; i < sizeof(T); i++) - { - --bytes[i]; - if (bytes[i] != (T)(-1)) - break; - } - return (*this); - } + LittleEndian & operator -- () + { + for (unsigned i = 0; i < sizeof(T); i++) + { + --bytes[i]; + if (bytes[i] != (T)(-1)) + break; + } + return (*this); + } }; #pragma pack(pop) @@ -137,105 +145,105 @@ struct LittleEndian template struct BigEndian { - union - { - unsigned char bytes[sizeof(T)]; - T raw_value; - }; + union + { + unsigned char bytes[sizeof(T)]; + T raw_value; + }; - BigEndian(T t = T()) - { - operator =(t); - } + BigEndian(T t = T()) + { + operator =(t); + } - BigEndian(const BigEndian & t) - { - raw_value = t.raw_value; - } + BigEndian(const BigEndian & t) + { + raw_value = t.raw_value; + } - BigEndian(const LittleEndian & t) - { - for (unsigned i = 0; i < sizeof(T); i++) - bytes[i] = t.bytes[sizeof(T)-1-i]; - } + BigEndian(const LittleEndian & t) + { + for (unsigned i = 0; i < sizeof(T); i++) + bytes[i] = t.bytes[sizeof(T)-1-i]; + } - operator const T() const - { - T t = T(); - for (unsigned i = 0; i < sizeof(T); i++) - t |= T(bytes[sizeof(T) - 1 - i]) << (i << 3); - return t; - } + operator const T() const + { + T t = T(); + for (unsigned i = 0; i < sizeof(T); i++) + t |= T(bytes[sizeof(T) - 1 - i]) << (i << 3); + return t; + } - const T operator = (const T t) - { - for (unsigned i = 0; i < sizeof(T); i++) - bytes[sizeof(T) - 1 - i] = t >> (i << 3); - return t; - } + const T operator = (const T t) + { + for (unsigned i = 0; i < sizeof(T); i++) + bytes[sizeof(T) - 1 - i] = t >> (i << 3); + return t; + } - // operators + // operators - const T operator += (const T t) - { - return (*this = *this + t); - } + const T operator += (const T t) + { + return (*this = *this + t); + } - const T operator -= (const T t) - { - return (*this = *this - t); - } + const T operator -= (const T t) + { + return (*this = *this - t); + } - const T operator *= (const T t) - { - return (*this = *this * t); - } + const T operator *= (const T t) + { + return (*this = *this * t); + } - const T operator /= (const T t) - { - return (*this = *this / t); - } + const T operator /= (const T t) + { + return (*this = *this / t); + } - const T operator %= (const T t) - { - return (*this = *this % t); - } + const T operator %= (const T t) + { + return (*this = *this % t); + } - BigEndian operator ++ (int) - { - BigEndian tmp(*this); - operator ++ (); - return tmp; - } + BigEndian operator ++ (int) + { + BigEndian tmp(*this); + operator ++ (); + return tmp; + } - BigEndian & operator ++ () - { - for (unsigned i = 0; i < sizeof(T); i++) - { - ++bytes[sizeof(T) - 1 - i]; - if (bytes[sizeof(T) - 1 - i] != 0) - break; - } - return (*this); - } + BigEndian & operator ++ () + { + for (unsigned i = 0; i < sizeof(T); i++) + { + ++bytes[sizeof(T) - 1 - i]; + if (bytes[sizeof(T) - 1 - i] != 0) + break; + } + return (*this); + } - BigEndian operator -- (int) - { - BigEndian tmp(*this); - operator -- (); - return tmp; - } + BigEndian operator -- (int) + { + BigEndian tmp(*this); + operator -- (); + return tmp; + } - BigEndian & operator -- () - { - for (unsigned i = 0; i < sizeof(T); i++) - { - --bytes[sizeof(T) - 1 - i]; - if (bytes[sizeof(T) - 1 - i] != (T)(-1)) - break; - } - return (*this); - } + BigEndian & operator -- () + { + for (unsigned i = 0; i < sizeof(T); i++) + { + --bytes[sizeof(T) - 1 - i]; + if (bytes[sizeof(T) - 1 - i] != (T)(-1)) + break; + } + return (*this); + } }; #pragma pack(pop) diff --git a/libi2pd/Log.cpp b/libi2pd/Log.cpp index 79b4a511..a0014841 100644 --- a/libi2pd/Log.cpp +++ b/libi2pd/Log.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2016, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -110,18 +110,18 @@ namespace log { } } - std::string str_tolower(std::string s) { - std::transform(s.begin(), s.end(), s.begin(), - // static_cast(std::tolower) // wrong - // [](int c){ return std::tolower(c); } // wrong - // [](char c){ return std::tolower(c); } // wrong - [](unsigned char c){ return std::tolower(c); } // correct - ); - return s; - } + std::string str_tolower(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), + // static_cast(std::tolower) // wrong + // [](int c){ return std::tolower(c); } // wrong + // [](char c){ return std::tolower(c); } // wrong + [](unsigned char c){ return std::tolower(c); } // correct + ); + return s; + } - void Log::SetLogLevel (const std::string& level_) { - std::string level=str_tolower(level_); + void Log::SetLogLevel (const std::string& level_) { + std::string level=str_tolower(level_); if (level == "none") { m_MinLevel = eLogNone; } else if (level == "error") { m_MinLevel = eLogError; } else if (level == "warn") { m_MinLevel = eLogWarning; } @@ -199,7 +199,6 @@ namespace log { void Log::SendTo (const std::string& path) { if (m_LogStream) m_LogStream = nullptr; // close previous - if (m_MinLevel == eLogNone) return; auto flags = std::ofstream::out | std::ofstream::app; auto os = std::make_shared (path, flags); if (os->is_open ()) @@ -237,5 +236,11 @@ namespace log { Log & Logger() { return logger; } + + static ThrowFunction g_ThrowFunction; + ThrowFunction GetThrowFunction () { return g_ThrowFunction; } + void SetThrowFunction (ThrowFunction f) { g_ThrowFunction = f; } + } // log } // i2p + diff --git a/libi2pd/Log.h b/libi2pd/Log.h index ba10b5e9..972a00e1 100644 --- a/libi2pd/Log.h +++ b/libi2pd/Log.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2016, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -17,6 +17,7 @@ #include #include #include +#include #include "Queue.h" #ifndef _WIN32 @@ -151,8 +152,12 @@ namespace log { }; Log & Logger(); + + typedef std::function ThrowFunction; + ThrowFunction GetThrowFunction (); + void SetThrowFunction (ThrowFunction f); } // log -} +} // i2p /** internal usage only -- folding args array to single string */ template @@ -197,4 +202,23 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept log.Append(msg); } +/** + * @brief Throw fatal error message with the list of arguments + * @param args Array of message parts + */ +template +void ThrowFatal (TArgs&&... args) noexcept +{ + auto f = i2p::log::GetThrowFunction (); + if (!f) return; + // fold message to single string + std::stringstream ss(""); +#if (__cplusplus >= 201703L) // C++ 17 or higher + (LogPrint (ss, std::forward(args)), ...); +#else + LogPrint (ss, std::forward(args)...); +#endif + f (ss.str ()); +} + #endif // LOG_H__ diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index 85d83bf0..8b44a795 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -114,7 +114,7 @@ namespace transport void NTCP2Establisher::KDF2Bob () { - KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetPub ()); + KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetPub ()); } void NTCP2Establisher::KDF3Alice () @@ -707,7 +707,7 @@ namespace transport auto existing = i2p::data::netdb.FindRouter (ri.GetRouterIdentity ()->GetIdentHash ()); // check if exists already SetRemoteIdentity (existing ? existing->GetRouterIdentity () : ri.GetRouterIdentity ()); if (m_Server.AddNTCP2Session (shared_from_this (), true)) - { + { Established (); ReceiveLength (); } @@ -1135,7 +1135,8 @@ namespace transport SendQueue (); else if (m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE) { - LogPrint (eLogWarning, "NTCP2: outgoing messages queue size exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE); + LogPrint (eLogWarning, "NTCP2: outgoing messages queue size to ", + GetIdentHashBase64(), " exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE); Terminate (); } } @@ -1148,7 +1149,7 @@ namespace transport NTCP2Server::NTCP2Server (): RunnableServiceWithWork ("NTCP2"), m_TerminationTimer (GetService ()), - m_Resolver(GetService ()) + m_ProxyType(eNoProxy), m_Resolver(GetService ()) { } @@ -1164,25 +1165,23 @@ namespace transport StartIOService (); if(UsingProxy()) { - LogPrint(eLogError, "NTCP2: USING PROXY "); + LogPrint(eLogInfo, "NTCP2: Using proxy to connect to peers"); // TODO: resolve proxy until it is resolved boost::asio::ip::tcp::resolver::query q(m_ProxyAddress, std::to_string(m_ProxyPort)); boost::system::error_code e; auto itr = m_Resolver.resolve(q, e); if(e) - { LogPrint(eLogError, "NTCP2: Failed to resolve proxy ", e.message()); - } else { m_ProxyEndpoint.reset (new boost::asio::ip::tcp::endpoint(*itr)); if (m_ProxyEndpoint) - LogPrint(eLogError, "NTCP2: m_ProxyEndpoint ", *m_ProxyEndpoint); + LogPrint(eLogDebug, "NTCP2: m_ProxyEndpoint ", *m_ProxyEndpoint); } } else { - LogPrint(eLogError, "NTCP2: NOTUSING PROXY "); + LogPrint(eLogInfo, "NTCP2: Proxy is not used"); auto& addresses = context.GetRouterInfo ().GetAddresses (); for (const auto& address: addresses) { @@ -1197,11 +1196,12 @@ namespace transport } catch ( std::exception & ex ) { - LogPrint(eLogError, "NTCP2: Failed to bind to ip4 port ",address->port, ex.what()); + LogPrint(eLogError, "NTCP2: Failed to bind to v4 port ", address->port, ex.what()); + ThrowFatal ("Unable to start IPv4 NTCP2 transport at port ", address->port, ": ", ex.what ()); continue; } - LogPrint (eLogInfo, "NTCP2: Start listening TCP port ", address->port); + LogPrint (eLogInfo, "NTCP2: Start listening v4 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)); } @@ -1216,11 +1216,14 @@ namespace transport 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); + 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, "NTCP2: failed to bind to ip6 port ", address->port); + } + catch ( std::exception & ex ) + { + LogPrint(eLogError, "NTCP2: failed to bind to v6 port ", address->port, ": ", ex.what()); + ThrowFatal ("Unable to start IPv6 NTCP2 transport at port ", address->port, ": ", ex.what ()); continue; } } @@ -1414,7 +1417,7 @@ namespace transport if ((*it)->IsEstablished () || (*it)->IsTerminationTimeoutExpired (ts)) { (*it)->Terminate (); - it = m_PendingIncomingSessions.erase (it); // etsablished of expired + it = m_PendingIncomingSessions.erase (it); // established of expired } else if ((*it)->IsTerminated ()) it = m_PendingIncomingSessions.erase (it); // already terminated @@ -1426,6 +1429,31 @@ namespace transport } } + void NTCP2Server::ConnectWithProxy (const std::string& host, uint16_t port, RemoteAddressType addrtype, std::shared_ptr conn) + { + if(!m_ProxyEndpoint) return; + GetService().post([this, host, port, addrtype, conn]() { + if (this->AddNTCP2Session (conn)) + { + + auto timer = std::make_shared(GetService()); + auto timeout = NTCP_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 (*m_ProxyEndpoint, std::bind (&NTCP2Server::HandleProxyConnect, this, std::placeholders::_1, conn, timer, host, port, addrtype)); + } + }); + } + void NTCP2Server::UseProxy(ProxyType proxytype, const std::string & addr, uint16_t port) { m_ProxyType = proxytype; @@ -1443,14 +1471,14 @@ namespace transport return; } switch (m_ProxyType) - { + { case eSocksProxy: { // TODO: support username/password auth etc static const uint8_t buff[3] = {0x05, 0x01, 0x00}; - boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff, 3), boost::asio::transfer_all(), - [] (const boost::system::error_code & ec, std::size_t transferred) - { + boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff, 3), boost::asio::transfer_all(), + [] (const boost::system::error_code & ec, std::size_t transferred) + { (void) transferred; if(ec) { @@ -1458,7 +1486,7 @@ namespace transport } }); auto readbuff = std::make_shared >(2); - boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 2), + boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 2), [this, readbuff, timer, conn, host, port, addrtype](const boost::system::error_code & ec, std::size_t transferred) { if(ec) @@ -1504,17 +1532,17 @@ namespace transport std::ostream out(&writebuff); out << req.to_string(); - boost::asio::async_write(conn->GetSocket(), writebuff.data(), boost::asio::transfer_all(), - [](const boost::system::error_code & ec, std::size_t transferred) - { + boost::asio::async_write(conn->GetSocket(), writebuff.data(), boost::asio::transfer_all(), + [](const boost::system::error_code & ec, std::size_t transferred) + { (void) transferred; if(ec) LogPrint(eLogError, "NTCP2: http proxy write error ", ec.message()); }); boost::asio::streambuf * readbuff = new boost::asio::streambuf; - boost::asio::async_read_until(conn->GetSocket(), *readbuff, "\r\n\r\n", - [this, readbuff, timer, conn] (const boost::system::error_code & ec, std::size_t transferred) + boost::asio::async_read_until(conn->GetSocket(), *readbuff, "\r\n\r\n", + [this, readbuff, timer, conn] (const boost::system::error_code & ec, std::size_t transferred) { if(ec) { @@ -1549,38 +1577,13 @@ namespace transport } default: LogPrint(eLogError, "NTCP2: unknown proxy type, invalid state"); - } + } } - void NTCP2Server::ConnectWithProxy (const std::string& host, uint16_t port, RemoteAddressType addrtype, std::shared_ptr conn) - { - if(!m_ProxyEndpoint) return; - GetService().post([this, host, port, addrtype, conn]() { - if (this->AddNTCP2Session (conn)) - { - - auto timer = std::make_shared(GetService()); - auto timeout = NTCP_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 (*m_ProxyEndpoint, std::bind (&NTCP2Server::HandleProxyConnect, this, std::placeholders::_1, conn, timer, host, port, addrtype)); - } - }); - } - void NTCP2Server::AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType addrtype) { // build request - size_t sz = 0; + size_t sz = 6; // header + port auto buff = std::make_shared >(256); auto readbuff = std::make_shared >(256); (*buff)[0] = 0x05; @@ -1590,37 +1593,27 @@ namespace transport if(addrtype == eIP4Address) { (*buff)[3] = 0x01; - auto addr = boost::asio::ip::address::from_string(host).to_v4(); - auto addrbytes = addr.to_bytes(); - auto addrsize = addrbytes.size(); - memcpy(buff->data () + 4, addrbytes.data(), addrsize); + auto addrbytes = boost::asio::ip::address::from_string(host).to_v4().to_bytes(); + sz += 4; + memcpy(buff->data () + 4, addrbytes.data(), 4); } else if (addrtype == eIP6Address) { (*buff)[3] = 0x04; - auto addr = boost::asio::ip::address::from_string(host).to_v6(); - auto addrbytes = addr.to_bytes(); - auto addrsize = addrbytes.size(); - memcpy(buff->data () + 4, addrbytes.data(), addrsize); + auto addrbytes = boost::asio::ip::address::from_string(host).to_v6().to_bytes(); + sz += 16; + memcpy(buff->data () + 4, addrbytes.data(), 16); } else if (addrtype == eHostname) { - (*buff)[3] = 0x03; - size_t addrsize = host.size(); - sz = addrsize + 1 + 4; - if (2 + sz > buff->size ()) - { - // too big - return; - } - (*buff)[4] = (uint8_t) addrsize; - memcpy(buff->data() + 5, host.c_str(), addrsize); + // We mustn't really fall here because all connections are made to IP addresses + LogPrint(eLogError, "NTCP2: Tried to connect to domain name via socks proxy"); + return; } - htobe16buf(buff->data () + sz, port); - sz += 2; - boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff->data (), sz), boost::asio::transfer_all(), - [](const boost::system::error_code & ec, std::size_t written) - { + htobe16buf(buff->data () + sz - 2, port); + boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff->data (), sz), boost::asio::transfer_all(), + [buff](const boost::system::error_code & ec, std::size_t written) + { if(ec) { LogPrint(eLogError, "NTCP2: failed to write handshake to socks proxy ", ec.message()); @@ -1628,9 +1621,9 @@ namespace transport } }); - boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 10), - [timer, conn, sz, readbuff](const boost::system::error_code & e, std::size_t transferred) - { + boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 10), + [timer, conn, sz, readbuff](const boost::system::error_code & e, std::size_t transferred) + { if(e) { LogPrint(eLogError, "NTCP2: socks proxy read error ", e.message()); @@ -1650,6 +1643,5 @@ namespace transport conn->Terminate(); }); } - } } diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h index 11ae5995..8b8c6acb 100644 --- a/libi2pd/NTCP2.h +++ b/libi2pd/NTCP2.h @@ -126,7 +126,6 @@ namespace transport { public: - NTCP2Session (NTCP2Server& server, std::shared_ptr in_RemoteRouter = nullptr); ~NTCP2Session (); void Terminate (); @@ -274,7 +273,7 @@ namespace transport std::map > m_NTCP2Sessions; std::list > m_PendingIncomingSessions; - ProxyType m_ProxyType =eNoProxy; + ProxyType m_ProxyType; std::string m_ProxyAddress; uint16_t m_ProxyPort; boost::asio::ip::tcp::resolver m_Resolver; diff --git a/libi2pd/NTCPSession.cpp b/libi2pd/NTCPSession.cpp index 9711ffb0..4d3f1da6 100644 --- a/libi2pd/NTCPSession.cpp +++ b/libi2pd/NTCPSession.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include @@ -26,7 +34,7 @@ namespace transport { std::shared_ptr session; }; - + NTCPSession::NTCPSession (NTCPServer& server, std::shared_ptr in_RemoteRouter): TransportSession (in_RemoteRouter, NTCP_ESTABLISH_TIMEOUT), m_Server (server), m_Socket (m_Server.GetService ()), @@ -468,7 +476,7 @@ namespace transport LogPrint (eLogError, "NTCP: Phase 4 read error: ", ecode.message (), ". Check your clock"); if (ecode != boost::asio::error::operation_aborted) { - // this router doesn't like us + // this router doesn't like us i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true); Terminate (); } @@ -736,13 +744,11 @@ namespace transport } } - void NTCPSession::SendTimeSyncMessage () { Send (nullptr); } - void NTCPSession::SendI2NPMessages (const std::vector >& msgs) { m_Server.GetService ().post (std::bind (&NTCPSession::PostI2NPMessages, shared_from_this (), msgs)); @@ -820,9 +826,13 @@ namespace transport try { m_NTCPAcceptor = 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 (eLogInfo, "NTCP: Start listening v6 TCP port ", address->port); + } + catch ( std::exception & ex ) + { /** fail to bind ip4 */ - LogPrint(eLogError, "NTCP: Failed to bind to ip4 port ",address->port, ex.what()); + LogPrint(eLogError, "NTCP: Failed to bind to v4 port ", address->port, ": ", ex.what()); + ThrowFatal ("Unable to start IPv4 NTCP transport at port ", address->port, ": ", ex.what ()); continue; } @@ -841,11 +851,14 @@ namespace transport m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port)); m_NTCPV6Acceptor->listen (); - LogPrint (eLogInfo, "NTCP: Start listening V6 TCP port ", address->port); + LogPrint (eLogInfo, "NTCP: Start listening v6 TCP port ", address->port); auto conn = std::make_shared (*this); m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6, this, conn, std::placeholders::_1)); - } catch ( std::exception & ex ) { - LogPrint(eLogError, "NTCP: failed to bind to ip6 port ", address->port); + } + catch ( std::exception & ex ) + { + LogPrint(eLogError, "NTCP: failed to bind to v6 port ", address->port, ": ", ex.what()); + ThrowFatal (eLogError, "Unable to start IPv6 NTCP transport at port ", address->port, ": ", ex.what ()); continue; } } @@ -897,7 +910,6 @@ namespace transport } } - void NTCPServer::Run () { while (m_IsRunning) @@ -1193,7 +1205,6 @@ namespace transport void NTCPServer::AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType addrtype) { - // build request size_t sz = 0; uint8_t buff[256]; diff --git a/libi2pd/NTCPSession.h b/libi2pd/NTCPSession.h index e2207eb7..d3aa6f7c 100644 --- a/libi2pd/NTCPSession.h +++ b/libi2pd/NTCPSession.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef NTCP_SESSION_H__ #define NTCP_SESSION_H__ diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index 5d452d1d..466016c5 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include @@ -216,10 +224,10 @@ namespace data LogPrint (eLogDebug, "NetDb: RouterInfo floodfill status updated: ", ident.ToBase64()); std::unique_lock l(m_FloodfillsMutex); if (wasFloodfill) - m_Floodfills.remove (r); - else + m_Floodfills.remove (r); + else m_Floodfills.push_back (r); - } + } } else { @@ -390,7 +398,7 @@ namespace data } } - m_Reseeder->Bootstrap (); + m_Reseeder->Bootstrap (); } void NetDb::ReseedFromFloodfill(const RouterInfo & ri, int numRouters, int numFloodfills) @@ -532,12 +540,12 @@ namespace data auto total = m_RouterInfos.size (); uint64_t expirationTimeout = NETDB_MAX_EXPIRATION_TIMEOUT*1000LL; uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); - auto uptime = i2p::context.GetUptime (); + auto uptime = i2p::context.GetUptime (); // routers don't expire if less than 90 or uptime is less than 1 hour bool checkForExpiration = total > NETDB_MIN_ROUTERS && uptime > 600; // 10 minutes if (checkForExpiration && uptime > 3600) // 1 hour expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL : - NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total; + NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total; for (auto& it: m_RouterInfos) { @@ -555,7 +563,7 @@ namespace data // find & mark expired routers if (it.second->UsesIntroducer ()) { - if (ts > it.second->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL) + if (ts > it.second->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL) // RouterInfo expires after 1 hour if uses introducer it.second->SetUnreachable (true); } @@ -873,7 +881,7 @@ namespace data std::shared_ptr replyMsg; if (lookupType == DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP) { - LogPrint (eLogInfo, "NetDb: exploratory close to ", key, " ", numExcluded, " excluded"); + LogPrint (eLogInfo, "NetDb: exploratory close to ", key, " ", numExcluded, " excluded"); std::set excludedRouters; for (int i = 0; i < numExcluded; i++) { @@ -894,7 +902,7 @@ namespace data } else { - if (lookupType == DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP || + if (lookupType == DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP || lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP) { auto router = FindRouter (ident); @@ -907,7 +915,7 @@ namespace data } } - if (!replyMsg && (lookupType == DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP || + if (!replyMsg && (lookupType == DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP || lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP)) { auto leaseSet = FindLeaseSet (ident); @@ -944,7 +952,7 @@ namespace data if (replyTunnelID) { // encryption might be used though tunnel only - if (flag & DATABASE_LOOKUP_ENCRYPTION_FLAG) // encrypted reply requested + if (flag & (DATABASE_LOOKUP_ENCRYPTION_FLAG | DATABASE_LOOKUP_ECIES_FLAG)) // encrypted reply requested { const uint8_t * sessionKey = excluded; const uint8_t numTags = excluded[32]; @@ -957,12 +965,12 @@ namespace data replyMsg = i2p::garlic::WrapECIESX25519AEADRatchetMessage (replyMsg, sessionKey, tag); } else - { + { const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag i2p::garlic::ElGamalAESSession garlic (sessionKey, sessionTag); replyMsg = garlic.WrapSingleMessage (replyMsg); - } - if (!replyMsg) + } + if (!replyMsg) LogPrint (eLogError, "NetDb: failed to wrap message"); } else @@ -1103,7 +1111,7 @@ namespace data { return !router->IsHidden () && router->IsSSUV6 (); }); - } + } std::shared_ptr NetDb::GetRandomIntroducer () const { @@ -1121,7 +1129,8 @@ namespace data { return !router->IsHidden () && router != compatibleWith && router->IsCompatible (*compatibleWith) && - (router->GetCaps () & RouterInfo::eHighBandwidth); + (router->GetCaps () & RouterInfo::eHighBandwidth) && + router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION; }); } diff --git a/libi2pd/NetDb.hpp b/libi2pd/NetDb.hpp index d9e10504..1c65969a 100644 --- a/libi2pd/NetDb.hpp +++ b/libi2pd/NetDb.hpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef NETDB_H__ #define NETDB_H__ // this file is called NetDb.hpp to resolve conflict with libc's netdb.h on case insensitive fs @@ -21,17 +29,19 @@ #include "Reseed.h" #include "NetDbRequests.h" #include "Family.h" +#include "version.h" namespace i2p { namespace data { const int NETDB_MIN_ROUTERS = 90; - const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60*60; // 1 hour, in seconds - const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65*60; - const int NETDB_MIN_EXPIRATION_TIMEOUT = 90*60; // 1.5 hours - const int NETDB_MAX_EXPIRATION_TIMEOUT = 27*60*60; // 27 hours - const int NETDB_PUBLISH_INTERVAL = 60*40; + const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60 * 60; // 1 hour, in seconds + const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65 * 60; + const int NETDB_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours + const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours + const int NETDB_PUBLISH_INTERVAL = 60 * 40; + const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 36); // 0.9.36 /** function for visiting a leaseset stored in a floodfill */ typedef std::function)> LeaseSetVisitor; @@ -61,13 +71,13 @@ namespace data std::shared_ptr FindRouterProfile (const IdentHash& ident) const; void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr); - void RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete = nullptr); + void RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete = nullptr); void HandleDatabaseStoreMsg (std::shared_ptr msg); void HandleDatabaseSearchReplyMsg (std::shared_ptr msg); void HandleDatabaseLookupMsg (std::shared_ptr msg); void HandleNTCP2RouterInfoMsg (std::shared_ptr m); - + std::shared_ptr GetRandomRouter () const; std::shared_ptr GetRandomRouter (std::shared_ptr compatibleWith) const; std::shared_ptr GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith) const; @@ -78,13 +88,13 @@ namespace data std::vector GetClosestFloodfills (const IdentHash& destination, size_t num, std::set& excluded, bool closeThanUsOnly = false) const; std::shared_ptr GetClosestNonFloodfill (const IdentHash& destination, const std::set& excluded) const; - std::shared_ptr GetRandomRouterInFamily(const std::string & fam) const; + std::shared_ptr GetRandomRouterInFamily(const std::string & fam) const; void SetUnreachable (const IdentHash& ident, bool unreachable); void PostI2NPMsg (std::shared_ptr msg); - /** set hidden mode, aka don't publish our RI to netdb and don't explore */ - void SetHidden(bool hide); + /** set hidden mode, aka don't publish our RI to netdb and don't explore */ + void SetHidden(bool hide); void Reseed (); Families& GetFamilies () { return m_Families; }; @@ -117,12 +127,13 @@ namespace data void ManageLeaseSets (); void ManageRequests (); - void ReseedFromFloodfill(const RouterInfo & ri, int numRouters=40, int numFloodfills=20); + void ReseedFromFloodfill(const RouterInfo & ri, int numRouters = 40, int numFloodfills = 20); std::shared_ptr AddRouterInfo (const uint8_t * buf, int len, bool& updated); std::shared_ptr AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len, bool& updated); - template - std::shared_ptr GetRandomRouter (Filter filter) const; + + template + std::shared_ptr GetRandomRouter (Filter filter) const; private: @@ -148,12 +159,12 @@ namespace data bool m_PersistProfiles; - /** router info we are bootstrapping from or nullptr if we are not currently doing that*/ - std::shared_ptr m_FloodfillBootstrap; + /** router info we are bootstrapping from or nullptr if we are not currently doing that*/ + std::shared_ptr m_FloodfillBootstrap; - /** true if in hidden mode */ - bool m_HiddenMode; + /** true if in hidden mode */ + bool m_HiddenMode; }; extern NetDb netdb; diff --git a/libi2pd/NetDbRequests.cpp b/libi2pd/NetDbRequests.cpp index f1853124..e7aab34c 100644 --- a/libi2pd/NetDbRequests.cpp +++ b/libi2pd/NetDbRequests.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include "Log.h" #include "I2NPProtocol.h" #include "Transports.h" @@ -14,8 +22,8 @@ namespace data std::shared_ptr msg; if(replyTunnel) msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, - replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, - &m_ExcludedPeers); + replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, + &m_ExcludedPeers); else msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers); if(router) @@ -158,4 +166,3 @@ namespace data } } } - diff --git a/libi2pd/NetDbRequests.h b/libi2pd/NetDbRequests.h index 7a7a55ab..16ea430d 100644 --- a/libi2pd/NetDbRequests.h +++ b/libi2pd/NetDbRequests.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef NETDB_REQUESTS_H__ #define NETDB_REQUESTS_H__ @@ -66,4 +74,3 @@ namespace data } #endif - diff --git a/libi2pd/Poly1305.cpp b/libi2pd/Poly1305.cpp index bf94c9a9..23098d74 100644 --- a/libi2pd/Poly1305.cpp +++ b/libi2pd/Poly1305.cpp @@ -7,12 +7,11 @@ */ -#if !OPENSSL_AEAD_CHACHA20_POLY1305 +#if !OPENSSL_AEAD_CHACHA20_POLY1305 namespace i2p { namespace crypto { - void Poly1305HMAC(uint64_t * out, const uint64_t * key, const uint8_t * buf, std::size_t sz) { Poly1305 p(key); diff --git a/libi2pd/Poly1305.h b/libi2pd/Poly1305.h index 800fbfd9..f91a037e 100644 --- a/libi2pd/Poly1305.h +++ b/libi2pd/Poly1305.h @@ -1,9 +1,9 @@ /** - This code is licensed under the MCGSI Public License - Copyright 2018 Jeff Becker - - Kovri go write your own code - + * This code is licensed under the MCGSI Public License + * Copyright 2018 Jeff Becker + * + * Kovri go write your own code + * */ #ifndef LIBI2PD_POLY1305_H #define LIBI2PD_POLY1305_H @@ -11,7 +11,7 @@ #include #include "Crypto.h" -#if !OPENSSL_AEAD_CHACHA20_POLY1305 +#if !OPENSSL_AEAD_CHACHA20_POLY1305 namespace i2p { namespace crypto @@ -24,7 +24,6 @@ namespace crypto namespace poly1305 { - struct LongBlock { unsigned long data[17]; @@ -252,8 +251,8 @@ namespace crypto poly1305::LongBlock m_HR; uint8_t m_Final; }; + void Poly1305HMAC(uint64_t * out, const uint64_t * key, const uint8_t * buf, std::size_t sz); - } } #endif diff --git a/libi2pd/Profiling.cpp b/libi2pd/Profiling.cpp index 3840eb32..850774d9 100644 --- a/libi2pd/Profiling.cpp +++ b/libi2pd/Profiling.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include diff --git a/libi2pd/Profiling.h b/libi2pd/Profiling.h index 4ba6702f..dab50e6b 100644 --- a/libi2pd/Profiling.h +++ b/libi2pd/Profiling.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef PROFILING_H__ #define PROFILING_H__ diff --git a/libi2pd/Queue.h b/libi2pd/Queue.h index 0438cc37..d43567a5 100644 --- a/libi2pd/Queue.h +++ b/libi2pd/Queue.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef QUEUE_H__ #define QUEUE_H__ diff --git a/libi2pd/Reseed.cpp b/libi2pd/Reseed.cpp index 98f95dc3..2812f413 100644 --- a/libi2pd/Reseed.cpp +++ b/libi2pd/Reseed.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include @@ -32,73 +40,76 @@ namespace data { } - /** @brief tries to bootstrap into I2P network (from local files and servers, with respect of options) - */ - void Reseeder::Bootstrap () - { - std::string su3FileName; i2p::config::GetOption("reseed.file", su3FileName); - std::string zipFileName; i2p::config::GetOption("reseed.zipfile", zipFileName); + /** + @brief tries to bootstrap into I2P network (from local files and servers, with respect of options) + */ + void Reseeder::Bootstrap () + { + std::string su3FileName; i2p::config::GetOption("reseed.file", su3FileName); + std::string zipFileName; i2p::config::GetOption("reseed.zipfile", zipFileName); - if (su3FileName.length() > 0) // bootstrap from SU3 file or URL - { - int num; - if (su3FileName.length() > 8 && su3FileName.substr(0, 8) == "https://") - { - num = ReseedFromSU3Url (su3FileName); // from https URL - } - else - { - num = ProcessSU3File (su3FileName.c_str ()); - } - if (num == 0) - LogPrint (eLogWarning, "Reseed: failed to reseed from ", su3FileName); - } - else if (zipFileName.length() > 0) // bootstrap from ZIP file - { - int num = ProcessZIPFile (zipFileName.c_str ()); - if (num == 0) - LogPrint (eLogWarning, "Reseed: failed to reseed from ", zipFileName); - } - else // bootstrap from reseed servers - { - int num = ReseedFromServers (); - if (num == 0) - LogPrint (eLogWarning, "Reseed: failed to reseed from servers"); - } - } + if (su3FileName.length() > 0) // bootstrap from SU3 file or URL + { + int num; + if (su3FileName.length() > 8 && su3FileName.substr(0, 8) == "https://") + { + num = ReseedFromSU3Url (su3FileName); // from https URL + } + else + { + num = ProcessSU3File (su3FileName.c_str ()); + } + if (num == 0) + LogPrint (eLogWarning, "Reseed: failed to reseed from ", su3FileName); + } + else if (zipFileName.length() > 0) // bootstrap from ZIP file + { + int num = ProcessZIPFile (zipFileName.c_str ()); + if (num == 0) + LogPrint (eLogWarning, "Reseed: failed to reseed from ", zipFileName); + } + else // bootstrap from reseed servers + { + int num = ReseedFromServers (); + if (num == 0) + LogPrint (eLogWarning, "Reseed: failed to reseed from servers"); + } + } - /** @brief bootstrap from random server, retry 10 times - * @return number of entries added to netDb - */ + /** + * @brief bootstrap from random server, retry 10 times + * @return number of entries added to netDb + */ int Reseeder::ReseedFromServers () { std::string reseedURLs; i2p::config::GetOption("reseed.urls", reseedURLs); - std::vector httpsReseedHostList; - boost::split(httpsReseedHostList, reseedURLs, boost::is_any_of(","), boost::token_compress_on); + std::vector httpsReseedHostList; + boost::split(httpsReseedHostList, reseedURLs, boost::is_any_of(","), boost::token_compress_on); - if (reseedURLs.length () == 0) - { - LogPrint (eLogWarning, "Reseed: No reseed servers specified"); - return 0; - } + if (reseedURLs.length () == 0) + { + LogPrint (eLogWarning, "Reseed: No reseed servers specified"); + return 0; + } - int reseedRetries = 0; - while (reseedRetries < 10) - { - auto ind = rand () % httpsReseedHostList.size (); - std::string reseedUrl = httpsReseedHostList[ind] + "i2pseeds.su3"; - auto num = ReseedFromSU3Url (reseedUrl); - if (num > 0) return num; // success - reseedRetries++; - } - LogPrint (eLogWarning, "Reseed: failed to reseed from servers after 10 attempts"); - return 0; + int reseedRetries = 0; + while (reseedRetries < 10) + { + auto ind = rand () % httpsReseedHostList.size (); + std::string reseedUrl = httpsReseedHostList[ind] + "i2pseeds.su3"; + auto num = ReseedFromSU3Url (reseedUrl); + if (num > 0) return num; // success + reseedRetries++; + } + LogPrint (eLogWarning, "Reseed: failed to reseed from servers after 10 attempts"); + return 0; } - /** @brief bootstrap from HTTPS URL with SU3 file - * @param url - * @return number of entries added to netDb - */ + /** + * @brief bootstrap from HTTPS URL with SU3 file + * @param url + * @return number of entries added to netDb + */ int Reseeder::ReseedFromSU3Url (const std::string& url) { LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", url); @@ -702,4 +713,3 @@ namespace data } } } - diff --git a/libi2pd/Reseed.h b/libi2pd/Reseed.h index a69969bf..345b45bf 100644 --- a/libi2pd/Reseed.h +++ b/libi2pd/Reseed.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef RESEED_H #define RESEED_H @@ -21,7 +29,7 @@ namespace data Reseeder(); ~Reseeder(); - void Bootstrap (); + void Bootstrap (); int ReseedFromServers (); int ReseedFromSU3Url (const std::string& url); int ProcessSU3File (const char * filename); diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index b86ed8f2..cbbd961e 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include "Config.h" @@ -338,10 +346,10 @@ namespace i2p { case low : /* not set */; break; case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break; // 'P' - case unlim : caps |= i2p::data::RouterInfo::eExtraBandwidth; - #if (__cplusplus >= 201703L) // C++ 17 or higher - [[fallthrough]]; - #endif + case unlim : caps |= i2p::data::RouterInfo::eExtraBandwidth; +#if (__cplusplus >= 201703L) // C++ 17 or higher + [[fallthrough]]; +#endif // no break here, extra + high means 'X' case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break; } diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index 28c324c4..6f08a87a 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef ROUTER_CONTEXT_H__ #define ROUTER_CONTEXT_H__ @@ -15,7 +23,7 @@ namespace i2p { const char ROUTER_INFO[] = "router.info"; const char ROUTER_KEYS[] = "router.keys"; - const char NTCP2_KEYS[] = "ntcp2.keys"; + const char NTCP2_KEYS[] = "ntcp2.keys"; const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes enum RouterStatus @@ -36,12 +44,12 @@ namespace i2p { private: - struct NTCP2PrivateKeys + struct NTCP2PrivateKeys { uint8_t staticPublicKey[32]; uint8_t staticPrivateKey[32]; uint8_t iv[16]; - }; + }; public: @@ -63,7 +71,7 @@ namespace i2p const uint8_t * GetNTCP2StaticPublicKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; }; const uint8_t * GetNTCP2StaticPrivateKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr; }; const uint8_t * GetNTCP2IV () const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; }; - i2p::crypto::X25519Keys& GetStaticKeys (); + i2p::crypto::X25519Keys& GetStaticKeys (); uint32_t GetUptime () const; // in seconds uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; }; @@ -77,7 +85,7 @@ 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, bool v4only = false); void UpdateNTCP2Address (bool enable); @@ -124,7 +132,7 @@ namespace i2p // implements GarlicDestination void HandleI2NPMessage (const uint8_t * buf, size_t len); - bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) { return false; }; // not implemented + bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) { return false; }; // not implemented private: @@ -141,7 +149,7 @@ namespace i2p i2p::data::PrivateKeys m_Keys; std::shared_ptr m_Decryptor; uint64_t m_LastUpdateTime; // in seconds - bool m_AcceptsTunnels, m_IsFloodfill; + bool m_AcceptsTunnels, m_IsFloodfill; std::chrono::time_point m_StartupTime; uint64_t m_BandwidthLimit; // allowed bandwidth int m_ShareRatio; diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index 37c2af7e..91c12b60 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include "I2PEndian.h" @@ -27,7 +35,7 @@ namespace data RouterInfo::RouterInfo (const std::string& fullPath): m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false), - m_SupportedTransports (0), m_Caps (0) + m_SupportedTransports (0), m_Caps (0), m_Version (0) { m_Addresses = boost::make_shared(); // create empty list m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; @@ -35,11 +43,12 @@ namespace data } RouterInfo::RouterInfo (const uint8_t * buf, int len): - m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), m_Caps (0) + m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), + m_Caps (0), m_Version (0) { m_Addresses = boost::make_shared(); // create empty list if (len <= MAX_RI_BUFFER_SIZE) - { + { m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; memcpy (m_Buffer, buf, len); m_BufferLen = len; @@ -172,7 +181,6 @@ namespace data LogPrint (eLogError, "RouterInfo: malformed message"); m_IsUnreachable = true; } - } void RouterInfo::ReadFromStream (std::istream& s) @@ -191,7 +199,7 @@ namespace data s.read ((char *)&address->cost, sizeof (address->cost)); s.read ((char *)&address->date, sizeof (address->date)); bool isNTCP2Only = false; - char transportStyle[6]; + char transportStyle[6]; auto transportStyleLen = ReadString (transportStyle, 6, s) - 1; if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2 { @@ -230,13 +238,13 @@ namespace data address->host.to_string (ecode); if (!ecode) #endif - { + { // add supported protocol if (address->host.is_v4 ()) supportedTransports |= (address->transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4; else supportedTransports |= (address->transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6; - } + } } } else if (!strcmp (key, "port")) @@ -261,15 +269,15 @@ namespace data { if (!address->ntcp2) address->ntcp2.reset (new NTCP2Ext ()); supportedTransports |= (address->host.is_v4 ()) ? eNTCP2V4 : eNTCP2V6; - Base64ToByteStream (value, strlen (value), address->ntcp2->staticKey, 32); - } + Base64ToByteStream (value, strlen (value), address->ntcp2->staticKey, 32); + } else if (!strcmp (key, "i")) // ntcp2 iv { if (!address->ntcp2) address->ntcp2.reset (new NTCP2Ext ()); supportedTransports |= (address->host.is_v4 ()) ? eNTCP2V4 : eNTCP2V6; - Base64ToByteStream (value, strlen (value), address->ntcp2->iv, 16); + Base64ToByteStream (value, strlen (value), address->ntcp2->iv, 16); address->ntcp2->isPublished = true; // presence if "i" means "published" - } + } else if (key[0] == 'i') { // introducers @@ -277,7 +285,7 @@ namespace data { LogPrint (eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped"); continue; - } + } introducers = true; size_t l = strlen(key); unsigned char index = key[l-1] - '0'; // TODO: @@ -308,7 +316,7 @@ namespace data } if (introducers) supportedTransports |= eSSUV4; // in case if host is not presented if (isNTCP2Only && address->ntcp2) address->ntcp2->isNTCP2Only = true; - if (supportedTransports) + if (supportedTransports) { addresses->push_back(address); m_SupportedTransports |= supportedTransports; @@ -340,6 +348,21 @@ namespace data // extract caps if (!strcmp (key, "caps")) ExtractCaps (value); + // extract version + else if (!strcmp (key, ROUTER_INFO_PROPERTY_VERSION)) + { + m_Version = 0; + char * ch = value; + while (*ch) + { + if (*ch >= '0' && *ch <= '9') + { + m_Version *= 10; + m_Version += (*ch - '0'); + } + ch++; + } + } // check netId else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID) && atoi (value) != i2p::context.GetNetID ()) { @@ -368,9 +391,10 @@ namespace data SetUnreachable (true); } - bool RouterInfo::IsFamily(const std::string & fam) const { - return m_Family == fam; - } + bool RouterInfo::IsFamily(const std::string & fam) const + { + return m_Family == fam; + } void RouterInfo::ExtractCaps (const char * value) { @@ -564,7 +588,7 @@ namespace data properties << '='; WriteString (boost::lexical_cast(address.port), properties); properties << ';'; - } + } if (address.IsNTCP2 ()) { // publish s and v for NTCP2 @@ -572,7 +596,7 @@ namespace data WriteString (address.ntcp2->staticKey.ToBase64 (), properties); properties << ';'; WriteString ("v", properties); properties << '='; WriteString ("2", properties); properties << ';'; - } + } uint16_t size = htobe16 (properties.str ().size ()); s.write ((char *)&size, sizeof (size)); @@ -726,7 +750,7 @@ namespace data addr->ntcp2->isNTCP2Only = true; // NTCP2 only address if (port) addr->ntcp2->isPublished = true; memcpy (addr->ntcp2->staticKey, staticKey, 32); - memcpy (addr->ntcp2->iv, iv, 16); + memcpy (addr->ntcp2->iv, iv, 16); m_Addresses->push_back(std::move(addr)); } @@ -838,7 +862,7 @@ namespace data m_SupportedTransports |= eNTCPV6 | eSSUV6 | eNTCP2V6; } - void RouterInfo::EnableV4 () + void RouterInfo::EnableV4 () { if (!IsV4 ()) m_SupportedTransports |= eNTCPV4 | eSSUV4 | eNTCP2V4; @@ -861,7 +885,7 @@ namespace data } } - void RouterInfo::DisableV4 () + void RouterInfo::DisableV4 () { if (IsV4 ()) { @@ -910,7 +934,7 @@ namespace data }); } - template + template std::shared_ptr RouterInfo::GetAddress (Filter filter) const { // TODO: make it more generic using comparator @@ -921,7 +945,7 @@ namespace data #endif for (const auto& address : *addresses) if (filter (address)) return address; - + return nullptr; } diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index b23625e0..ef902c0e 100644 --- a/libi2pd/RouterInfo.h +++ b/libi2pd/RouterInfo.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef ROUTER_INFO_H__ #define ROUTER_INFO_H__ @@ -19,6 +27,7 @@ namespace data const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets"; const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters"; const char ROUTER_INFO_PROPERTY_NETID[] = "netId"; + const char ROUTER_INFO_PROPERTY_VERSION[] = "router.version"; const char ROUTER_INFO_PROPERTY_FAMILY[] = "family"; const char ROUTER_INFO_PROPERTY_FAMILY_SIG[] = "family.sig"; @@ -142,6 +151,7 @@ namespace data void SetRouterIdentity (std::shared_ptr identity); std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); }; uint64_t GetTimestamp () const { return m_Timestamp; }; + int GetVersion () const { return m_Version; }; 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; @@ -201,8 +211,8 @@ namespace data void DeleteBuffer () { delete[] m_Buffer; m_Buffer = nullptr; }; bool IsNewer (const uint8_t * buf, size_t len) const; - /** return true if we are in a router family and the signature is valid */ - bool IsFamily(const std::string & fam) const; + /** return true if we are in a router family and the signature is valid */ + bool IsFamily(const std::string & fam) const; // implements RoutingDestination std::shared_ptr GetIdentity () const { return m_RouterIdentity; }; @@ -235,6 +245,7 @@ namespace data std::map m_Properties; bool m_IsUpdated, m_IsUnreachable; uint8_t m_SupportedTransports, m_Caps; + int m_Version; mutable std::shared_ptr m_Profile; }; } diff --git a/libi2pd/SSU.cpp b/libi2pd/SSU.cpp index 4eb958de..c435715f 100644 --- a/libi2pd/SSU.cpp +++ b/libi2pd/SSU.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include "Log.h" @@ -45,19 +53,37 @@ namespace transport void SSUServer::OpenSocket () { - m_Socket.open (boost::asio::ip::udp::v4()); - m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE)); - m_Socket.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE)); - m_Socket.bind (m_Endpoint); + try + { + m_Socket.open (boost::asio::ip::udp::v4()); + m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE)); + m_Socket.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE)); + m_Socket.bind (m_Endpoint); + LogPrint (eLogInfo, "SSU: Start listening v4 port ", m_Endpoint.port()); + } + catch ( std::exception & ex ) + { + LogPrint (eLogError, "SSU: failed to bind to v4 port ", m_Endpoint.port(), ": ", ex.what()); + ThrowFatal ("Unable to start IPv4 SSU transport at port ", m_Endpoint.port(), ": ", ex.what ()); + } } void SSUServer::OpenSocketV6 () { - m_SocketV6.open (boost::asio::ip::udp::v6()); - m_SocketV6.set_option (boost::asio::ip::v6_only (true)); - m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE)); - m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE)); - m_SocketV6.bind (m_EndpointV6); + try + { + m_SocketV6.open (boost::asio::ip::udp::v6()); + m_SocketV6.set_option (boost::asio::ip::v6_only (true)); + m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE)); + m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE)); + m_SocketV6.bind (m_EndpointV6); + LogPrint (eLogInfo, "SSU: Start listening v6 port ", m_EndpointV6.port()); + } + catch ( std::exception & ex ) + { + LogPrint (eLogError, "SSU: failed to bind to v6 port ", m_EndpointV6.port(), ": ", ex.what()); + ThrowFatal ("Unable to start IPv6 SSU transport at port ", m_Endpoint.port(), ": ", ex.what ()); + } } void SSUServer::Start () @@ -166,7 +192,7 @@ namespace transport m_Socket.close (); OpenSocket (); Receive (); - } + } } } } @@ -187,7 +213,7 @@ namespace transport m_SocketV6.close (); OpenSocketV6 (); ReceiveV6 (); - } + } } } } @@ -341,10 +367,10 @@ namespace transport { if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous { - if (session) - { - session->FlushData (); - session = nullptr; + if (session) + { + session->FlushData (); + session = nullptr; } auto it = sessions->find (packet->from); if (it != sessions->end ()) @@ -570,7 +596,7 @@ namespace transport { return session->GetState () == eSessionStateEstablished && session != excluded; } - ); + ); } template @@ -594,7 +620,7 @@ namespace transport { return session->GetState () == eSessionStateEstablished && session != excluded; } - ); + ); } std::set SSUServer::FindIntroducers (int maxNumIntroducers) @@ -610,7 +636,7 @@ namespace transport session->GetState () == eSessionStateEstablished && ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION; } - ); + ); if (session) { ret.insert (session.get ()); @@ -808,4 +834,3 @@ namespace transport } } } - diff --git a/libi2pd/SSU.h b/libi2pd/SSU.h index 10e5ec06..6a79f754 100644 --- a/libi2pd/SSU.h +++ b/libi2pd/SSU.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef SSU_H__ #define SSU_H__ @@ -40,7 +48,7 @@ namespace transport public: SSUServer (int port); - SSUServer (const boost::asio::ip::address & addr, int port); // ipv6 only constructor + SSUServer (const boost::asio::ip::address & addr, int port); // ipv6 only constructor ~SSUServer (); void Start (); void Stop (); @@ -135,4 +143,3 @@ namespace transport } #endif - diff --git a/libi2pd/SSUData.cpp b/libi2pd/SSUData.cpp index 866da277..5068f006 100644 --- a/libi2pd/SSUData.cpp +++ b/libi2pd/SSUData.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include "Log.h" @@ -496,7 +504,7 @@ namespace transport } // decay if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES || - i2p::util::GetSecondsSinceEpoch () > m_LastMessageReceivedTime + DECAY_INTERVAL) + i2p::util::GetSecondsSinceEpoch () > m_LastMessageReceivedTime + DECAY_INTERVAL) m_ReceivedMessages.clear (); ScheduleIncompleteMessagesCleanup (); @@ -504,4 +512,3 @@ namespace transport } } } - diff --git a/libi2pd/SSUData.h b/libi2pd/SSUData.h index fbd167bf..f4a5ba4f 100644 --- a/libi2pd/SSUData.h +++ b/libi2pd/SSUData.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef SSU_DATA_H__ #define SSU_DATA_H__ @@ -128,4 +136,3 @@ namespace transport } #endif - diff --git a/libi2pd/SSUSession.cpp b/libi2pd/SSUSession.cpp index 88dbcf04..73699d6a 100644 --- a/libi2pd/SSUSession.cpp +++ b/libi2pd/SSUSession.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include "version.h" #include "Crypto.h" @@ -336,7 +344,7 @@ namespace transport m_SignedData->Insert (payload, 4); // insert Alice's signed on time payload += 4; // signed-on time size_t paddingSize = (payload - buf) + m_RemoteIdentity->GetSignatureLen (); - paddingSize &= 0x0F; // %16 + paddingSize &= 0x0F; // %16 if (paddingSize > 0) paddingSize = 16 - paddingSize; payload += paddingSize; // verify signature @@ -934,7 +942,7 @@ namespace transport for (const auto& it: msgs) if (it) { - if (it->GetLength () <= SSU_MAX_I2NP_MESSAGE_SIZE) + if (it->GetLength () <= SSU_MAX_I2NP_MESSAGE_SIZE) m_Data.Send (it); else LogPrint (eLogError, "SSU: I2NP message of size ", it->GetLength (), " can't be sent. Dropped"); @@ -1206,4 +1214,3 @@ namespace transport } } } - diff --git a/libi2pd/SSUSession.h b/libi2pd/SSUSession.h index 8f81838a..066e01eb 100644 --- a/libi2pd/SSUSession.h +++ b/libi2pd/SSUSession.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef SSU_SESSION_H__ #define SSU_SESSION_H__ @@ -28,7 +36,7 @@ namespace transport const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes const int SSU_CLOCK_SKEW = 60; // in seconds - const size_t SSU_MAX_I2NP_MESSAGE_SIZE = 32768; + const size_t SSU_MAX_I2NP_MESSAGE_SIZE = 32768; // payload types (4 bits) const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0; @@ -81,12 +89,12 @@ namespace transport void Done (); void Failed (); const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; - + bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); }; void SendI2NPMessages (const std::vector >& msgs); void SendPeerTest (); // Alice - SessionState GetState () const { return m_State; }; + SessionState GetState () const { return m_State; }; size_t GetNumSentBytes () const { return m_NumSentBytes; }; size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; @@ -158,10 +166,7 @@ namespace transport std::unique_ptr m_SignedData; // we need it for SessionConfirmed only std::map > m_RelayRequests; // nonce->Charlie }; - - } } #endif - diff --git a/libi2pd/Signature.cpp b/libi2pd/Signature.cpp index 21d08f30..88ee4060 100644 --- a/libi2pd/Signature.cpp +++ b/libi2pd/Signature.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include "Log.h" #include "Signature.h" @@ -6,11 +14,11 @@ namespace i2p { namespace crypto { -#if OPENSSL_EDDSA +#if OPENSSL_EDDSA EDDSA25519Verifier::EDDSA25519Verifier (): m_Pkey (nullptr) { - m_MDCtx = EVP_MD_CTX_create (); + m_MDCtx = EVP_MD_CTX_create (); } EDDSA25519Verifier::~EDDSA25519Verifier () @@ -23,21 +31,21 @@ namespace crypto { m_Pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_ED25519, NULL, signingKey, 32); EVP_DigestVerifyInit (m_MDCtx, NULL, NULL, NULL, m_Pkey); - } - + } + bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const { return EVP_DigestVerify (m_MDCtx, signature, 64, buf, len); } - -#else + +#else EDDSA25519Verifier::EDDSA25519Verifier () { } EDDSA25519Verifier::~EDDSA25519Verifier () { - } + } void EDDSA25519Verifier::SetPublicKey (const uint8_t * signingKey) { @@ -45,8 +53,8 @@ namespace crypto BN_CTX * ctx = BN_CTX_new (); m_PublicKey = GetEd25519 ()->DecodePublicKey (m_PublicKeyEncoded, ctx); BN_CTX_free (ctx); - } - + } + bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const { uint8_t digest[64]; @@ -83,19 +91,19 @@ namespace crypto EDDSA25519SignerCompat::~EDDSA25519SignerCompat () { - } - + } + void EDDSA25519SignerCompat::Sign (const uint8_t * buf, int len, uint8_t * signature) const { GetEd25519 ()->Sign (m_ExpandedPrivateKey, m_PublicKeyEncoded, buf, len, signature); } - -#if OPENSSL_EDDSA + +#if OPENSSL_EDDSA EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey): m_Fallback (nullptr) - { + { m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_ED25519, NULL, signingPrivateKey, 32); - uint8_t publicKey[EDDSA25519_PUBLIC_KEY_LENGTH]; + uint8_t publicKey[EDDSA25519_PUBLIC_KEY_LENGTH]; size_t len = EDDSA25519_PUBLIC_KEY_LENGTH; EVP_PKEY_get_raw_public_key (m_Pkey, publicKey, &len); if (signingPublicKey && memcmp (publicKey, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH)) @@ -105,10 +113,10 @@ namespace crypto m_Fallback = new EDDSA25519SignerCompat (signingPrivateKey, signingPublicKey); } else - { - m_MDCtx = EVP_MD_CTX_create (); + { + m_MDCtx = EVP_MD_CTX_create (); EVP_DigestSignInit (m_MDCtx, NULL, NULL, NULL, m_Pkey); - } + } } EDDSA25519Signer::~EDDSA25519Signer () @@ -118,22 +126,20 @@ namespace crypto { EVP_MD_CTX_destroy (m_MDCtx); EVP_PKEY_free (m_Pkey); - } + } } void EDDSA25519Signer::Sign (const uint8_t * buf, int len, uint8_t * signature) const { if (m_Fallback) return m_Fallback->Sign (buf, len, signature); else - { - size_t l = 64; + { + size_t l = 64; uint8_t sig[64]; // temporary buffer for signature. openssl issue #7232 - EVP_DigestSign (m_MDCtx, sig, &l, buf, len); + EVP_DigestSign (m_MDCtx, sig, &l, buf, len); memcpy (signature, sig, 64); - } - } -#endif + } + } +#endif } } - - diff --git a/libi2pd/Signature.h b/libi2pd/Signature.h index 0c5f27d6..18084603 100644 --- a/libi2pd/Signature.h +++ b/libi2pd/Signature.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef SIGNATURE_H__ #define SIGNATURE_H__ @@ -46,9 +54,9 @@ namespace crypto { m_PublicKey = CreateDSA (); } - + void SetPublicKey (const uint8_t * signingKey) - { + { DSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL); } @@ -163,9 +171,9 @@ namespace crypto { m_PublicKey = EC_KEY_new_by_curve_name (curve); } - + void SetPublicKey (const uint8_t * signingKey) - { + { BIGNUM * x = BN_bin2bn (signingKey, keyLen/2, NULL); BIGNUM * y = BN_bin2bn (signingKey + keyLen/2, keyLen/2, NULL); EC_KEY_set_public_key_affine_coordinates (m_PublicKey, x, y); @@ -287,7 +295,7 @@ namespace crypto EDDSA25519Verifier (); void SetPublicKey (const uint8_t * signingKey); ~EDDSA25519Verifier (); - + bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const; size_t GetPublicKeyLen () const { return EDDSA25519_PUBLIC_KEY_LENGTH; }; @@ -298,10 +306,10 @@ namespace crypto #if OPENSSL_EDDSA EVP_PKEY * m_Pkey; EVP_MD_CTX * m_MDCtx; -#else +#else EDDSAPoint m_PublicKey; uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; -#endif +#endif }; class EDDSA25519SignerCompat: public Signer @@ -311,17 +319,17 @@ namespace crypto EDDSA25519SignerCompat (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey = nullptr); // we pass signingPublicKey to check if it matches private key ~EDDSA25519SignerCompat (); - + void Sign (const uint8_t * buf, int len, uint8_t * signature) const; const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; }; // for keys creation private: - + uint8_t m_ExpandedPrivateKey[64]; uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; }; -#if OPENSSL_EDDSA +#if OPENSSL_EDDSA class EDDSA25519Signer: public Signer { public: @@ -329,23 +337,23 @@ namespace crypto EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey = nullptr); // we pass signingPublicKey to check if it matches private key ~EDDSA25519Signer (); - + void Sign (const uint8_t * buf, int len, uint8_t * signature) const; private: EVP_PKEY * m_Pkey; - EVP_MD_CTX * m_MDCtx; + EVP_MD_CTX * m_MDCtx; EDDSA25519SignerCompat * m_Fallback; }; #else typedef EDDSA25519SignerCompat EDDSA25519Signer; - -#endif - + +#endif + inline void CreateEDDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) { -#if OPENSSL_EDDSA +#if OPENSSL_EDDSA EVP_PKEY *pkey = NULL; EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_ED25519, NULL); EVP_PKEY_keygen_init (pctx); @@ -355,12 +363,12 @@ namespace crypto EVP_PKEY_get_raw_public_key (pkey, signingPublicKey, &len); len = EDDSA25519_PRIVATE_KEY_LENGTH; EVP_PKEY_get_raw_private_key (pkey, signingPrivateKey, &len); - EVP_PKEY_free (pkey); -#else + EVP_PKEY_free (pkey); +#else RAND_bytes (signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH); EDDSA25519Signer signer (signingPrivateKey); memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH); -#endif +#endif } @@ -399,18 +407,18 @@ namespace crypto GOSTR3410Verifier (GOSTR3410ParamSet paramSet): m_ParamSet (paramSet), m_PublicKey (nullptr) { - } + } - void SetPublicKey (const uint8_t * signingKey) + void SetPublicKey (const uint8_t * signingKey) { BIGNUM * x = BN_bin2bn (signingKey, GetPublicKeyLen ()/2, NULL); BIGNUM * y = BN_bin2bn (signingKey + GetPublicKeyLen ()/2, GetPublicKeyLen ()/2, NULL); m_PublicKey = GetGOSTR3410Curve (m_ParamSet)->CreatePoint (x, y); BN_free (x); BN_free (y); } - ~GOSTR3410Verifier () - { - if (m_PublicKey) EC_POINT_free (m_PublicKey); + ~GOSTR3410Verifier () + { + if (m_PublicKey) EC_POINT_free (m_PublicKey); } bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const @@ -501,18 +509,18 @@ namespace crypto auto publicKey = GetEd25519 ()->GeneratePublicKey (m_PrivateKey, ctx); GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx); BN_CTX_free (ctx); - } + } ~RedDSA25519Signer () {}; - + void Sign (const uint8_t * buf, int len, uint8_t * signature) const { - GetEd25519 ()->SignRedDSA (m_PrivateKey, m_PublicKeyEncoded, buf, len, signature); + GetEd25519 ()->SignRedDSA (m_PrivateKey, m_PublicKeyEncoded, buf, len, signature); } - + const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; }; // for keys creation private: - + uint8_t m_PrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH]; uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; }; @@ -521,10 +529,9 @@ namespace crypto { GetEd25519 ()->CreateRedDSAPrivateKey (signingPrivateKey); RedDSA25519Signer signer (signingPrivateKey); - memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH); + memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH); } } } #endif - diff --git a/libi2pd/Siphash.h b/libi2pd/Siphash.h index 70822466..78b22b90 100644 --- a/libi2pd/Siphash.h +++ b/libi2pd/Siphash.h @@ -1,9 +1,9 @@ /** - This code is licensed under the MCGSI Public License - Copyright 2018 Jeff Becker - - Kovri go write your own code - + * This code is licensed under the MCGSI Public License + * Copyright 2018 Jeff Becker + * + * Kovri go write your own code + * */ #ifndef SIPHASH_H #define SIPHASH_H @@ -14,140 +14,140 @@ #if !OPENSSL_SIPHASH namespace i2p { -namespace crypto +namespace crypto { - namespace siphash - { - constexpr int crounds = 2; - constexpr int drounds = 4; + namespace siphash + { + constexpr int crounds = 2; + constexpr int drounds = 4; - inline uint64_t rotl(const uint64_t & x, int b) - { - uint64_t ret = x << b; - ret |= x >> (64 - b); - return ret; - } + inline uint64_t rotl(const uint64_t & x, int b) + { + uint64_t ret = x << b; + ret |= x >> (64 - b); + return ret; + } - inline void u32to8le(const uint32_t & v, uint8_t * p) - { - p[0] = (uint8_t) v; - p[1] = (uint8_t) (v >> 8); - p[2] = (uint8_t) (v >> 16); - p[3] = (uint8_t) (v >> 24); - } + inline void u32to8le(const uint32_t & v, uint8_t * p) + { + p[0] = (uint8_t) v; + p[1] = (uint8_t) (v >> 8); + p[2] = (uint8_t) (v >> 16); + p[3] = (uint8_t) (v >> 24); + } - inline void u64to8le(const uint64_t & v, uint8_t * p) - { - p[0] = v & 0xff; - p[1] = (v >> 8) & 0xff; - p[2] = (v >> 16) & 0xff; - p[3] = (v >> 24) & 0xff; - p[4] = (v >> 32) & 0xff; - p[5] = (v >> 40) & 0xff; - p[6] = (v >> 48) & 0xff; - p[7] = (v >> 56) & 0xff; - } + inline void u64to8le(const uint64_t & v, uint8_t * p) + { + p[0] = v & 0xff; + p[1] = (v >> 8) & 0xff; + p[2] = (v >> 16) & 0xff; + p[3] = (v >> 24) & 0xff; + p[4] = (v >> 32) & 0xff; + p[5] = (v >> 40) & 0xff; + p[6] = (v >> 48) & 0xff; + p[7] = (v >> 56) & 0xff; + } - inline uint64_t u8to64le(const uint8_t * p) - { - uint64_t i = 0; - int idx = 0; - while(idx < 8) - { - i |= ((uint64_t) p[idx]) << (idx * 8); - ++idx; - } - return i; - } - - inline void round(uint64_t & _v0, uint64_t & _v1, uint64_t & _v2, uint64_t & _v3) - { - _v0 += _v1; - _v1 = rotl(_v1, 13); - _v1 ^= _v0; - _v0 = rotl(_v0, 32); - _v2 += _v3; - _v3 = rotl(_v3, 16); - _v3 ^= _v2; - _v0 += _v3; - _v3 = rotl(_v3, 21); - _v3 ^= _v0; - _v2 += _v1; - _v1 = rotl(_v1, 17); - _v1 ^= _v2; - _v2 = rotl(_v2, 32); - } - } - - /** hashsz must be 8 or 16 */ - template - inline void Siphash(uint8_t * h, const uint8_t * buf, std::size_t bufsz, const uint8_t * key) - { - uint64_t v0 = 0x736f6d6570736575ULL; - uint64_t v1 = 0x646f72616e646f6dULL; - uint64_t v2 = 0x6c7967656e657261ULL; - uint64_t v3 = 0x7465646279746573ULL; - const uint64_t k0 = siphash::u8to64le(key); - const uint64_t k1 = siphash::u8to64le(key + 8); - uint64_t msg; - int i; - const uint8_t * end = buf + bufsz - (bufsz % sizeof(uint64_t)); - auto left = bufsz & 7; - uint64_t b = ((uint64_t)bufsz) << 56; - v3 ^= k1; - v2 ^= k0; - v1 ^= k1; - v0 ^= k0; + inline uint64_t u8to64le(const uint8_t * p) + { + uint64_t i = 0; + int idx = 0; + while(idx < 8) + { + i |= ((uint64_t) p[idx]) << (idx * 8); + ++idx; + } + return i; + } - if(hashsz == 16) v1 ^= 0xee; + inline void round(uint64_t & _v0, uint64_t & _v1, uint64_t & _v2, uint64_t & _v3) + { + _v0 += _v1; + _v1 = rotl(_v1, 13); + _v1 ^= _v0; + _v0 = rotl(_v0, 32); + _v2 += _v3; + _v3 = rotl(_v3, 16); + _v3 ^= _v2; + _v0 += _v3; + _v3 = rotl(_v3, 21); + _v3 ^= _v0; + _v2 += _v1; + _v1 = rotl(_v1, 17); + _v1 ^= _v2; + _v2 = rotl(_v2, 32); + } + } - while(buf != end) - { - msg = siphash::u8to64le(buf); - v3 ^= msg; - for(i = 0; i < siphash::crounds; ++i) - siphash::round(v0, v1, v2, v3); - - v0 ^= msg; - buf += 8; - } + /** hashsz must be 8 or 16 */ + template + inline void Siphash(uint8_t * h, const uint8_t * buf, std::size_t bufsz, const uint8_t * key) + { + uint64_t v0 = 0x736f6d6570736575ULL; + uint64_t v1 = 0x646f72616e646f6dULL; + uint64_t v2 = 0x6c7967656e657261ULL; + uint64_t v3 = 0x7465646279746573ULL; + const uint64_t k0 = siphash::u8to64le(key); + const uint64_t k1 = siphash::u8to64le(key + 8); + uint64_t msg; + int i; + const uint8_t * end = buf + bufsz - (bufsz % sizeof(uint64_t)); + auto left = bufsz & 7; + uint64_t b = ((uint64_t)bufsz) << 56; + v3 ^= k1; + v2 ^= k0; + v1 ^= k1; + v0 ^= k0; - while(left) - { - --left; - b |= ((uint64_t)(buf[left])) << (left * 8); - } + if(hashsz == 16) v1 ^= 0xee; - v3 ^= b; + while(buf != end) + { + msg = siphash::u8to64le(buf); + v3 ^= msg; + for(i = 0; i < siphash::crounds; ++i) + siphash::round(v0, v1, v2, v3); - for(i = 0; i < siphash::crounds; ++i) - siphash::round(v0, v1, v2, v3); + v0 ^= msg; + buf += 8; + } - v0 ^= b; + while(left) + { + --left; + b |= ((uint64_t)(buf[left])) << (left * 8); + } + + v3 ^= b; + + for(i = 0; i < siphash::crounds; ++i) + siphash::round(v0, v1, v2, v3); + + v0 ^= b; - if(hashsz == 16) - v2 ^= 0xee; - else - v2 ^= 0xff; + if(hashsz == 16) + v2 ^= 0xee; + else + v2 ^= 0xff; - for(i = 0; i < siphash::drounds; ++i) - siphash::round(v0, v1, v2, v3); + for(i = 0; i < siphash::drounds; ++i) + siphash::round(v0, v1, v2, v3); - b = v0 ^ v1 ^ v2 ^ v3; + b = v0 ^ v1 ^ v2 ^ v3; - siphash::u64to8le(b, h); + siphash::u64to8le(b, h); - if(hashsz == 8) return; + if(hashsz == 8) return; - v1 ^= 0xdd; + v1 ^= 0xdd; - for (i = 0; i < siphash::drounds; ++i) - siphash::round(v0, v1, v2, v3); + for (i = 0; i < siphash::drounds; ++i) + siphash::round(v0, v1, v2, v3); - b = v0 ^ v1 ^ v2 ^ v3; - siphash::u64to8le(b, h + 8); - } + b = v0 ^ v1 ^ v2 ^ v3; + siphash::u64to8le(b, h + 8); + } } } #endif diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index 3efcfb99..ff8915c0 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include "Crypto.h" #include "Log.h" #include "RouterInfo.h" @@ -34,7 +42,7 @@ namespace stream else { // partially - rem = len - offset; + rem = len - offset; memcpy (buf + offset, nextBuffer->GetRemaningBuffer (), len - offset); nextBuffer->offset += (len - offset); offset = len; // break @@ -60,10 +68,10 @@ namespace stream m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local), m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), - m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port), + m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port), m_WindowSize (MIN_WINDOW_SIZE), m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), - m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0) + m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0), m_MTU (STREAMING_MTU) { RAND_bytes ((uint8_t *)&m_RecvStreamID, 4); m_RemoteIdentity = remote->GetIdentity (); @@ -73,9 +81,9 @@ namespace stream m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local), m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service), - m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_WindowSize (MIN_WINDOW_SIZE), + m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_WindowSize (MIN_WINDOW_SIZE), m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), - m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0) + m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0), m_MTU (STREAMING_MTU) { RAND_bytes ((uint8_t *)&m_RecvStreamID, 4); } @@ -228,7 +236,7 @@ namespace stream Terminate (); return; } - + packet->offset = packet->GetPayload () - packet->buf; if (packet->GetLength () > 0) { @@ -258,7 +266,7 @@ namespace stream bool Stream::ProcessOptions (uint16_t flags, Packet * packet) { const uint8_t * optionData = packet->GetOptionData (); - size_t optionSize = packet->GetOptionSize (); + size_t optionSize = packet->GetOptionSize (); if (flags & PACKET_FLAG_DELAY_REQUESTED) optionData += 2; @@ -269,7 +277,7 @@ namespace stream m_RemoteIdentity = std::make_shared(optionData, optionSize); if (m_RemoteIdentity->IsRSA ()) { - LogPrint (eLogInfo, "Streaming: Incoming stream from RSA destination ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " Discarded"); + LogPrint (eLogInfo, "Streaming: Incoming stream from RSA destination ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " Discarded"); return false; } optionData += m_RemoteIdentity->GetFullLen (); @@ -306,11 +314,11 @@ namespace stream size_t offset = 0; m_TransientVerifier = i2p::data::ProcessOfflineSignature (m_RemoteIdentity, optionData, optionSize - (optionData - packet->GetOptionData ()), offset); optionData += offset; - if (!m_TransientVerifier) + if (!m_TransientVerifier) { LogPrint (eLogError, "Streaming: offline signature failed"); return false; - } + } } } @@ -322,7 +330,7 @@ namespace stream { memcpy (signature, optionData, signatureLen); memset (const_cast(optionData), 0, signatureLen); - bool verified = m_TransientVerifier ? + bool verified = m_TransientVerifier ? m_TransientVerifier->Verify (packet->GetBuffer (), packet->GetLength (), signature) : m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature); if (!verified) @@ -340,7 +348,7 @@ namespace stream return false; } } - return true; + return true; } void Stream::ProcessAck (Packet * packet) @@ -423,15 +431,8 @@ namespace stream size_t Stream::Send (const uint8_t * buf, size_t len) { - size_t sent = len; - while(len > MAX_PACKET_SIZE) - { - AsyncSend (buf, MAX_PACKET_SIZE, nullptr); - buf += MAX_PACKET_SIZE; - len -= MAX_PACKET_SIZE; - } AsyncSend (buf, len, nullptr); - return sent; + return len; } void Stream::AsyncSend (const uint8_t * buf, size_t len, SendHandler handler) @@ -442,7 +443,7 @@ namespace stream m_SendBuffer.Add (buf, len, handler); } else if (handler) - handler(boost::system::error_code ()); + handler(boost::system::error_code ()); m_Service.post (std::bind (&Stream::SendBuffer, shared_from_this ())); } @@ -478,8 +479,14 @@ namespace stream size++; // resend delay if (m_Status == eStreamStatusNew) { - // initial packet + // initial packet m_Status = eStreamStatusOpen; + if (!m_RemoteLeaseSet) m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());; + if (m_RemoteLeaseSet) + { + m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true); + m_MTU = m_RoutingSession->IsRatchets () ? STREAMING_MTU_RATCHETS : STREAMING_MTU; + } uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED | PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED; if (isNoAck) flags |= PACKET_FLAG_NO_ACK; @@ -489,11 +496,11 @@ namespace stream size += 2; // flags size_t identityLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetFullLen (); size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen (); - uint8_t * optionsSize = packet + size; // set options size later + uint8_t * optionsSize = packet + size; // set options size later size += 2; // options size m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen); size += identityLen; // from - htobe16buf (packet + size, STREAMING_MTU); + htobe16buf (packet + size, m_MTU); size += 2; // max packet size if (isOfflineSignature) { @@ -505,7 +512,7 @@ namespace stream memset (signature, 0, signatureLen); // zeroes for now size += signatureLen; // signature htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size - size += m_SendBuffer.Get (packet + size, STREAMING_MTU - size); // payload + size += m_SendBuffer.Get (packet + size, m_MTU); // payload m_LocalDestination.GetOwner ()->Sign (packet, size, signature); } else @@ -515,7 +522,7 @@ namespace stream size += 2; // flags htobuf16 (packet + size, 0); // no options size += 2; // options size - size += m_SendBuffer.Get(packet + size, STREAMING_MTU - size); // payload + size += m_SendBuffer.Get(packet + size, m_MTU); // payload } p->len = size; packets.push_back (p); @@ -655,7 +662,7 @@ namespace stream size += 4; // receiveStreamID htobe32buf (packet + size, m_SequenceNumber++); size += 4; // sequenceNum - htobe32buf (packet + size, m_LastReceivedSequenceNumber >= 0 ? m_LastReceivedSequenceNumber : 0); + htobe32buf (packet + size, m_LastReceivedSequenceNumber >= 0 ? m_LastReceivedSequenceNumber : 0); size += 4; // ack Through packet[size] = 0; size++; // NACK count @@ -749,14 +756,15 @@ namespace stream auto ts = i2p::util::GetMillisecondsSinceEpoch (); if (!m_CurrentRemoteLease || !m_CurrentRemoteLease->endDate || // excluded from LeaseSet - ts >= m_CurrentRemoteLease->endDate - i2p::data::LEASE_ENDDATE_THRESHOLD) + ts >= m_CurrentRemoteLease->endDate - i2p::data::LEASE_ENDDATE_THRESHOLD) UpdateCurrentRemoteLease (true); if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD) { std::vector msgs; for (auto it: packets) { - auto msg = m_RoutingSession->WrapSingleMessage (m_LocalDestination.CreateDataMessage (it->GetBuffer (), it->GetLength (), m_Port)); + auto msg = m_RoutingSession->WrapSingleMessage (m_LocalDestination.CreateDataMessage ( + it->GetBuffer (), it->GetLength (), m_Port, !m_RoutingSession->IsRatchets ())); msgs.push_back (i2p::tunnel::TunnelMessageBlock { i2p::tunnel::eDeliveryTypeTunnel, @@ -785,7 +793,7 @@ namespace stream if (ts > m_RoutingSession->GetLeaseSetSubmissionTime () + i2p::garlic::LEASET_CONFIRMATION_TIMEOUT) { // LeaseSet was not confirmed, should try other tunnels - LogPrint (eLogWarning, "Streaming: LeaseSet was not confirmed in ", i2p::garlic::LEASET_CONFIRMATION_TIMEOUT, " milliseconds. Trying to resubmit"); + LogPrint (eLogWarning, "Streaming: LeaseSet was not confirmed in ", i2p::garlic::LEASET_CONFIRMATION_TIMEOUT, " milliseconds. Trying to resubmit"); m_RoutingSession->SetSharedRoutingPath (nullptr); m_CurrentOutboundTunnel = nullptr; m_CurrentRemoteLease = nullptr; @@ -843,14 +851,14 @@ namespace stream switch (m_NumResendAttempts) { case 1: // congesion avoidance - m_WindowSize /= 2; + m_WindowSize >>= 1; // /2 if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE; break; case 2: m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change first time -#if (__cplusplus >= 201703L) // C++ 17 or higher - [[fallthrough]]; -#endif +#if (__cplusplus >= 201703L) // C++ 17 or higher + [[fallthrough]]; +#endif // no break here case 4: if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); @@ -911,7 +919,7 @@ namespace stream // LeaseSet updated m_RemoteIdentity = m_RemoteLeaseSet->GetIdentity (); m_TransientVerifier = m_RemoteLeaseSet->GetTransientVerifier (); - } + } } if (m_RemoteLeaseSet) { @@ -926,7 +934,7 @@ namespace stream m_LocalDestination.GetOwner ()->RequestDestinationWithEncryptedLeaseSet ( std::make_shared(m_RemoteIdentity)); else - m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); + m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold } if (!leases.empty ()) @@ -994,7 +1002,7 @@ namespace stream { std::unique_lock l(m_StreamsMutex); for (auto it: m_Streams) - it.second->Terminate (false); // we delete here + it.second->Terminate (false); // we delete here m_Streams.clear (); m_IncomingStreams.clear (); } @@ -1136,8 +1144,8 @@ namespace stream return false; DeleteStream (it->second); return true; - } - + } + void StreamingDestination::SetAcceptor (const Acceptor& acceptor) { m_Acceptor = acceptor; // we must set it immediately for IsAcceptorSet @@ -1164,7 +1172,7 @@ namespace stream m_Owner->GetService ().post([acceptor, this](void) { if (!m_PendingIncomingStreams.empty ()) - { + { acceptor (m_PendingIncomingStreams.front ()); m_PendingIncomingStreams.pop_front (); if (m_PendingIncomingStreams.empty ()) @@ -1207,17 +1215,16 @@ namespace stream DeletePacket (uncompressed); } - std::shared_ptr StreamingDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort) + std::shared_ptr StreamingDestination::CreateDataMessage ( + const uint8_t * payload, size_t len, uint16_t toPort, bool checksum) { - auto msg = NewI2NPShortMessage (); - if (!m_Gzip || len <= i2p::stream::COMPRESSION_THRESHOLD_SIZE) - m_Deflator.SetCompressionLevel (Z_NO_COMPRESSION); - else - m_Deflator.SetCompressionLevel (Z_DEFAULT_COMPRESSION); + auto msg = m_I2NPMsgsPool.AcquireShared (); uint8_t * buf = msg->GetPayload (); buf += 4; // reserve for lengthlength msg->len += 4; - size_t size = m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len); + size_t size = (!m_Gzip || len <= i2p::stream::COMPRESSION_THRESHOLD_SIZE)? + i2p::data::GzipNoCompression (payload, len, buf, msg->maxLen - msg->len): + m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len); if (size) { htobe32buf (msg->GetPayload (), size); // length @@ -1225,7 +1232,7 @@ namespace stream htobe16buf (buf + 6, toPort); // destination port buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol msg->len += size; - msg->FillI2NPMessageHeader (eI2NPData); + msg->FillI2NPMessageHeader (eI2NPData, 0, checksum); } else msg = nullptr; diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h index 985a7eca..a56b0565 100644 --- a/libi2pd/Streaming.h +++ b/libi2pd/Streaming.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef STREAMING_H__ #define STREAMING_H__ @@ -41,6 +49,7 @@ namespace stream const uint16_t PACKET_FLAG_OFFLINE_SIGNATURE = 0x0800; const size_t STREAMING_MTU = 1730; + const size_t STREAMING_MTU_RATCHETS = 1812; const size_t MAX_PACKET_SIZE = 4096; const size_t COMPRESSION_THRESHOLD_SIZE = 66; const int MAX_NUM_RESEND_ATTEMPTS = 6; @@ -234,6 +243,7 @@ namespace stream int m_WindowSize, m_RTT, m_RTO, m_AckDelay; uint64_t m_LastWindowSizeIncreaseTime; int m_NumResendAttempts; + size_t m_MTU; }; class StreamingDestination: public std::enable_shared_from_this @@ -255,20 +265,18 @@ namespace stream void ResetAcceptor (); bool IsAcceptorSet () const { return m_Acceptor != nullptr; }; void AcceptOnce (const Acceptor& acceptor); + void AcceptOnceAcceptor (std::shared_ptr stream, Acceptor acceptor, Acceptor prev); std::shared_ptr GetOwner () const { return m_Owner; }; void SetOwner (std::shared_ptr owner) { m_Owner = owner; }; uint16_t GetLocalPort () const { return m_LocalPort; }; void HandleDataMessagePayload (const uint8_t * buf, size_t len); - std::shared_ptr CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort); + std::shared_ptr CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort, bool checksum = true); Packet * NewPacket () { return m_PacketsPool.Acquire(); } void DeletePacket (Packet * p) { return m_PacketsPool.Release(p); } - - void AcceptOnceAcceptor (std::shared_ptr stream, Acceptor acceptor, Acceptor prev); - private: void HandleNextPacket (Packet * packet); @@ -289,6 +297,7 @@ namespace stream std::map > m_SavedPackets; // receiveStreamID->packets, arrived before SYN i2p::util::MemoryPool m_PacketsPool; + i2p::util::MemoryPool > m_I2NPMsgsPool; public: diff --git a/libi2pd/Tag.h b/libi2pd/Tag.h index 010b160f..3856abd9 100644 --- a/libi2pd/Tag.h +++ b/libi2pd/Tag.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef TAG_H__ #define TAG_H__ @@ -16,93 +24,91 @@ namespace i2p { namespace data { - -template -class Tag -{ - BOOST_STATIC_ASSERT_MSG(sz % 8 == 0, "Tag size must be multiple of 8 bytes"); - -public: - - Tag () = default; - Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); } - - bool operator== (const Tag& other) const { return !memcmp (m_Buf, other.m_Buf, sz); } - bool operator!= (const Tag& other) const { return !(*this == other); } - bool operator< (const Tag& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; } - - uint8_t * operator()() { return m_Buf; } - const uint8_t * operator()() const { return m_Buf; } - - operator uint8_t * () { return m_Buf; } - operator const uint8_t * () const { return m_Buf; } - - const uint8_t * data() const { return m_Buf; } - const uint64_t * GetLL () const { return ll; } - - bool IsZero () const + template + class Tag { - for (size_t i = 0; i < sz/8; ++i) - if (ll[i]) return false; - return true; - } + BOOST_STATIC_ASSERT_MSG(sz % 8 == 0, "Tag size must be multiple of 8 bytes"); - void Fill(uint8_t c) - { - memset(m_Buf, c, sz); - } + public: - void Randomize() - { - RAND_bytes(m_Buf, sz); - } + Tag () = default; + Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); } - std::string ToBase64 () const - { - char str[sz*2]; - size_t l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2); - return std::string (str, str + l); - } + bool operator== (const Tag& other) const { return !memcmp (m_Buf, other.m_Buf, sz); } + bool operator!= (const Tag& other) const { return !(*this == other); } + bool operator< (const Tag& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; } - std::string ToBase32 () const - { - char str[sz*2]; - size_t l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2); - return std::string (str, str + l); - } + uint8_t * operator()() { return m_Buf; } + const uint8_t * operator()() const { return m_Buf; } - size_t FromBase32 (const std::string& s) - { - return i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz); - } + operator uint8_t * () { return m_Buf; } + operator const uint8_t * () const { return m_Buf; } - size_t FromBase64 (const std::string& s) - { - return i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz); - } + const uint8_t * data() const { return m_Buf; } + const uint64_t * GetLL () const { return ll; } -private: + bool IsZero () const + { + for (size_t i = 0; i < sz/8; ++i) + if (ll[i]) return false; + return true; + } - union // 8 bytes aligned - { - uint8_t m_Buf[sz]; - uint64_t ll[sz/8]; + void Fill(uint8_t c) + { + memset(m_Buf, c, sz); + } + + void Randomize() + { + RAND_bytes(m_Buf, sz); + } + + std::string ToBase64 () const + { + char str[sz*2]; + size_t l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2); + return std::string (str, str + l); + } + + std::string ToBase32 () const + { + char str[sz*2]; + size_t l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2); + return std::string (str, str + l); + } + + size_t FromBase32 (const std::string& s) + { + return i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz); + } + + size_t FromBase64 (const std::string& s) + { + return i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz); + } + + private: + + union // 8 bytes aligned + { + uint8_t m_Buf[sz]; + uint64_t ll[sz/8]; + }; }; -}; - } // data } // i2p namespace std { - // hash for std::unordered_map - template struct hash > - { - size_t operator()(const i2p::data::Tag& s) const - { - return s.GetLL ()[0]; - } - }; + // hash for std::unordered_map + template struct hash > + { + size_t operator()(const i2p::data::Tag& s) const + { + return s.GetLL ()[0]; + } + }; } #endif /* TAG_H__ */ diff --git a/libi2pd/Timestamp.cpp b/libi2pd/Timestamp.cpp index acc9cb10..4362a878 100644 --- a/libi2pd/Timestamp.cpp +++ b/libi2pd/Timestamp.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include @@ -24,26 +32,26 @@ namespace util static uint64_t GetLocalMillisecondsSinceEpoch () { return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count (); + std::chrono::system_clock::now().time_since_epoch()).count (); } static uint32_t GetLocalHoursSinceEpoch () { return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count (); + std::chrono::system_clock::now().time_since_epoch()).count (); } static uint64_t GetLocalSecondsSinceEpoch () { return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count (); + std::chrono::system_clock::now().time_since_epoch()).count (); } static int64_t g_TimeOffset = 0; // in seconds static void SyncTimeWithNTP (const std::string& address) { - LogPrint (eLogInfo, "Timestamp: NTP request to ", address); + LogPrint (eLogInfo, "Timestamp: NTP request to ", address); boost::asio::io_service service; boost::asio::ip::udp::resolver::query query (boost::asio::ip::udp::v4 (), address, "ntp"); boost::system::error_code ec; @@ -148,11 +156,11 @@ namespace util } void NTPTimeSync::Sync () - { + { if (m_NTPServersList.size () > 0) SyncTimeWithNTP (m_NTPServersList[rand () % m_NTPServersList.size ()]); else - m_IsRunning = false; + m_IsRunning = false; if (m_IsRunning) { @@ -200,4 +208,3 @@ namespace util } } } - diff --git a/libi2pd/Timestamp.h b/libi2pd/Timestamp.h index 31dc86aa..91175a49 100644 --- a/libi2pd/Timestamp.h +++ b/libi2pd/Timestamp.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef TIMESTAMP_H__ #define TIMESTAMP_H__ @@ -15,8 +23,8 @@ namespace util uint32_t GetHoursSinceEpoch (); uint64_t GetSecondsSinceEpoch (); - void GetCurrentDate (char * date); // returns date as YYYYMMDD string, 9 bytes - void GetDateString (uint64_t timestamp, char * date); // timestap is seconds since epoch, returns date as YYYYMMDD string, 9 bytes + void GetCurrentDate (char * date); // returns date as YYYYMMDD string, 9 bytes + void GetDateString (uint64_t timestamp, char * date); // timestap is seconds since epoch, returns date as YYYYMMDD string, 9 bytes class NTPTimeSync { @@ -26,17 +34,17 @@ namespace util ~NTPTimeSync (); void Start (); - void Stop (); + void Stop (); private: - + void Run (); - void Sync (); + void Sync (); private: bool m_IsRunning; - std::unique_ptr m_Thread; + std::unique_ptr m_Thread; boost::asio::io_service m_Service; boost::asio::deadline_timer m_Timer; int m_SyncInterval; @@ -46,4 +54,3 @@ namespace util } #endif - diff --git a/libi2pd/TransitTunnel.cpp b/libi2pd/TransitTunnel.cpp index 1adc4178..73ca977c 100644 --- a/libi2pd/TransitTunnel.cpp +++ b/libi2pd/TransitTunnel.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include "I2PEndian.h" #include "Log.h" @@ -12,7 +20,7 @@ namespace i2p namespace tunnel { TransitTunnel::TransitTunnel (uint32_t receiveTunnelID, - const uint8_t * nextIdent, uint32_t nextTunnelID, + const uint8_t * nextIdent, uint32_t nextTunnelID, const uint8_t * layerKey,const uint8_t * ivKey): TunnelBase (receiveTunnelID, nextTunnelID, nextIdent) { @@ -88,7 +96,7 @@ namespace tunnel std::shared_ptr CreateTransitTunnel (uint32_t receiveTunnelID, const uint8_t * nextIdent, uint32_t nextTunnelID, - const uint8_t * layerKey,const uint8_t * ivKey, + const uint8_t * layerKey,const uint8_t * ivKey, bool isGateway, bool isEndpoint) { if (isEndpoint) diff --git a/libi2pd/TransitTunnel.h b/libi2pd/TransitTunnel.h index 5b891dc3..e71ec750 100644 --- a/libi2pd/TransitTunnel.h +++ b/libi2pd/TransitTunnel.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef TRANSIT_TUNNEL_H__ #define TRANSIT_TUNNEL_H__ diff --git a/libi2pd/TransportSession.h b/libi2pd/TransportSession.h index 55c39c7a..a97f246f 100644 --- a/libi2pd/TransportSession.h +++ b/libi2pd/TransportSession.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef TRANSPORT_SESSION_H__ #define TRANSPORT_SESSION_H__ @@ -68,15 +76,15 @@ namespace transport std::string GetIdentHashBase64() const { return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : ""; } - std::shared_ptr GetRemoteIdentity () + std::shared_ptr GetRemoteIdentity () { std::lock_guard l(m_RemoteIdentityMutex); - return m_RemoteIdentity; + return m_RemoteIdentity; } - void SetRemoteIdentity (std::shared_ptr ident) + void SetRemoteIdentity (std::shared_ptr ident) { std::lock_guard l(m_RemoteIdentityMutex); - m_RemoteIdentity = ident; + m_RemoteIdentity = ident; } size_t GetNumSentBytes () const { return m_NumSentBytes; }; diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index 804f26cb..7157306d 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include "Log.h" #include "Crypto.h" #include "RouterContext.h" @@ -176,7 +184,7 @@ namespace transport if (proxyurl.schema == "http") proxytype = NTCPServer::eHTTPProxy; - m_NTCPServer->UseProxy(proxytype, proxyurl.host, proxyurl.port) ; + m_NTCPServer->UseProxy(proxytype, proxyurl.host, proxyurl.port); m_NTCPServer->Start(); if(!m_NTCPServer->NetworkIsReady()) { @@ -194,7 +202,7 @@ namespace transport return; } // create NTCP2. TODO: move to acceptor - bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); + bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); if (ntcp2) { if(!ntcp2proxy.empty()) @@ -209,7 +217,7 @@ namespace transport if (proxyurl.schema == "http") proxytype = NTCP2Server::eHTTPProxy; - m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port) ; + m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port); m_NTCP2Server->Start(); } else @@ -220,10 +228,10 @@ namespace transport return; } else - { + { m_NTCP2Server = new NTCP2Server (); m_NTCP2Server->Start (); - } + } } // create acceptors @@ -381,7 +389,7 @@ namespace transport m_LoopbackHandler.Flush (); return; } - if(RoutesRestricted() && ! IsRestrictedPeer(ident)) return; + if(RoutesRestricted() && !IsRestrictedPeer(ident)) return; auto it = m_Peers.find (ident); if (it == m_Peers.end ()) { @@ -390,7 +398,7 @@ namespace transport { auto r = netdb.FindRouter (ident); { - std::unique_lock l(m_PeersMutex); + std::unique_lock l(m_PeersMutex); it = m_Peers.insert (std::pair(ident, { 0, r, {}, i2p::util::GetSecondsSinceEpoch (), {} })).first; } @@ -413,7 +421,8 @@ namespace transport } else { - LogPrint (eLogWarning, "Transports: delayed messages queue size exceeds ", MAX_NUM_DELAYED_MESSAGES); + LogPrint (eLogWarning, "Transports: delayed messages queue size to ", + ident.ToBase64 (), " exceeds ", MAX_NUM_DELAYED_MESSAGES); std::unique_lock l(m_PeersMutex); m_Peers.erase (it); } @@ -424,36 +433,36 @@ namespace transport { if (peer.router) // we have RI already { - 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); + 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); - if(m_NTCP2Server->UsingProxy()) - { - NTCP2Server::RemoteAddressType remote = NTCP2Server::eIP4Address; - std::string addr = address->host.to_string(); + if(m_NTCP2Server->UsingProxy()) + { + NTCP2Server::RemoteAddressType remote = NTCP2Server::eIP4Address; + std::string addr = address->host.to_string(); - if(address->host.is_v6()) - remote = NTCP2Server::eIP6Address; + if(address->host.is_v6()) + remote = NTCP2Server::eIP6Address; - m_NTCP2Server->ConnectWithProxy(addr, address->port, remote, s); - } - else - m_NTCP2Server->Connect (address->host, address->port, s); - return true; - } - } - } + m_NTCP2Server->ConnectWithProxy(addr, address->port, remote, s); + } + else + m_NTCP2Server->Connect (address->host, address->port, s); + return true; + } + } + } if (peer.numAttempts == 1) // NTCP1 { - peer.numAttempts++; + peer.numAttempts++; auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ()); if (address && m_NTCPServer) { @@ -553,13 +562,13 @@ namespace transport { auto router = i2p::data::netdb.GetRandomPeerTestRouter (isv4); // v4 only if v4 if (router) - m_SSUServer->CreateSession (router, true, isv4); // peer test + m_SSUServer->CreateSession (router, true, isv4); // peer test else { // if not peer test capable routers found pick any router = i2p::data::netdb.GetRandomRouter (); if (router && router->IsSSU ()) - m_SSUServer->CreateSession (router); // no peer test + m_SSUServer->CreateSession (router); // no peer test } } if (i2p::context.SupportsV6 ()) @@ -574,7 +583,7 @@ namespace transport if (addr) m_SSUServer->GetServiceV6 ().post ([this, router, addr] { - m_SSUServer->CreateDirectSession (router, { addr->host, (uint16_t)addr->port }, false); + m_SSUServer->CreateDirectSession (router, { addr->host, (uint16_t)addr->port }, false); }); } } @@ -713,7 +722,7 @@ namespace transport { profile->TunnelNonReplied(); } - std::unique_lock l(m_PeersMutex); + std::unique_lock l(m_PeersMutex); it = m_Peers.erase (it); } else diff --git a/libi2pd/Transports.h b/libi2pd/Transports.h index 117092ad..cdfbfccf 100644 --- a/libi2pd/Transports.h +++ b/libi2pd/Transports.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef TRANSPORTS_H__ #define TRANSPORTS_H__ @@ -103,7 +111,7 @@ namespace transport uint64_t GetTotalReceivedBytes () const { return m_TotalReceivedBytes; }; uint64_t GetTotalTransitTransmittedBytes () const { return m_TotalTransitTransmittedBytes; } void UpdateTotalTransitTransmittedBytes (uint32_t add) { m_TotalTransitTransmittedBytes += add; }; - uint32_t GetInBandwidth () const { return m_InBandwidth; }; + uint32_t GetInBandwidth () const { return m_InBandwidth; }; uint32_t GetOutBandwidth () const { return m_OutBandwidth; }; uint32_t GetTransitBandwidth () const { return m_TransitBandwidth; }; bool IsBandwidthExceeded () const; @@ -111,16 +119,16 @@ namespace transport size_t GetNumPeers () const { return m_Peers.size (); }; std::shared_ptr GetRandomPeer () const; - /** get a trusted first hop for restricted routes */ - std::shared_ptr GetRestrictedPeer() const; - /** do we want to use restricted routes? */ - bool RoutesRestricted() const; - /** restrict routes to use only these router families for first hops */ - void RestrictRoutesToFamilies(std::set families); - /** restrict routes to use only these routers for first hops */ - void RestrictRoutesToRouters(std::set routers); + /** get a trusted first hop for restricted routes */ + std::shared_ptr GetRestrictedPeer() const; + /** do we want to use restricted routes? */ + bool RoutesRestricted() const; + /** restrict routes to use only these router families for first hops */ + void RestrictRoutesToFamilies(std::set families); + /** restrict routes to use only these routers for first hops */ + void RestrictRoutesToRouters(std::set routers); - bool IsRestrictedPeer(const i2p::data::IdentHash & ident) const; + bool IsRestrictedPeer(const i2p::data::IdentHash & ident) const; void PeerTest (); diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index 879ee2d3..fe7e36af 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include "I2PEndian.h" #include diff --git a/libi2pd/Tunnel.h b/libi2pd/Tunnel.h index f97bcc63..1fb12af9 100644 --- a/libi2pd/Tunnel.h +++ b/libi2pd/Tunnel.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef TUNNEL_H__ #define TUNNEL_H__ diff --git a/libi2pd/TunnelBase.h b/libi2pd/TunnelBase.h index 53782ae3..f98066d3 100644 --- a/libi2pd/TunnelBase.h +++ b/libi2pd/TunnelBase.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef TUNNEL_BASE_H__ #define TUNNEL_BASE_H__ diff --git a/libi2pd/TunnelConfig.h b/libi2pd/TunnelConfig.h index 48e66f2e..0bd8a842 100644 --- a/libi2pd/TunnelConfig.h +++ b/libi2pd/TunnelConfig.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef TUNNEL_CONFIG_H__ #define TUNNEL_CONFIG_H__ diff --git a/libi2pd/TunnelEndpoint.cpp b/libi2pd/TunnelEndpoint.cpp index 324d5315..eb70bdca 100644 --- a/libi2pd/TunnelEndpoint.cpp +++ b/libi2pd/TunnelEndpoint.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include "I2PEndian.h" #include #include "Crypto.h" @@ -64,8 +72,7 @@ namespace tunnel m.hash = i2p::data::IdentHash (fragment); fragment += 32; // to hash break; - default: - ; + default: ; } bool isFragmented = flag & 0x08; diff --git a/libi2pd/TunnelEndpoint.h b/libi2pd/TunnelEndpoint.h index c2ffe53d..43b836f1 100644 --- a/libi2pd/TunnelEndpoint.h +++ b/libi2pd/TunnelEndpoint.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef TUNNEL_ENDPOINT_H__ #define TUNNEL_ENDPOINT_H__ diff --git a/libi2pd/TunnelGateway.cpp b/libi2pd/TunnelGateway.cpp index 7d0069a9..8f01bc4e 100644 --- a/libi2pd/TunnelGateway.cpp +++ b/libi2pd/TunnelGateway.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include "Crypto.h" #include "I2PEndian.h" @@ -51,7 +59,7 @@ namespace tunnel // create fragments const std::shared_ptr & msg = block.data; size_t fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length - + if (!messageCreated && fullMsgLen > m_RemainingSize) // check if we should complete previous message { size_t numFollowOnFragments = fullMsgLen / TUNNEL_DATA_MAX_PAYLOAD_SIZE; @@ -172,7 +180,7 @@ namespace tunnel SHA256(payload, size+16, hash); memcpy (buf+20, hash, 4); // checksum payload[-1] = 0; // zero - ptrdiff_t paddingSize = payload - buf - 25; // 25 = 24 + 1 + ptrdiff_t paddingSize = payload - buf - 25; // 25 = 24 + 1 if (paddingSize > 0) { // non-zero padding @@ -219,4 +227,3 @@ namespace tunnel } } } - diff --git a/libi2pd/TunnelGateway.h b/libi2pd/TunnelGateway.h index 7959b57b..01101a36 100644 --- a/libi2pd/TunnelGateway.h +++ b/libi2pd/TunnelGateway.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef TUNNEL_GATEWAY_H__ #define TUNNEL_GATEWAY_H__ diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index 7256b2db..d0fd401f 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include "I2PEndian.h" @@ -16,7 +24,6 @@ namespace i2p { namespace tunnel { - TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels): m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops), m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true), @@ -67,7 +74,8 @@ namespace tunnel m_Tests.clear (); } - bool TunnelPool::Reconfigure(int inHops, int outHops, int inQuant, int outQuant) { + bool TunnelPool::Reconfigure(int inHops, int outHops, int inQuant, int outQuant) + { if( inHops >= 0 && outHops >= 0 && inQuant > 0 && outQuant > 0) { m_NumInboundHops = inHops; @@ -78,7 +86,7 @@ namespace tunnel } return false; } - + void TunnelPool::TunnelCreated (std::shared_ptr createdTunnel) { if (!m_IsActive) return; @@ -180,8 +188,8 @@ namespace tunnel { if (it->IsEstablished () && it != excluded) { - tunnel = it; - i++; + tunnel = it; + i++; } if (i > ind && tunnel) break; } @@ -411,7 +419,7 @@ namespace tunnel { std::lock_guard lock(m_CustomPeerSelectorMutex); if (m_CustomPeerSelector) - return m_CustomPeerSelector->SelectPeers(peers, numHops, isInbound); + return m_CustomPeerSelector->SelectPeers(peers, numHops, isInbound); } // explicit peers in use if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound); diff --git a/libi2pd/TunnelPool.h b/libi2pd/TunnelPool.h index d6fb91cc..04ff4ae7 100644 --- a/libi2pd/TunnelPool.h +++ b/libi2pd/TunnelPool.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef TUNNEL_POOL__ #define TUNNEL_POOL__ @@ -75,23 +83,23 @@ namespace tunnel /** i2cp reconfigure */ bool Reconfigure(int inboundHops, int outboundHops, int inboundQuant, int outboundQuant); - + void SetCustomPeerSelector(ITunnelPeerSelector * selector); void UnsetCustomPeerSelector(); bool HasCustomPeerSelector(); - /** @brief make this tunnel pool yield tunnels that fit latency range [min, max] */ - void RequireLatency(uint64_t min, uint64_t max) { m_MinLatency = min; m_MaxLatency = max; } + /** @brief make this tunnel pool yield tunnels that fit latency range [min, max] */ + void RequireLatency(uint64_t min, uint64_t max) { m_MinLatency = min; m_MaxLatency = max; } - /** @brief return true if this tunnel pool has a latency requirement */ - bool HasLatencyRequirement() const { return m_MinLatency > 0 && m_MaxLatency > 0; } + /** @brief return true if this tunnel pool has a latency requirement */ + bool HasLatencyRequirement() const { return m_MinLatency > 0 && m_MaxLatency > 0; } - /** @brief get the lowest latency tunnel in this tunnel pool regardless of latency requirements */ - std::shared_ptr GetLowestLatencyInboundTunnel(std::shared_ptr exclude=nullptr) const; - std::shared_ptr GetLowestLatencyOutboundTunnel(std::shared_ptr exclude=nullptr) const; + /** @brief get the lowest latency tunnel in this tunnel pool regardless of latency requirements */ + std::shared_ptr GetLowestLatencyInboundTunnel(std::shared_ptr exclude = nullptr) const; + std::shared_ptr GetLowestLatencyOutboundTunnel(std::shared_ptr exclude = nullptr) const; - // for overriding tunnel peer selection - std::shared_ptr SelectNextHop (std::shared_ptr prevHop) const; + // for overriding tunnel peer selection + std::shared_ptr SelectNextHop (std::shared_ptr prevHop) const; private: @@ -118,8 +126,8 @@ namespace tunnel std::mutex m_CustomPeerSelectorMutex; ITunnelPeerSelector * m_CustomPeerSelector; - uint64_t m_MinLatency=0; // if > 0 this tunnel pool will try building tunnels with minimum latency by ms - uint64_t m_MaxLatency=0; // if > 0 this tunnel pool will try building tunnels with maximum latency by ms + uint64_t m_MinLatency = 0; // if > 0 this tunnel pool will try building tunnels with minimum latency by ms + uint64_t m_MaxLatency = 0; // if > 0 this tunnel pool will try building tunnels with maximum latency by ms public: diff --git a/libi2pd/api.cpp b/libi2pd/api.cpp index 0a76bda7..569fbd8c 100644 --- a/libi2pd/api.cpp +++ b/libi2pd/api.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include "Config.h" @@ -31,8 +39,8 @@ namespace api bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation); i2p::crypto::InitCrypto (precomputation); - int netID; i2p::config::GetOption("netid", netID); - i2p::context.SetNetID (netID); + int netID; i2p::config::GetOption("netid", netID); + i2p::context.SetNetID (netID); i2p::context.Init (); } @@ -133,4 +141,3 @@ namespace api } } } - diff --git a/libi2pd/api.h b/libi2pd/api.h index f64590d1..9b0256d8 100644 --- a/libi2pd/api.h +++ b/libi2pd/api.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef API_H__ #define API_H__ @@ -35,4 +43,3 @@ namespace api } #endif - diff --git a/libi2pd/util.cpp b/libi2pd/util.cpp index c1b741fc..f5204a50 100644 --- a/libi2pd/util.cpp +++ b/libi2pd/util.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include @@ -64,9 +72,9 @@ namespace util { m_IsRunning = true; m_Thread.reset (new std::thread (std::bind (& RunnableService::Run, this))); - } + } } - + void RunnableService::StopIOService () { if (m_IsRunning) @@ -79,7 +87,7 @@ namespace util m_Thread = nullptr; } } - } + } void RunnableService::Run () { @@ -94,28 +102,28 @@ namespace util LogPrint (eLogError, m_Name, ": runtime exception: ", ex.what ()); } } - } - + } + namespace net { #ifdef WIN32 bool IsWindowsXPorLater() { - static bool isRequested = false; - static bool isXP = false; - if (!isRequested) - { - // request - OSVERSIONINFO osvi; + static bool isRequested = false; + static bool isXP = false; + if (!isRequested) + { + // request + OSVERSIONINFO osvi; - ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx(&osvi); + ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); - isXP = osvi.dwMajorVersion <= 5; - isRequested = true; - } - return isXP; + isXP = osvi.dwMajorVersion <= 5; + isRequested = true; + } + return isXP; } int GetMTUWindowsIpv4(sockaddr_in inputAddress, int fallback) @@ -246,22 +254,24 @@ namespace net std::string localAddressUniversal = localAddress.to_string(); #endif - typedef int (* IPN)(int af, const char *src, void *dst); + typedef int (* IPN)(int af, const char *src, void *dst); IPN inetpton = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetPton"); - if (!inetpton) inetpton = inet_pton_xp; // use own implementation if not found + if (!inetpton) inetpton = inet_pton_xp; // use own implementation if not found if(localAddress.is_v4()) { sockaddr_in inputAddress; - inetpton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr)); + inetpton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr)); return GetMTUWindowsIpv4(inputAddress, fallback); } else if(localAddress.is_v6()) { sockaddr_in6 inputAddress; - inetpton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr)); + inetpton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr)); return GetMTUWindowsIpv6(inputAddress, fallback); - } else { + } + else + { LogPrint(eLogError, "NetIface: GetMTU(): address family is not supported"); return fallback; } @@ -355,7 +365,7 @@ namespace net if (cur_ifname == ifname && cur->ifa_addr && cur->ifa_addr->sa_family == af) { // match - char addr[INET6_ADDRSTRLEN]; + char addr[INET6_ADDRSTRLEN]; memset (addr, 0, INET6_ADDRSTRLEN); if(af == AF_INET) inet_ntop(af, &((sockaddr_in *)cur->ifa_addr)->sin_addr, addr, INET6_ADDRSTRLEN); @@ -379,7 +389,6 @@ namespace net LogPrint(eLogWarning, "NetIface: cannot find ipv4 address for interface ", ifname); } return boost::asio::ip::address::from_string(fallback); - #endif } } diff --git a/libi2pd/util.h b/libi2pd/util.h index 2202ccd9..cb8fd8f1 100644 --- a/libi2pd/util.h +++ b/libi2pd/util.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef UTIL_H #define UTIL_H @@ -132,34 +140,34 @@ namespace util boost::asio::io_service& GetIOService () { return m_Service; } bool IsRunning () const { return m_IsRunning; }; - + void StartIOService (); void StopIOService (); private: void Run (); - + private: std::string m_Name; volatile bool m_IsRunning; std::unique_ptr m_Thread; boost::asio::io_service m_Service; - }; + }; class RunnableServiceWithWork: public RunnableService { protected: - RunnableServiceWithWork (const std::string& name): - RunnableService (name), m_Work (GetIOService ()) {} - + RunnableServiceWithWork (const std::string& name): + RunnableService (name), m_Work (GetIOService ()) {} + private: boost::asio::io_service::work m_Work; - }; - + }; + namespace net { int GetMTU (const boost::asio::ip::address& localAddress); diff --git a/libi2pd/version.h b/libi2pd/version.h index 36fbe2ca..346cf8a7 100644 --- a/libi2pd/version.h +++ b/libi2pd/version.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef _VERSION_H_ #define _VERSION_H_ @@ -5,9 +13,10 @@ #define STRINGIZE(x) #x #define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c) +#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c) #define I2PD_VERSION_MAJOR 2 -#define I2PD_VERSION_MINOR 30 +#define I2PD_VERSION_MINOR 32 #define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_PATCH 0 #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) @@ -21,8 +30,9 @@ #define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MINOR 9 -#define I2P_VERSION_MICRO 45 +#define I2P_VERSION_MICRO 46 #define I2P_VERSION_PATCH 0 #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) +#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #endif diff --git a/libi2pd_client/AddressBook.cpp b/libi2pd_client/AddressBook.cpp index 0e40c76e..8b8e781d 100644 --- a/libi2pd_client/AddressBook.cpp +++ b/libi2pd_client/AddressBook.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include @@ -31,7 +39,7 @@ namespace client std::string etagsPath, indexPath, localPath; public: - AddressBookFilesystemStorage (): storage("addressbook", "b", "", "b32") + AddressBookFilesystemStorage (): storage("addressbook", "b", "", "b32") { i2p::config::GetOption("persist.addressbook", m_IsPersist); } @@ -77,10 +85,10 @@ namespace client std::shared_ptr AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident) const { - if (!m_IsPersist) + if (!m_IsPersist) { LogPrint(eLogDebug, "Addressbook: Persistence is disabled"); - return nullptr; + return nullptr; } std::string filename = storage.Path(ident.ToBase32()); std::ifstream f(filename, std::ifstream::binary); @@ -121,7 +129,7 @@ namespace client void AddressBookFilesystemStorage::RemoveAddress (const i2p::data::IdentHash& ident) { - if (!m_IsPersist) return; + if (!m_IsPersist) return; storage.Remove( ident.ToBase32() ); } @@ -189,7 +197,7 @@ namespace client } for (const auto& it: addresses) - { + { f << it.first << ","; if (it.second->IsIdentHash ()) f << it.second->identHash.ToBase32 (); @@ -251,12 +259,12 @@ namespace client if (blindedPublicKey->IsValid ()) addressType = eAddressBlindedPublicKey; } - } + } Address::Address (const i2p::data::IdentHash& hash) { addressType = eAddressIndentHash; - identHash = hash; + identHash = hash; } AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false), @@ -322,7 +330,7 @@ namespace client { auto pos = address.find(".b32.i2p"); if (pos != std::string::npos) - { + { auto addr = std::make_shared(address.substr (0, pos)); return addr->IsValid () ? addr : nullptr; } @@ -333,10 +341,10 @@ namespace client { auto addr = FindAddress (address); if (!addr) - LookupAddress (address); // TODO: + LookupAddress (address); // TODO: return addr; - } - } + } + } // if not .b32 we assume full base64 address i2p::data::IdentityEx dest; if (!dest.FromBase64 (address)) @@ -359,10 +367,10 @@ namespace client { m_Addresses[address] = std::make_shared

(jump.substr (0, pos)); LogPrint (eLogInfo, "Addressbook: added ", address," -> ", jump); - } + } else - { - // assume base64 + { + // assume base64 auto ident = std::make_shared(); if (ident->FromBase64 (jump)) { @@ -487,18 +495,18 @@ namespace client LogPrint (eLogWarning, "Addressbook: subscriptions.txt usage is deprecated, use config file instead"); } else if (!i2p::config::IsDefault("addressbook.subscriptions")) - { - // using config file items - std::string subscriptionURLs; i2p::config::GetOption("addressbook.subscriptions", subscriptionURLs); - std::vector subsList; - boost::split(subsList, subscriptionURLs, boost::is_any_of(","), boost::token_compress_on); + { + // using config file items + std::string subscriptionURLs; i2p::config::GetOption("addressbook.subscriptions", subscriptionURLs); + std::vector subsList; + boost::split(subsList, subscriptionURLs, boost::is_any_of(","), boost::token_compress_on); - for (size_t i = 0; i < subsList.size (); i++) - { - m_Subscriptions.push_back (std::make_shared (*this, subsList[i])); - } - LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded"); - } + for (size_t i = 0; i < subsList.size (); i++) + { + m_Subscriptions.push_back (std::make_shared (*this, subsList[i])); + } + LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded"); + } } else LogPrint (eLogError, "Addressbook: subscriptions already loaded"); @@ -515,7 +523,7 @@ namespace client if (dot != std::string::npos) { auto domain = it.first.substr (dot + 1); - auto it1 = m_Addresses.find (domain); // find domain in our addressbook + auto it1 = m_Addresses.find (domain); // find domain in our addressbook if (it1 != m_Addresses.end () && it1->second->IsIdentHash ()) { auto dest = context.FindLocalDestination (it1->second->identHash); @@ -610,7 +618,7 @@ namespace client { // download it from default subscription LogPrint (eLogInfo, "Addressbook: trying to download it from default subscription."); - std::string defaultSubURL; i2p::config::GetOption("addressbook.defaulturl", defaultSubURL); + std::string defaultSubURL; i2p::config::GetOption("addressbook.defaulturl", defaultSubURL); if (!m_DefaultSubscription) m_DefaultSubscription = std::make_shared(*this, defaultSubURL); m_IsDownloading = true; @@ -743,13 +751,13 @@ namespace client i2p::http::URL url; // must be run in separate thread LogPrint (eLogInfo, "Addressbook: Downloading hosts database from ", m_Link); - if (!url.parse(m_Link)) + if (!url.parse(m_Link)) { LogPrint(eLogError, "Addressbook: failed to parse url: ", m_Link); return false; } auto addr = m_Book.GetAddress (url.host); - if (!addr || !addr->IsIdentHash ()) + if (!addr || !addr->IsIdentHash ()) { LogPrint (eLogError, "Addressbook: Can't resolve ", url.host); return false; @@ -802,7 +810,7 @@ namespace client /* convert url to relative */ url.schema = ""; url.host = ""; - req.uri = url.to_string(); + req.uri = url.to_string(); auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, dest_port); std::string request = req.to_string(); stream->Send ((const uint8_t *) request.data(), request.length()); @@ -920,7 +928,7 @@ namespace client { auto datagram = m_LocalDestination->GetDatagramDestination (); if (datagram) - datagram->ResetReceiver (ADDRESS_RESOLVER_DATAGRAM_PORT); + datagram->ResetReceiver (ADDRESS_RESOLVER_DATAGRAM_PORT); } } diff --git a/libi2pd_client/AddressBook.h b/libi2pd_client/AddressBook.h index 82ba167b..04600792 100644 --- a/libi2pd_client/AddressBook.h +++ b/libi2pd_client/AddressBook.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef ADDRESS_BOOK_H__ #define ADDRESS_BOOK_H__ @@ -37,8 +45,8 @@ namespace client i2p::data::IdentHash identHash; std::shared_ptr blindedPublicKey; - Address (const std::string& b32); - Address (const i2p::data::IdentHash& hash); + Address (const std::string& b32); + Address (const i2p::data::IdentHash& hash); bool IsIdentHash () const { return addressType == eAddressIndentHash; }; bool IsValid () const { return addressType != eAddressInvalid; }; }; @@ -160,5 +168,3 @@ namespace client } #endif - - diff --git a/libi2pd_client/BOB.cpp b/libi2pd_client/BOB.cpp index c7bbdb46..c4447808 100644 --- a/libi2pd_client/BOB.cpp +++ b/libi2pd_client/BOB.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include "Log.h" #include "ClientContext.h" @@ -50,10 +58,10 @@ namespace client void BOBI2PInboundTunnel::ReceiveAddress (std::shared_ptr receiver) { receiver->socket->async_read_some (boost::asio::buffer( - receiver->buffer + receiver->bufferOffset, - BOB_COMMAND_BUFFER_SIZE - receiver->bufferOffset), + receiver->buffer + receiver->bufferOffset, + BOB_COMMAND_BUFFER_SIZE - receiver->bufferOffset), std::bind(&BOBI2PInboundTunnel::HandleReceivedAddress, this, - std::placeholders::_1, std::placeholders::_2, receiver)); + std::placeholders::_1, std::placeholders::_2, receiver)); } void BOBI2PInboundTunnel::HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred, @@ -255,7 +263,7 @@ namespace client std::bind(&BOBCommandSession::HandleReceivedLine, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } - + void BOBCommandSession::HandleReceivedLine(const boost::system::error_code& ecode, std::size_t bytes_transferred) { if(ecode) @@ -267,14 +275,14 @@ namespace client else { std::string line; - + std::istream is(&m_ReceiveBuffer); std::getline(is, line); - + std::string command, operand; std::istringstream iss(line); iss >> command >> operand; - + // process command auto& handlers = m_Owner.GetCommandHandlers(); auto it = handlers.find(command); @@ -346,7 +354,7 @@ namespace client std::ostream os(&m_SendBuffer); os << data << std::endl; } - + void BOBCommandSession::BuildStatusLine(bool currentTunnel, BOBDestination *dest, std::string &out) { // helper lambdas @@ -355,7 +363,7 @@ namespace client const auto destExists = [](const BOBDestination * const dest) { return dest != nullptr; }; const auto destReady = [](const BOBDestination * const dest) { return dest->GetLocalDestination()->IsReady(); }; const auto bool_str = [](const bool v) { return v ? "true" : "false"; }; // bool -> str - + // tunnel info const std::string nickname = currentTunnel ? m_Nickname : dest->GetNickname(); const bool quiet = currentTunnel ? m_IsQuiet : dest->GetQuiet(); @@ -367,7 +375,7 @@ namespace client const bool starting = destExists(dest) && !destReady(dest); const bool running = destExists(dest) && destReady(dest); const bool stopping = false; - + // build line std::stringstream ss; ss << "DATA " @@ -433,11 +441,11 @@ namespace client return; } } - + if (!m_CurrentDestination) { m_CurrentDestination = new BOBDestination (i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options), // deleted in clear command - m_Nickname, m_InHost, m_OutHost, m_InPort, m_OutPort, m_IsQuiet); + m_Nickname, m_InHost, m_OutHost, m_InPort, m_OutPort, m_IsQuiet); m_Owner.AddDestination (m_Nickname, m_CurrentDestination); } if (m_InPort) @@ -613,25 +621,24 @@ namespace client } auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination (); if (addr->IsIdentHash ()) - { + { // we might have leaseset already auto leaseSet = localDestination->FindLeaseSet (addr->identHash); if (leaseSet) - { + { SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ()); return; } } // trying to request - auto s = shared_from_this (); - auto requstCallback = - [s](std::shared_ptr ls) - { - if (ls) - s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ()); - else - s->SendReplyError ("LeaseSet Not found"); - }; + auto s = shared_from_this (); + auto requstCallback = [s](std::shared_ptr ls) + { + if (ls) + s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ()); + else + s->SendReplyError ("LeaseSet Not found"); + }; if (addr->IsIdentHash ()) localDestination->RequestDestination (addr->identHash, requstCallback); else @@ -794,7 +801,7 @@ namespace client BOBCommandChannel::~BOBCommandChannel () { - if (IsRunning ()) + if (IsRunning ()) Stop (); for (const auto& it: m_Destinations) delete it.second; @@ -856,8 +863,7 @@ namespace client session->SendVersion (); } else - LogPrint (eLogError, "BOB: accept error: ", ecode.message ()); + LogPrint (eLogError, "BOB: accept error: ", ecode.message ()); } } } - diff --git a/libi2pd_client/BOB.h b/libi2pd_client/BOB.h index 15a0afaf..74418011 100644 --- a/libi2pd_client/BOB.h +++ b/libi2pd_client/BOB.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef BOB_H__ #define BOB_H__ @@ -39,7 +47,7 @@ namespace client const char BOB_COMMAND_OPTION[] = "option"; const char BOB_COMMAND_STATUS[] = "status"; const char BOB_COMMAND_HELP[] = "help"; - + const char BOB_HELP_ZAP[] = "zap - Shuts down BOB."; const char BOB_HELP_QUIT[] = "quit - Quits this session with BOB."; const char BOB_HELP_START[] = "start - Starts the current nicknamed tunnel."; @@ -75,15 +83,15 @@ namespace client class BOBI2PInboundTunnel: public BOBI2PTunnel { - struct AddressReceiver - { - std::shared_ptr socket; - char buffer[BOB_COMMAND_BUFFER_SIZE + 1]; // for destination base64 address - uint8_t * data; // pointer to buffer - size_t dataLen, bufferOffset; + struct AddressReceiver + { + std::shared_ptr socket; + char buffer[BOB_COMMAND_BUFFER_SIZE + 1]; // for destination base64 address + uint8_t * data; // pointer to buffer + size_t dataLen, bufferOffset; - AddressReceiver (): data (nullptr), dataLen (0), bufferOffset (0) {}; - }; + AddressReceiver (): data (nullptr), dataLen (0), bufferOffset (0) {}; + }; public: @@ -115,7 +123,7 @@ namespace client { public: - BOBI2POutboundTunnel (const std::string& outhost, int port, std::shared_ptr localDestination, bool quiet); + BOBI2POutboundTunnel (const std::string& outhost, int port, std::shared_ptr localDestination, bool quiet); void Start (); void Stop (); @@ -162,7 +170,7 @@ namespace client std::shared_ptr m_LocalDestination; BOBI2POutboundTunnel * m_OutboundTunnel; BOBI2PInboundTunnel * m_InboundTunnel; - + std::string m_Nickname; std::string m_InHost, m_OutHost; int m_InPort, m_OutPort; @@ -215,14 +223,14 @@ namespace client void SendReplyOK (const char * msg = nullptr); void SendReplyError (const char * msg); void SendRaw (const char * data); - + void BuildStatusLine(bool currentTunnel, BOBDestination *destination, std::string &out); private: BOBCommandChannel& m_Owner; boost::asio::ip::tcp::socket m_Socket; - boost::asio::streambuf m_ReceiveBuffer, m_SendBuffer; + boost::asio::streambuf m_ReceiveBuffer, m_SendBuffer; bool m_IsOpen, m_IsQuiet, m_IsActive; std::string m_Nickname, m_InHost, m_OutHost; int m_InPort, m_OutPort; @@ -269,4 +277,3 @@ namespace client } #endif - diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index 722fb242..54007c95 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include @@ -52,20 +60,21 @@ namespace client // SAM bool sam; i2p::config::GetOption("sam.enabled", sam); - if (sam) + if (sam) { std::string samAddr; i2p::config::GetOption("sam.address", samAddr); uint16_t samPort; i2p::config::GetOption("sam.port", samPort); - bool singleThread; i2p::config::GetOption("sam.singlethread", singleThread); + bool singleThread; i2p::config::GetOption("sam.singlethread", singleThread); LogPrint(eLogInfo, "Clients: starting SAM bridge at ", samAddr, ":", samPort); - try + try { m_SamBridge = new SAMBridge (samAddr, samPort, singleThread); - m_SamBridge->Start (); - } - catch (std::exception& e) + m_SamBridge->Start (); + } + catch (std::exception& e) { - LogPrint(eLogError, "Clients: Exception in SAM bridge: ", e.what()); + LogPrint(eLogError, "Clients: Exception in SAM bridge: ", e.what()); + ThrowFatal ("Unable to start SAM bridge at ", samAddr, ":", samPort, ": ", e.what ()); } } @@ -75,11 +84,15 @@ namespace client std::string bobAddr; i2p::config::GetOption("bob.address", bobAddr); uint16_t bobPort; i2p::config::GetOption("bob.port", bobPort); LogPrint(eLogInfo, "Clients: starting BOB command channel at ", bobAddr, ":", bobPort); - try { - m_BOBCommandChannel = new BOBCommandChannel (bobAddr, bobPort); - m_BOBCommandChannel->Start (); - } catch (std::exception& e) { - LogPrint(eLogError, "Clients: Exception in BOB bridge: ", e.what()); + try + { + m_BOBCommandChannel = new BOBCommandChannel (bobAddr, bobPort); + m_BOBCommandChannel->Start (); + } + catch (std::exception& e) + { + LogPrint(eLogError, "Clients: Exception in BOB bridge: ", e.what()); + ThrowFatal ("Unable to start BOB bridge at ", bobAddr, ":", bobPort, ": ", e.what ()); } } @@ -98,6 +111,7 @@ namespace client catch (std::exception& e) { LogPrint(eLogError, "Clients: Exception in I2CP: ", e.what()); + ThrowFatal ("Unable to start I2CP at ", i2cpAddr, ":", i2cpPort, ": ", e.what ()); } } @@ -401,13 +415,13 @@ namespace client template std::string ClientContext::GetI2CPOption (const Section& section, const std::string& name, const Type& value) const { - return section.second.get (boost::property_tree::ptree::path_type (name, '/'), std::to_string (value)); + return section.second.get (boost::property_tree::ptree::path_type (name, '/'), std::to_string (value)); } template std::string ClientContext::GetI2CPStringOption (const Section& section, const std::string& name, const std::string& value) const { - return section.second.get (boost::property_tree::ptree::path_type (name, '/'), value); + return section.second.get (boost::property_tree::ptree::path_type (name, '/'), value); } template @@ -417,13 +431,13 @@ namespace client { if (it.first.length () >= group.length () && !it.first.compare (0, group.length (), group)) options[it.first] = it.second.get_value (""); - } + } } - + template void ClientContext::ReadI2CPOptions (const Section& section, std::map& options) const { - options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNEL_LENGTH, DEFAULT_INBOUND_TUNNEL_LENGTH); + options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNEL_LENGTH, DEFAULT_INBOUND_TUNNEL_LENGTH); options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, DEFAULT_OUTBOUND_TUNNEL_LENGTH); options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, DEFAULT_INBOUND_TUNNELS_QUANTITY); options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, DEFAULT_OUTBOUND_TUNNELS_QUANTITY); @@ -445,6 +459,8 @@ namespace client else if (authType == "2") // PSK ReadI2CPOptionsGroup (section, I2CP_PARAM_LEASESET_CLIENT_PSK, options); } + std::string explicitPeers = GetI2CPStringOption(section, I2CP_PARAM_EXPLICIT_PEERS, ""); + if (explicitPeers.length () > 0) options[I2CP_PARAM_EXPLICIT_PEERS] = explicitPeers; } void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map& options) const @@ -462,6 +478,10 @@ namespace client options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = value; if (i2p::config::GetOption(prefix + I2CP_PARAM_MAX_TUNNEL_LATENCY, value)) options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_TYPE, value)) + options[I2CP_PARAM_LEASESET_TYPE] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, value)) + options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = value; } void ClientContext::ReadTunnels () @@ -490,6 +510,7 @@ namespace client { for (auto& it: files) { + if (it.substr(it.size() - 5) != ".conf") continue; // skip files which not ends with ".conf" LogPrint(eLogDebug, "Clients: tunnels extra config file: ", it); ReadTunnels (it, numClientTunnels, numServerTunnels); } @@ -500,16 +521,12 @@ namespace client LogPrint (eLogInfo, "Clients: ", numServerTunnels, " I2P server tunnels created"); } - void ClientContext::ReadTunnels (const std::string& tunConf, int& numClientTunnels, int& numServerTunnels) { boost::property_tree::ptree pt; - try - { + try { boost::property_tree::read_ini (tunConf, pt); - } - catch (std::exception& ex) - { + } catch (std::exception& ex) { LogPrint (eLogWarning, "Clients: Can't read ", tunConf, ": ", ex.what ()); return; } @@ -521,10 +538,10 @@ namespace client { std::string type = section.second.get (I2P_TUNNELS_SECTION_TYPE); if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT - || type == I2P_TUNNELS_SECTION_TYPE_SOCKS - || type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS - || type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY - || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) + || type == I2P_TUNNELS_SECTION_TYPE_SOCKS + || type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS + || type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY + || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) { // mandatory params std::string dest; @@ -564,14 +581,12 @@ namespace client // TODO: hostnames boost::asio::ip::udp::endpoint end(boost::asio::ip::address::from_string(address), port); if (!localDestination) - { localDestination = m_SharedLocalDestination; - } - auto clientTunnel = std::make_shared(name, dest, end, localDestination, destinationPort); + + bool gzip = section.second.get (I2P_CLIENT_TUNNEL_GZIP, true); + auto clientTunnel = std::make_shared(name, dest, end, localDestination, destinationPort, gzip); if(m_ClientForwards.insert(std::make_pair(end, clientTunnel)).second) - { clientTunnel->Start(); - } else LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists"); @@ -597,7 +612,7 @@ namespace client } else if (type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS) { - LogPrint(eLogError, "Clients: I2P Client tunnel websocks is deprecated"); + LogPrint(eLogWarning, "Clients: I2P Client tunnel websocks is deprecated, not starting ", name, " tunnel"); continue; } else @@ -607,6 +622,7 @@ namespace client clientTunnel = tun; clientEndpoint = tun->GetLocalEndpoint (); } + uint32_t timeout = section.second.get(I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT, 0); if(timeout) { @@ -634,9 +650,9 @@ namespace client } } else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER - || type == I2P_TUNNELS_SECTION_TYPE_HTTP - || type == I2P_TUNNELS_SECTION_TYPE_IRC - || type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) + || type == I2P_TUNNELS_SECTION_TYPE_HTTP + || type == I2P_TUNNELS_SECTION_TYPE_IRC + || type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) { // mandatory params std::string host = section.second.get (I2P_SERVER_TUNNEL_HOST); @@ -656,8 +672,8 @@ namespace client // I2CP std::map options; - ReadI2CPOptions (section, options); - + ReadI2CPOptions (section, options); + std::shared_ptr localDestination = nullptr; i2p::data::PrivateKeys k; if(!LoadPrivateKeys (k, keys, sigType, cryptoType)) @@ -671,7 +687,7 @@ namespace client // TODO: hostnames auto localAddress = boost::asio::ip::address::from_string(address); boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port); - auto serverTunnel = std::make_shared(name, localDestination, localAddress, endpoint, port); + auto serverTunnel = std::make_shared(name, localDestination, localAddress, endpoint, port, gzip); if(!isUniqueLocal) { LogPrint(eLogInfo, "Clients: disabling loopback address mapping"); @@ -693,7 +709,7 @@ namespace client continue; } - std::shared_ptr serverTunnel; + std::shared_ptr serverTunnel; if (type == I2P_TUNNELS_SECTION_TYPE_HTTP) serverTunnel = std::make_shared (name, host, port, localDestination, hostOverride, inPort, gzip); else if (type == I2P_TUNNELS_SECTION_TYPE_IRC) @@ -739,17 +755,17 @@ namespace client ins.first->second->SetLocalDestination (serverTunnel->GetLocalDestination ()); } ins.first->second->isUpdated = true; - LogPrint (eLogInfo, "Clients: I2P server tunnel for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), "/", inPort, " already exists"); + LogPrint (eLogInfo, "Clients: I2P server tunnel for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), "/", inPort, " already exists"); } } else - LogPrint (eLogWarning, "Clients: Unknown section type=", type, " of ", name, " in ", tunConf); - + LogPrint (eLogWarning, "Clients: Unknown section type = ", type, " of ", name, " in ", tunConf); } catch (std::exception& ex) { LogPrint (eLogError, "Clients: Can't read tunnel ", name, " params: ", ex.what ()); + ThrowFatal ("Unable to start tunnel ", name, ": ", ex.what ()); } } } @@ -760,12 +776,12 @@ namespace client bool httproxy; i2p::config::GetOption("httpproxy.enabled", httproxy); if (httproxy) { - std::string httpProxyKeys; i2p::config::GetOption("httpproxy.keys", httpProxyKeys); - std::string httpProxyAddr; i2p::config::GetOption("httpproxy.address", httpProxyAddr); - uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort); - i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType); - std::string httpOutProxyURL; i2p::config::GetOption("httpproxy.outproxy", httpOutProxyURL); - bool httpAddresshelper; i2p::config::GetOption("httpproxy.addresshelper", httpAddresshelper); + std::string httpProxyKeys; i2p::config::GetOption("httpproxy.keys", httpProxyKeys); + std::string httpProxyAddr; i2p::config::GetOption("httpproxy.address", httpProxyAddr); + uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort); + std::string httpOutProxyURL; i2p::config::GetOption("httpproxy.outproxy", httpOutProxyURL); + bool httpAddresshelper; i2p::config::GetOption("httpproxy.addresshelper", httpAddresshelper); + i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType); LogPrint(eLogInfo, "Clients: starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort); if (httpProxyKeys.length () > 0) { @@ -788,6 +804,7 @@ namespace client catch (std::exception& e) { LogPrint(eLogError, "Clients: Exception in HTTP Proxy: ", e.what()); + ThrowFatal ("Unable to start HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort, ": ", e.what ()); } } } @@ -798,13 +815,13 @@ namespace client bool socksproxy; i2p::config::GetOption("socksproxy.enabled", socksproxy); if (socksproxy) { - std::string socksProxyKeys; i2p::config::GetOption("socksproxy.keys", socksProxyKeys); - std::string socksProxyAddr; i2p::config::GetOption("socksproxy.address", socksProxyAddr); - uint16_t socksProxyPort; i2p::config::GetOption("socksproxy.port", socksProxyPort); - bool socksOutProxy; i2p::config::GetOption("socksproxy.outproxy.enabled", socksOutProxy); - std::string socksOutProxyAddr; i2p::config::GetOption("socksproxy.outproxy", socksOutProxyAddr); - uint16_t socksOutProxyPort; i2p::config::GetOption("socksproxy.outproxyport", socksOutProxyPort); - i2p::data::SigningKeyType sigType; i2p::config::GetOption("socksproxy.signaturetype", sigType); + std::string socksProxyKeys; i2p::config::GetOption("socksproxy.keys", socksProxyKeys); + std::string socksProxyAddr; i2p::config::GetOption("socksproxy.address", socksProxyAddr); + uint16_t socksProxyPort; i2p::config::GetOption("socksproxy.port", socksProxyPort); + bool socksOutProxy; i2p::config::GetOption("socksproxy.outproxy.enabled", socksOutProxy); + std::string socksOutProxyAddr; i2p::config::GetOption("socksproxy.outproxy", socksOutProxyAddr); + uint16_t socksOutProxyPort; i2p::config::GetOption("socksproxy.outproxyport", socksOutProxyPort); + i2p::data::SigningKeyType sigType; i2p::config::GetOption("socksproxy.signaturetype", sigType); LogPrint(eLogInfo, "Clients: starting SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort); if (socksProxyKeys.length () > 0) { @@ -828,6 +845,7 @@ namespace client catch (std::exception& e) { LogPrint(eLogError, "Clients: Exception in SOCKS Proxy: ", e.what()); + ThrowFatal ("Unable to start SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort, ": ", e.what ()); } } } diff --git a/libi2pd_client/ClientContext.h b/libi2pd_client/ClientContext.h index 610914bf..8a4835c8 100644 --- a/libi2pd_client/ClientContext.h +++ b/libi2pd_client/ClientContext.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef CLIENT_CONTEXT_H__ #define CLIENT_CONTEXT_H__ @@ -33,6 +41,7 @@ namespace client const char I2P_CLIENT_TUNNEL_ADDRESS[] = "address"; const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination"; const char I2P_CLIENT_TUNNEL_KEYS[] = "keys"; + const char I2P_CLIENT_TUNNEL_GZIP[] = "gzip"; const char I2P_CLIENT_TUNNEL_SIGNATURE_TYPE[] = "signaturetype"; const char I2P_CLIENT_TUNNEL_CRYPTO_TYPE[] = "cryptotype"; const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport"; @@ -74,10 +83,11 @@ namespace client const std::map * params = nullptr); // same as previous but on external io_service std::shared_ptr CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true, const std::map * params = nullptr); - std::shared_ptr CreateNewLocalDestination (boost::asio::io_service& service, - const i2p::data::PrivateKeys& keys, bool isPublic = true, + std::shared_ptr CreateNewLocalDestination (boost::asio::io_service& service, + const i2p::data::PrivateKeys& keys, bool isPublic = true, const std::map * params = nullptr); // same as previous but on external io_service - std::shared_ptr CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string & name, const std::map * params = nullptr); + std::shared_ptr CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, + const std::string & name, const std::map * params = nullptr); void DeleteLocalDestination (std::shared_ptr destination); std::shared_ptr FindLocalDestination (const i2p::data::IdentHash& destination) const; bool LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, @@ -113,7 +123,7 @@ namespace client template void VisitTunnels (Visitor v); // Visitor: (I2PService *) -> bool, true means retain - void CreateNewSharedLocalDestination (); + void CreateNewSharedLocalDestination (); void AddLocalDestination (std::shared_ptr localDestination); private: @@ -140,6 +150,7 @@ namespace client std::unique_ptr m_CleanupUDPTimer; public: + // for HTTP const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; }; const decltype(m_ClientTunnels)& GetClientTunnels () const { return m_ClientTunnels; }; diff --git a/libi2pd_client/HTTPProxy.cpp b/libi2pd_client/HTTPProxy.cpp index af4d2913..64080752 100644 --- a/libi2pd_client/HTTPProxy.cpp +++ b/libi2pd_client/HTTPProxy.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2019, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -191,7 +191,7 @@ namespace proxy { res.body = ss.str(); std::string response = res.to_string(); boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::transfer_all(), - std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); + std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); } bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL & url, std::string & b64, bool & confirm) @@ -406,7 +406,7 @@ namespace proxy { void HTTPReqHandler::ForwardToUpstreamProxy() { LogPrint(eLogDebug, "HTTPProxy: forward to upstream"); - // build http requset + // build http request m_ClientRequestURL = m_RequestURL; LogPrint(eLogDebug, "HTTPProxy: ", m_ClientRequestURL.host); @@ -458,7 +458,7 @@ namespace proxy { if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port)); m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) { - m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamSocksProxyConnect, this, std::placeholders::_1)); + m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamSocksProxyConnect, this, std::placeholders::_1)); })); } else @@ -562,14 +562,16 @@ namespace proxy { if(m_ClientRequest.method == "CONNECT") { m_ClientResponse.code = 200; m_send_buf = m_ClientResponse.to_string(); - boost::asio::async_write(*m_sock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&] (const boost::system::error_code & ec, std::size_t transferred) { + boost::asio::async_write(*m_sock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&] (const boost::system::error_code & ec, std::size_t transferred) + { if(ec) GenericProxyError("socks proxy error", ec.message().c_str()); else HandoverToUpstreamProxy(); }); } else { m_send_buf = m_ClientRequestBuffer.str(); LogPrint(eLogDebug, "HTTPProxy: send ", m_send_buf.size(), " bytes"); - boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&](const boost::system::error_code & ec, std::size_t transferred) { + boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&](const boost::system::error_code & ec, std::size_t transferred) + { if(ec) GenericProxyError("failed to send request to upstream", ec.message().c_str()); else HandoverToUpstreamProxy(); }); diff --git a/libi2pd_client/HTTPProxy.h b/libi2pd_client/HTTPProxy.h index 590166e3..69ed4cef 100644 --- a/libi2pd_client/HTTPProxy.h +++ b/libi2pd_client/HTTPProxy.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef HTTP_PROXY_H__ #define HTTP_PROXY_H__ @@ -6,6 +14,7 @@ namespace proxy { class HTTPProxy: public i2p::client::TCPIPAcceptor { public: + HTTPProxy(const std::string& name, const std::string& address, int port, const std::string & outproxy, bool addresshelper, std::shared_ptr localDestination); HTTPProxy(const std::string& name, const std::string& address, int port, std::shared_ptr localDestination = nullptr) : HTTPProxy(name, address, port, "", true, localDestination) {} ; @@ -15,11 +24,13 @@ namespace proxy { bool GetHelperSupport() { return m_Addresshelper; } protected: + // Implements TCPIPAcceptor std::shared_ptr CreateHandler(std::shared_ptr socket); const char* GetName() { return m_Name.c_str (); } private: + std::string m_Name; std::string m_OutproxyUrl; bool m_Addresshelper; diff --git a/libi2pd_client/I2CP.cpp b/libi2pd_client/I2CP.cpp index f4c8a91e..fef5d6b6 100644 --- a/libi2pd_client/I2CP.cpp +++ b/libi2pd_client/I2CP.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2019, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -24,7 +24,7 @@ namespace client { I2CPDestination::I2CPDestination (std::shared_ptr owner, std::shared_ptr identity, bool isPublic, const std::map& params): - RunnableService ("I2CP"), LeaseSetDestination (GetIOService (), isPublic, ¶ms), + RunnableService ("I2CP"), LeaseSetDestination (GetIOService (), isPublic, ¶ms), m_Owner (owner), m_Identity (identity), m_EncryptionKeyType (m_Identity->GetCryptoKeyType ()) { } @@ -33,26 +33,26 @@ namespace client { if (IsRunning ()) Stop (); - } - + } + void I2CPDestination::Start () { if (!IsRunning ()) - { + { LeaseSetDestination::Start (); StartIOService (); - } + } } - + void I2CPDestination::Stop () { if (IsRunning ()) - { + { LeaseSetDestination::Stop (); StopIOService (); - } - } - + } + } + void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key) { memcpy (m_EncryptionPrivateKey, key, 256); @@ -98,7 +98,7 @@ namespace client auto ls = (storeType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) ? std::make_shared (m_Identity, buf, len): std::make_shared (storeType, m_Identity, buf, len); - ls->SetExpirationTime (m_LeaseSetExpirationTime); + ls->SetExpirationTime (m_LeaseSetExpirationTime); SetLeaseSet (ls); } @@ -221,7 +221,7 @@ namespace client auto s = shared_from_this (); m_Socket->async_read_some (boost::asio::buffer (m_Header, 1), [s](const boost::system::error_code& ecode, std::size_t bytes_transferred) - { + { if (!ecode && bytes_transferred > 0 && s->m_Header[0] == I2CP_PROTOCOL_BYTE) s->ReceiveHeader (); else @@ -247,15 +247,15 @@ namespace client if (m_PayloadLen > 0) { if (m_PayloadLen <= I2CP_MAX_MESSAGE_LENGTH) - { + { m_Payload = new uint8_t[m_PayloadLen]; ReceivePayload (); } else { - LogPrint (eLogError, "I2CP: Unexpected payload length ", m_PayloadLen); + LogPrint (eLogError, "I2CP: Unexpected payload length ", m_PayloadLen); Terminate (); - } + } } else // no following payload { @@ -323,7 +323,7 @@ namespace client memcpy (buf + I2CP_HEADER_SIZE, payload, len); boost::asio::async_write (*socket, boost::asio::buffer (buf, l), boost::asio::transfer_all (), std::bind(&I2CPSession::HandleI2CPMessageSent, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, buf)); + std::placeholders::_1, std::placeholders::_2, buf)); } else LogPrint (eLogError, "I2CP: Can't write to the socket"); @@ -510,8 +510,8 @@ namespace client } else LogPrint(eLogError, "I2CP: short message"); - SendSessionStatusMessage (status); - } + SendSessionStatusMessage (status); + } void I2CPSession::SendSessionStatusMessage (uint8_t status) { @@ -568,12 +568,12 @@ namespace client { LogPrint (eLogError, "I2CP: invalid LeaseSet2 of type ", storeType); return; - } + } offset += ls.GetBufferLen (); // private keys int numPrivateKeys = buf[offset]; offset++; uint16_t currentKeyType = 0; - const uint8_t * currentKey = nullptr; + const uint8_t * currentKey = nullptr; for (int i = 0; i < numPrivateKeys; i++) { if (offset + 4 > len) return; @@ -586,7 +586,7 @@ namespace client currentKey = buf + offset; } offset += keyLen; - } + } // TODO: support multiple keys if (currentKey) { @@ -594,7 +594,7 @@ namespace client m_Destination->SetEncryptionType (currentKeyType); } - m_Destination->LeaseSet2Created (storeType, ls.GetBuffer (), ls.GetBufferLen ()); + m_Destination->LeaseSet2Created (storeType, ls.GetBuffer (), ls.GetBufferLen ()); } } else @@ -779,14 +779,14 @@ namespace client memcpy (buf + I2CP_HEADER_SIZE + 10, payload, len); boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, l), boost::asio::transfer_all (), std::bind(&I2CPSession::HandleI2CPMessageSent, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, buf)); + std::placeholders::_1, std::placeholders::_2, buf)); } I2CPServer::I2CPServer (const std::string& interface, int port): m_IsRunning (false), m_Thread (nullptr), m_Acceptor (m_Service, #ifdef ANDROID - I2CPSession::proto::endpoint(std::string (1, '\0') + interface)) // leading 0 for abstract address + I2CPSession::proto::endpoint(std::string (1, '\0') + interface)) // leading 0 for abstract address #else I2CPSession::proto::endpoint(boost::asio::ip::address::from_string(interface), port)) #endif @@ -823,10 +823,10 @@ namespace client m_IsRunning = false; m_Acceptor.cancel (); { - auto sessions = m_Sessions; + auto sessions = m_Sessions; for (auto& it: sessions) it.second->Stop (); - } + } m_Sessions.clear (); m_Service.stop (); if (m_Thread) @@ -899,4 +899,3 @@ namespace client } } } - diff --git a/libi2pd_client/I2CP.h b/libi2pd_client/I2CP.h index 7f590555..f4e83f06 100644 --- a/libi2pd_client/I2CP.h +++ b/libi2pd_client/I2CP.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2019, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -25,7 +25,7 @@ namespace client const uint8_t I2CP_PROTOCOL_BYTE = 0x2A; const size_t I2CP_SESSION_BUFFER_SIZE = 4096; const size_t I2CP_MAX_MESSAGE_LENGTH = 65535; - + const size_t I2CP_HEADER_LENGTH_OFFSET = 0; const size_t I2CP_HEADER_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4; const size_t I2CP_HEADER_SIZE = I2CP_HEADER_TYPE_OFFSET + 1; @@ -69,10 +69,10 @@ namespace client I2CPDestination (std::shared_ptr owner, std::shared_ptr identity, bool isPublic, const std::map& params); ~I2CPDestination (); - + void Start (); void Stop (); - + void SetEncryptionPrivateKey (const uint8_t * key); void SetEncryptionType (i2p::data::CryptoKeyType keyType) { m_EncryptionKeyType = keyType; }; void LeaseSetCreated (const uint8_t * buf, size_t len); // called from I2CPSession @@ -82,7 +82,7 @@ namespace client // implements LocalDestination bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const; bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const { return m_EncryptionKeyType == keyType; }; - // TODO: implement GetEncryptionPublicKey + // TODO: implement GetEncryptionPublicKey std::shared_ptr GetIdentity () const { return m_Identity; }; protected: @@ -220,4 +220,3 @@ namespace client } #endif - diff --git a/libi2pd_client/I2PService.cpp b/libi2pd_client/I2PService.cpp index 7157020f..83838106 100644 --- a/libi2pd_client/I2PService.cpp +++ b/libi2pd_client/I2PService.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include "Destination.h" #include "Identity.h" #include "ClientContext.h" @@ -84,7 +92,7 @@ namespace client auto itr = m_ReadyCallbacks.begin(); while(itr != m_ReadyCallbacks.end()) { - if(itr->second != NEVER_TIMES_OUT && now >= itr->second) + if(itr->second != NEVER_TIMES_OUT && now >= itr->second) { itr->first(boost::asio::error::timed_out); itr = m_ReadyCallbacks.erase(itr); @@ -94,9 +102,9 @@ namespace client } } if(!ec && m_ReadyCallbacks.size()) - TriggerReadyCheckTimer(); + TriggerReadyCheckTimer(); else - m_ReadyTimerTriggered = false; + m_ReadyTimerTriggered = false; } void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) { @@ -115,22 +123,24 @@ namespace client { if(m_ConnectTimeout && !m_LocalDestination->IsReady()) { - AddReadyCallback([this, streamRequestComplete, address, port] (const boost::system::error_code & ec) { + AddReadyCallback([this, streamRequestComplete, address, port] (const boost::system::error_code & ec) + { if(ec) { - LogPrint(eLogWarning, "I2PService::CeateStream() ", ec.message()); + LogPrint(eLogWarning, "I2PService::CreateStream() ", ec.message()); streamRequestComplete(nullptr); } else - { if (address->IsIdentHash ()) + { + if (address->IsIdentHash ()) this->m_LocalDestination->CreateStream(streamRequestComplete, address->identHash, port); else - this->m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port); + this->m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port); } }); } else - { + { if (address->IsIdentHash ()) m_LocalDestination->CreateStream (streamRequestComplete, address->identHash, port); else @@ -180,7 +190,7 @@ namespace client { m_up->async_read_some(boost::asio::buffer(m_upstream_to_down_buf, TCP_IP_PIPE_BUFFER_SIZE), std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); + std::placeholders::_1, std::placeholders::_2)); } else LogPrint(eLogError, "TCPIPPipe: upstream receive: no socket"); @@ -191,7 +201,7 @@ namespace client if (m_down) { m_down->async_read_some(boost::asio::buffer(m_downstream_to_up_buf, TCP_IP_PIPE_BUFFER_SIZE), std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); + std::placeholders::_1, std::placeholders::_2)); } else LogPrint(eLogError, "TCPIPPipe: downstream receive: no socket"); @@ -205,8 +215,8 @@ namespace client boost::asio::async_write(*m_up, boost::asio::buffer(m_upstream_buf, len), boost::asio::transfer_all(), std::bind(&TCPIPPipe::HandleUpstreamWrite, - shared_from_this(), - std::placeholders::_1)); + shared_from_this(), + std::placeholders::_1)); } else LogPrint(eLogError, "TCPIPPipe: upstream write: no socket"); @@ -220,8 +230,8 @@ namespace client boost::asio::async_write(*m_down, boost::asio::buffer(m_downstream_buf, len), boost::asio::transfer_all(), std::bind(&TCPIPPipe::HandleDownstreamWrite, - shared_from_this(), - std::placeholders::_1)); + shared_from_this(), + std::placeholders::_1)); } else LogPrint(eLogError, "TCPIPPipe: downstream write: no socket"); @@ -283,7 +293,7 @@ namespace client void TCPIPAcceptor::Start () { m_Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), m_LocalEndpoint)); - //update the local end point in case port has been set zero and got updated now + // update the local end point in case port has been set zero and got updated now m_LocalEndpoint = m_Acceptor->local_endpoint(); m_Acceptor->listen (); Accept (); diff --git a/libi2pd_client/I2PService.h b/libi2pd_client/I2PService.h index e0dfd2da..e14f85c1 100644 --- a/libi2pd_client/I2PService.h +++ b/libi2pd_client/I2PService.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef I2PSERVICE_H__ #define I2PSERVICE_H__ @@ -18,10 +26,12 @@ namespace client class I2PService : public std::enable_shared_from_this { public: + typedef std::function ReadyCallback; public: - I2PService (std::shared_ptr localDestination = nullptr); + + I2PService (std::shared_ptr localDestination = nullptr); I2PService (i2p::data::SigningKeyType kt); virtual ~I2PService (); @@ -42,7 +52,7 @@ namespace client void AddReadyCallback(ReadyCallback cb); inline std::shared_ptr GetLocalDestination () { return m_LocalDestination; } - inline std::shared_ptr GetLocalDestination () const { return m_LocalDestination; } + inline std::shared_ptr GetLocalDestination () const { return m_LocalDestination; } inline void SetLocalDestination (std::shared_ptr dest) { if (m_LocalDestination) m_LocalDestination->Release (); @@ -59,21 +69,24 @@ namespace client virtual const char* GetName() { return "Generic I2P Service"; } private: + void TriggerReadyCheckTimer(); void HandleReadyCheckTimer(const boost::system::error_code & ec); private: + std::shared_ptr m_LocalDestination; std::unordered_set > m_Handlers; std::mutex m_HandlersMutex; std::vector > m_ReadyCallbacks; boost::asio::deadline_timer m_ReadyTimer; - bool m_ReadyTimerTriggered; + bool m_ReadyTimerTriggered; uint32_t m_ConnectTimeout; - const size_t NEVER_TIMES_OUT = 0; - + const size_t NEVER_TIMES_OUT = 0; + public: + bool isUpdated; // transient, used during reload only }; @@ -81,6 +94,7 @@ namespace client class I2PServiceHandler { public: + I2PServiceHandler(I2PService * parent) : m_Service(parent), m_Dead(false) { } virtual ~I2PServiceHandler() { } //If you override this make sure you call it from the children @@ -89,6 +103,7 @@ namespace client void Terminate () { Kill (); }; protected: + // Call when terminating or handing over to avoid race conditions inline bool Kill () { return m_Dead.exchange(true); } // Call to know if the handler is dead @@ -99,6 +114,7 @@ namespace client inline I2PService * GetOwner() { return m_Service; } private: + I2PService *m_Service; std::atomic m_Dead; //To avoid cleaning up multiple times }; @@ -109,11 +125,13 @@ namespace client class TCPIPPipe: public I2PServiceHandler, public std::enable_shared_from_this { public: + TCPIPPipe(I2PService * owner, std::shared_ptr upstream, std::shared_ptr downstream); ~TCPIPPipe(); void Start(); protected: + void Terminate(); void AsyncReceiveUpstream(); void AsyncReceiveDownstream(); @@ -125,6 +143,7 @@ namespace client void DownstreamWrite(size_t len); private: + uint8_t m_upstream_to_down_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_to_up_buf[TCP_IP_PIPE_BUFFER_SIZE]; uint8_t m_upstream_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_buf[TCP_IP_PIPE_BUFFER_SIZE]; std::shared_ptr m_up, m_down; @@ -135,6 +154,7 @@ namespace client class TCPIPAcceptor: public I2PService { public: + TCPIPAcceptor (const std::string& address, int port, std::shared_ptr localDestination = nullptr) : I2PService(localDestination), m_LocalEndpoint (boost::asio::ip::address::from_string(address), port), @@ -149,14 +169,16 @@ namespace client //If you override this make sure you call it from the children void Stop (); - const boost::asio::ip::tcp::endpoint& GetLocalEndpoint () const { return m_LocalEndpoint; }; + const boost::asio::ip::tcp::endpoint& GetLocalEndpoint () const { return m_LocalEndpoint; }; virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; } protected: + virtual std::shared_ptr CreateHandler(std::shared_ptr socket) = 0; private: + void Accept(); void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); boost::asio::ip::tcp::endpoint m_LocalEndpoint; diff --git a/libi2pd_client/I2PTunnel.cpp b/libi2pd_client/I2PTunnel.cpp index 290ce11e..5b384bff 100644 --- a/libi2pd_client/I2PTunnel.cpp +++ b/libi2pd_client/I2PTunnel.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include "Base.h" #include "Log.h" @@ -69,15 +77,15 @@ namespace client return ourIP; } +#ifdef __linux__ static void MapToLoopback(const std::shared_ptr & sock, const i2p::data::IdentHash & addr) { - // bind to 127.x.x.x address // where x.x.x are first three bytes from ident auto ourIP = GetLoopbackAddressFor(addr); sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0)); - } +#endif void I2PTunnelConnection::Connect (bool isUniqueLocal) { @@ -168,7 +176,7 @@ namespace client { m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2), + std::placeholders::_1, std::placeholders::_2), I2P_TUNNEL_CONNECTION_MAX_IDLE); } else // closed by peer @@ -257,7 +265,7 @@ namespace client if (!m_ConnectionSent && !line.compare(0, 10, "Connection")) { /* close connection, if not Connection: (U|u)pgrade (for websocket) */ - auto x = line.find("pgrade"); + auto x = line.find("pgrade"); if (x != std::string::npos && std::tolower(line[x - 1]) == 'u') m_OutHeader << line << "\r\n"; else @@ -281,7 +289,7 @@ namespace client if (endOfHeader) { if (!m_ConnectionSent) m_OutHeader << "Connection: close\r\n"; - if (!m_ProxyConnectionSent) m_OutHeader << "Proxy-Connection: close\r\n"; + if (!m_ProxyConnectionSent) m_OutHeader << "Proxy-Connection: close\r\n"; m_OutHeader << "\r\n"; // end of header m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header m_InHeader.str (""); @@ -389,7 +397,7 @@ namespace client } - /* This handler tries to stablish a connection with the desired server and dies if it fails to do so */ + /* This handler tries to establish a connection with the desired server and dies if it fails to do so */ class I2PClientTunnelHandler: public I2PServiceHandler, public std::enable_shared_from_this { public: @@ -462,7 +470,7 @@ namespace client } /* HACK: maybe we should create a caching IdentHash provider in AddressBook */ - std::shared_ptr I2PClientTunnel::GetAddress () + std::shared_ptr I2PClientTunnel::GetAddress () { if (!m_Address) { @@ -477,7 +485,7 @@ namespace client { auto address = GetAddress (); if (address) - return std::make_shared(this, address, m_DestinationPort, socket); + return std::make_shared(this, address, m_DestinationPort, socket); else return nullptr; } @@ -696,7 +704,7 @@ namespace client } I2PUDPServerTunnel::I2PUDPServerTunnel(const std::string & name, std::shared_ptr localDestination, - boost::asio::ip::address localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port) : + boost::asio::ip::address localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port, bool gzip) : m_IsUniqueLocal(true), m_Name(name), m_LocalAddress(localAddress), @@ -704,7 +712,7 @@ namespace client { m_LocalDest = localDestination; m_LocalDest->Start(); - auto dgram = m_LocalDest->CreateDatagramDestination(); + auto dgram = m_LocalDest->CreateDatagramDestination(gzip); dgram->SetReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); } @@ -745,7 +753,7 @@ namespace client I2PUDPClientTunnel::I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest, boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr localDestination, - uint16_t remotePort) : + uint16_t remotePort, bool gzip) : m_Name(name), m_RemoteDest(remoteDest), m_LocalDest(localDestination), @@ -756,7 +764,7 @@ namespace client RemotePort(remotePort), m_cancel_resolve(false) { - auto dgram = m_LocalDest->CreateDatagramDestination(); + auto dgram = m_LocalDest->CreateDatagramDestination(gzip); dgram->SetReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, @@ -826,7 +834,7 @@ namespace client { LogPrint(eLogError, "UDP Tunnel: ", m_RemoteDest, " not found"); return; - } + } m_RemoteIdent = new i2p::data::IdentHash; *m_RemoteIdent = addr->identHash; LogPrint(eLogInfo, "UDP Tunnel: resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32()); diff --git a/libi2pd_client/I2PTunnel.h b/libi2pd_client/I2PTunnel.h index 0d1ac9a8..d4db80d8 100644 --- a/libi2pd_client/I2PTunnel.h +++ b/libi2pd_client/I2PTunnel.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef I2PTUNNEL_H__ #define I2PTUNNEL_H__ @@ -30,6 +38,7 @@ namespace client class I2PTunnelConnection: public I2PServiceHandler, public std::enable_shared_from_this { public: + I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, std::shared_ptr leaseSet, int port = 0); // to I2P I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, @@ -41,6 +50,7 @@ namespace client void Connect (bool isUniqueLocal = true); protected: + void Terminate (); void Receive (); @@ -55,6 +65,7 @@ namespace client std::shared_ptr GetSocket () const { return m_Socket; }; private: + uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE]; std::shared_ptr m_Socket; std::shared_ptr m_Stream; @@ -65,15 +76,18 @@ namespace client class I2PClientTunnelConnectionHTTP: public I2PTunnelConnection { public: + I2PClientTunnelConnectionHTTP (I2PService * owner, std::shared_ptr socket, std::shared_ptr stream): I2PTunnelConnection (owner, socket, stream), m_HeaderSent (false), m_ConnectionSent (false), m_ProxyConnectionSent (false) {}; protected: + void Write (const uint8_t * buf, size_t len); private: + std::stringstream m_InHeader, m_OutHeader; bool m_HeaderSent, m_ConnectionSent, m_ProxyConnectionSent; }; @@ -81,14 +95,17 @@ namespace client class I2PServerTunnelConnectionHTTP: public I2PTunnelConnection { public: + I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, std::shared_ptr socket, const boost::asio::ip::tcp::endpoint& target, const std::string& host); protected: + void Write (const uint8_t * buf, size_t len); private: + std::string m_Host; std::stringstream m_InHeader, m_OutHeader; bool m_HeaderSent; @@ -98,14 +115,17 @@ namespace client class I2PTunnelConnectionIRC: public I2PTunnelConnection { public: + I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr stream, std::shared_ptr socket, const boost::asio::ip::tcp::endpoint& target, const std::string& m_WebircPass); protected: + void Write (const uint8_t * buf, size_t len); private: + std::shared_ptr m_From; std::stringstream m_OutPacket, m_InPacket; bool m_NeedsWebIrc; @@ -116,10 +136,12 @@ namespace client class I2PClientTunnel: public TCPIPAcceptor { protected: + // Implements TCPIPAcceptor std::shared_ptr CreateHandler(std::shared_ptr socket); public: + I2PClientTunnel (const std::string& name, const std::string& destination, const std::string& address, int port, std::shared_ptr localDestination, int destinationPort = 0); ~I2PClientTunnel () {} @@ -130,9 +152,11 @@ namespace client const char* GetName() { return m_Name.c_str (); } private: + std::shared_ptr GetAddress (); private: + std::string m_Name, m_Destination; std::shared_ptr m_Address; int m_DestinationPort; @@ -160,9 +184,9 @@ namespace client uint8_t m_Buffer[I2P_UDP_MAX_MTU]; UDPSession(boost::asio::ip::udp::endpoint localEndpoint, - const std::shared_ptr & localDestination, - boost::asio::ip::udp::endpoint remote, const i2p::data::IdentHash * ident, - uint16_t ourPort, uint16_t theirPort); + const std::shared_ptr & localDestination, + boost::asio::ip::udp::endpoint remote, const i2p::data::IdentHash * ident, + uint16_t ourPort, uint16_t theirPort); void HandleReceived(const boost::system::error_code & ecode, std::size_t len); void Receive(); }; @@ -195,10 +219,11 @@ namespace client class I2PUDPServerTunnel { public: + I2PUDPServerTunnel(const std::string & name, std::shared_ptr localDestination, boost::asio::ip::address localAddress, - boost::asio::ip::udp::endpoint forwardTo, uint16_t port); + boost::asio::ip::udp::endpoint forwardTo, uint16_t port, bool gzip); ~I2PUDPServerTunnel(); /** expire stale udp conversations */ void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT); @@ -210,10 +235,12 @@ namespace client void SetUniqueLocal(bool isUniqueLocal = true) { m_IsUniqueLocal = isUniqueLocal; } private: + void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); UDPSessionPtr ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort); private: + bool m_IsUniqueLocal; const std::string m_Name; boost::asio::ip::address m_LocalAddress; @@ -226,9 +253,10 @@ namespace client class I2PUDPClientTunnel { public: + I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest, boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr localDestination, - uint16_t remotePort); + uint16_t remotePort, bool gzip); ~I2PUDPClientTunnel(); void Start(); const char * GetName() const { return m_Name.c_str(); } @@ -240,6 +268,7 @@ namespace client void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT); private: + typedef std::pair UDPConvo; void RecvFromLocal(); void HandleRecvFromLocal(const boost::system::error_code & e, std::size_t transferred); @@ -263,6 +292,7 @@ namespace client class I2PServerTunnel: public I2PService { public: + I2PServerTunnel (const std::string& name, const std::string& address, int port, std::shared_ptr localDestination, int inport = 0, bool gzip = true); @@ -282,6 +312,7 @@ namespace client const char* GetName() { return m_Name.c_str (); } private: + void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it, std::shared_ptr resolver); @@ -290,6 +321,7 @@ namespace client virtual std::shared_ptr CreateI2PConnection (std::shared_ptr stream); private: + bool m_IsUniqueLocal; std::string m_Name, m_Address; int m_Port; @@ -302,28 +334,34 @@ namespace client class I2PServerTunnelHTTP: public I2PServerTunnel { public: + I2PServerTunnelHTTP (const std::string& name, const std::string& address, int port, std::shared_ptr localDestination, const std::string& host, int inport = 0, bool gzip = true); private: + std::shared_ptr CreateI2PConnection (std::shared_ptr stream); private: + std::string m_Host; }; class I2PServerTunnelIRC: public I2PServerTunnel { public: + I2PServerTunnelIRC (const std::string& name, const std::string& address, int port, std::shared_ptr localDestination, const std::string& webircpass, int inport = 0, bool gzip = true); private: + std::shared_ptr CreateI2PConnection (std::shared_ptr stream); private: + std::string m_WebircPass; }; } diff --git a/libi2pd_client/MatchedDestination.cpp b/libi2pd_client/MatchedDestination.cpp index 5cec178f..4ffa7442 100644 --- a/libi2pd_client/MatchedDestination.cpp +++ b/libi2pd_client/MatchedDestination.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include "MatchedDestination.h" #include "Log.h" #include "ClientContext.h" @@ -14,10 +22,10 @@ namespace client void MatchedTunnelDestination::ResolveCurrentLeaseSet() { - auto addr = i2p::client::context.GetAddressBook().GetAddress (m_RemoteName); + auto addr = i2p::client::context.GetAddressBook().GetAddress (m_RemoteName); if(addr && addr->IsIdentHash ()) { - m_RemoteIdent = addr->identHash; + m_RemoteIdent = addr->identHash; auto ls = FindLeaseSet(m_RemoteIdent); if(ls) HandleFoundCurrentLeaseSet(ls); @@ -39,7 +47,7 @@ namespace client { m_ResolveTimer->expires_from_now(boost::posix_time::seconds(1)); m_ResolveTimer->async_wait([&](const boost::system::error_code & ec) { - if(!ec) ResolveCurrentLeaseSet(); + if(!ec) ResolveCurrentLeaseSet(); }); } } @@ -50,7 +58,7 @@ namespace client ClientDestination::Start(); m_ResolveTimer = std::make_shared(GetService()); GetTunnelPool()->SetCustomPeerSelector(this); - ResolveCurrentLeaseSet(); + ResolveCurrentLeaseSet(); } void MatchedTunnelDestination::Stop() diff --git a/libi2pd_client/MatchedDestination.h b/libi2pd_client/MatchedDestination.h index 9d61799a..30ad8942 100644 --- a/libi2pd_client/MatchedDestination.h +++ b/libi2pd_client/MatchedDestination.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef MATCHED_DESTINATION_H_ #define MATCHED_DESTINATION_H_ #include "Destination.h" @@ -8,26 +16,30 @@ namespace i2p namespace client { /** - client tunnel that uses same OBEP as IBGW of each remote lease for a remote destination + * client tunnel that uses same OBEP as IBGW of each remote lease for a remote destination */ class MatchedTunnelDestination : public RunnableClientDestination, public i2p::tunnel::ITunnelPeerSelector { - public: - MatchedTunnelDestination(const i2p::data::PrivateKeys& keys, const std::string & remoteName, const std::map * params = nullptr); - void Start(); - void Stop(); + public: - bool SelectPeers(i2p::tunnel::Path & peers, int hops, bool inbound); + MatchedTunnelDestination(const i2p::data::PrivateKeys& keys, const std::string & remoteName, + const std::map * params = nullptr); + void Start(); + void Stop(); - private: - void ResolveCurrentLeaseSet(); - void HandleFoundCurrentLeaseSet(std::shared_ptr ls); + bool SelectPeers(i2p::tunnel::Path & peers, int hops, bool inbound); - private: - std::string m_RemoteName; - i2p::data::IdentHash m_RemoteIdent; - std::shared_ptr m_RemoteLeaseSet; - std::shared_ptr m_ResolveTimer; + private: + + void ResolveCurrentLeaseSet(); + void HandleFoundCurrentLeaseSet(std::shared_ptr ls); + + private: + + std::string m_RemoteName; + i2p::data::IdentHash m_RemoteIdent; + std::shared_ptr m_RemoteLeaseSet; + std::shared_ptr m_ResolveTimer; }; } } diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index d0942221..2c6b711d 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #ifdef _MSC_VER @@ -17,7 +25,7 @@ namespace client { SAMSocket::SAMSocket (SAMBridge& owner): m_Owner (owner), m_Socket(owner.GetService()), m_Timer (m_Owner.GetService ()), - m_BufferOffset (0), + m_BufferOffset (0), m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false), m_IsAccepting (false), m_Stream (nullptr) { @@ -26,7 +34,7 @@ namespace client SAMSocket::~SAMSocket () { m_Stream = nullptr; - } + } void SAMSocket::Terminate (const char* reason) { @@ -54,8 +62,7 @@ namespace client } break; } - default: - ; + default: ; } m_SocketType = eSAMSocketTypeTerminated; if (m_Socket.is_open ()) @@ -68,7 +75,7 @@ namespace client } void SAMSocket::ReceiveHandshake () - { + { m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); @@ -152,7 +159,7 @@ namespace client size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); #endif boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (), - std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (), + std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } else @@ -170,7 +177,7 @@ namespace client { return id == m_ID; } - + void SAMSocket::HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) @@ -350,7 +357,7 @@ namespace client } std::shared_ptr forward = nullptr; - if ((type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw) && + if ((type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw) && params.find(SAM_VALUE_HOST) != params.end() && params.find(SAM_VALUE_PORT) != params.end()) { // udp forward selected @@ -372,7 +379,7 @@ namespace client } forward = std::make_shared(addr, port); } - + //ensure we actually received a destination if (destination.empty()) { @@ -381,7 +388,7 @@ namespace client } if (destination != SAM_VALUE_TRANSIENT) - { + { //ensure it's a base64 string i2p::data::PrivateKeys keys; if (!keys.FromBase64(destination)) @@ -389,7 +396,7 @@ namespace client SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true); return; } - } + } // create destination auto session = m_Owner.CreateSession (id, type, destination == SAM_VALUE_TRANSIENT ? "" : destination, ¶ms); @@ -542,7 +549,7 @@ namespace client m_SocketType = eSAMSocketTypeAcceptor; if (!session->localDestination->IsAcceptingStreams ()) { - m_IsAccepting = true; + m_IsAccepting = true; session->localDestination->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); } SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); @@ -567,7 +574,7 @@ namespace client { i2p::data::IdentityEx dest; dest.FromBase64 (params[SAM_PARAM_DESTINATION]); - if (session->Type == eSAMSessionTypeDatagram) + if (session->Type == eSAMSessionTypeDatagram) d->SendDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ()); else // raw d->SendRawDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ()); @@ -598,20 +605,20 @@ namespace client if (it != params.end ()) { if (!m_Owner.ResolveSignatureType (it->second, signatureType)) - LogPrint (eLogWarning, "SAM: ", SAM_PARAM_SIGNATURE_TYPE, " is invalid ", it->second); + LogPrint (eLogWarning, "SAM: ", SAM_PARAM_SIGNATURE_TYPE, " is invalid ", it->second); } it = params.find (SAM_PARAM_CRYPTO_TYPE); if (it != params.end ()) { try - { + { cryptoType = std::stoi(it->second); } - catch (const std::exception& ex) + catch (const std::exception& ex) { - LogPrint (eLogWarning, "SAM: ", SAM_PARAM_CRYPTO_TYPE, "error: ", ex.what ()); - } - } + LogPrint (eLogWarning, "SAM: ", SAM_PARAM_CRYPTO_TYPE, "error: ", ex.what ()); + } + } auto keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType); #ifdef _MSC_VER size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, @@ -647,12 +654,12 @@ namespace client else dest->RequestDestination (addr->identHash, std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete, - shared_from_this (), std::placeholders::_1, name)); + shared_from_this (), std::placeholders::_1, name)); } else dest->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete, - shared_from_this (), std::placeholders::_1, name)); + shared_from_this (), std::placeholders::_1, name)); } else { @@ -762,7 +769,7 @@ namespace client if (m_Stream) { if (m_Stream->GetStatus () == i2p::stream::eStreamStatusNew || - m_Stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular + m_Stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular { m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE), std::bind (&SAMSocket::HandleI2PReceive, shared_from_this(), @@ -800,7 +807,7 @@ namespace client { delete [] buff; } - + void SAMSocket::WriteI2PData(size_t sz) { boost::asio::async_write ( @@ -809,7 +816,7 @@ namespace client boost::asio::transfer_all(), std::bind(&SAMSocket::HandleWriteI2PData, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } - + void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) @@ -955,7 +962,7 @@ namespace client auto ep = session->UDPEndpoint; if (ep) // udp forward enabled - m_Owner.SendTo(buf, len, ep); + m_Owner.SendTo(buf, len, ep); else { #ifdef _MSC_VER @@ -978,7 +985,7 @@ namespace client { m_Owner.GetService ().post (std::bind( !ec ? &SAMSocket::Receive : &SAMSocket::TerminateClose, shared_from_this())); } - + SAMSession::SAMSession (SAMBridge & parent, const std::string & id, SAMSessionType type, std::shared_ptr dest): m_Bridge(parent), localDestination (dest), @@ -986,7 +993,7 @@ namespace client Name(id), Type (type) { } - + SAMSession::~SAMSession () { i2p::client::context.DeleteLocalDestination (localDestination); @@ -1001,7 +1008,7 @@ namespace client } SAMBridge::SAMBridge (const std::string& address, int port, bool singleThread): - RunnableService ("SAM"), m_IsSingleThread (singleThread), + RunnableService ("SAM"), m_IsSingleThread (singleThread), m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)), m_DatagramEndpoint (boost::asio::ip::address::from_string(address), port-1), m_DatagramSocket (GetIOService (), m_DatagramEndpoint), m_SignatureTypes @@ -1063,7 +1070,7 @@ namespace client std::unique_lock lock(m_OpenSocketsMutex); m_OpenSockets.remove_if([socket](const std::shared_ptr & item) -> bool { return item == socket; }); } - + void SAMBridge::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket) { if (!ecode) @@ -1089,7 +1096,7 @@ namespace client Accept (); } - std::shared_ptr SAMBridge::CreateSession (const std::string& id, SAMSessionType type, + std::shared_ptr SAMBridge::CreateSession (const std::string& id, SAMSessionType type, const std::string& destination, const std::map * params) { std::shared_ptr localDestination = nullptr; @@ -1097,7 +1104,7 @@ namespace client { i2p::data::PrivateKeys keys; if (!keys.FromBase64 (destination)) return nullptr; - localDestination = m_IsSingleThread ? + localDestination = m_IsSingleThread ? i2p::client::context.CreateNewLocalDestination (GetIOService (), keys, true, params) : i2p::client::context.CreateNewLocalDestination (keys, true, params); } @@ -1110,24 +1117,24 @@ namespace client { auto it = params->find (SAM_PARAM_SIGNATURE_TYPE); if (it != params->end ()) - { + { if (!ResolveSignatureType (it->second, signatureType)) - LogPrint (eLogWarning, "SAM: ", SAM_PARAM_SIGNATURE_TYPE, " is invalid ", it->second); - } + LogPrint (eLogWarning, "SAM: ", SAM_PARAM_SIGNATURE_TYPE, " is invalid ", it->second); + } it = params->find (SAM_PARAM_CRYPTO_TYPE); if (it != params->end ()) - { + { try - { + { cryptoType = std::stoi(it->second); } - catch (const std::exception& ex) + catch (const std::exception& ex) { - LogPrint (eLogWarning, "SAM: ", SAM_PARAM_CRYPTO_TYPE, "error: ", ex.what ()); - } - } + LogPrint (eLogWarning, "SAM: ", SAM_PARAM_CRYPTO_TYPE, "error: ", ex.what ()); + } + } } - localDestination = m_IsSingleThread ? + localDestination = m_IsSingleThread ? i2p::client::context.CreateNewLocalDestination (GetIOService (), true, signatureType, cryptoType, params) : i2p::client::context.CreateNewLocalDestination (true, signatureType, cryptoType, params); } @@ -1165,11 +1172,11 @@ namespace client { auto timer = std::make_shared(GetService ()); timer->expires_from_now (boost::posix_time::seconds(5)); // postpone destination clean for 5 seconds - timer->async_wait ([timer, session](const boost::system::error_code& ecode) + timer->async_wait ([timer, session](const boost::system::error_code& ecode) { // session's destructor is called here }); - } + } } } @@ -1193,7 +1200,7 @@ namespace client } return list; } - + void SAMBridge::SendTo(const uint8_t * buf, size_t len, std::shared_ptr remote) { if(remote) @@ -1261,7 +1268,7 @@ namespace client bool SAMBridge::ResolveSignatureType (const std::string& name, i2p::data::SigningKeyType& type) const { try - { + { type = std::stoi (name); } catch (const std::invalid_argument& ex) @@ -1273,12 +1280,12 @@ namespace client else return false; } - catch (const std::exception& ex) + catch (const std::exception& ex) { - return false; - } - // name has been resolved - return true; + return false; + } + // name has been resolved + return true; } } } diff --git a/libi2pd_client/SAM.h b/libi2pd_client/SAM.h index 5a447c06..ceda5253 100644 --- a/libi2pd_client/SAM.h +++ b/libi2pd_client/SAM.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef SAM_H__ #define SAM_H__ @@ -87,7 +95,7 @@ namespace client typedef boost::asio::ip::tcp::socket Socket_t; SAMSocket (SAMBridge& owner); - ~SAMSocket (); + ~SAMSocket (); Socket_t& GetSocket () { return m_Socket; }; void ReceiveHandshake (); @@ -97,10 +105,11 @@ namespace client void Terminate (const char* reason); bool IsSession(const std::string & id) const; - - private: + + private: + void TerminateClose() { Terminate(nullptr); } - + void HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred); @@ -137,7 +146,7 @@ namespace client void HandleWriteI2PDataImmediate(const boost::system::error_code & ec, uint8_t * buff); void HandleStreamSend(const boost::system::error_code & ec); - + private: SAMBridge& m_Owner; @@ -186,7 +195,7 @@ namespace client void Stop (); boost::asio::io_service& GetService () { return GetIOService (); }; - std::shared_ptr CreateSession (const std::string& id, SAMSessionType type, const std::string& destination, // empty string means transient + std::shared_ptr CreateSession (const std::string& id, SAMSessionType type, const std::string& destination, // empty string means transient const std::map * params); void CloseSession (const std::string& id); std::shared_ptr FindSession (const std::string& id) const; @@ -197,8 +206,8 @@ namespace client void SendTo(const uint8_t * buf, size_t len, std::shared_ptr remote); void RemoveSocket(const std::shared_ptr & socket); - - bool ResolveSignatureType (const std::string& name, i2p::data::SigningKeyType& type) const; + + bool ResolveSignatureType (const std::string& name, i2p::data::SigningKeyType& type) const; private: diff --git a/libi2pd_client/SOCKS.cpp b/libi2pd_client/SOCKS.cpp index 8742d4c5..c5428c86 100644 --- a/libi2pd_client/SOCKS.cpp +++ b/libi2pd_client/SOCKS.cpp @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include @@ -41,6 +49,7 @@ namespace proxy class SOCKSHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this { private: + enum state { GET_SOCKSV, @@ -118,7 +127,7 @@ namespace proxy boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method); boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port); boost::asio::const_buffers_1 GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port); - boost::asio::const_buffers_1 GenerateUpstreamRequest(); + boost::asio::const_buffers_1 GenerateUpstreamRequest(); bool Socks5ChooseAuth(); void SocksRequestFailed(errTypes error); void SocksRequestSuccess(); @@ -128,27 +137,27 @@ namespace proxy void HandleStreamRequestComplete (std::shared_ptr stream); void ForwardSOCKS(); - void SocksUpstreamSuccess(); - void AsyncUpstreamSockRead(); - void SendUpstreamRequest(); + void SocksUpstreamSuccess(); + void AsyncUpstreamSockRead(); + void SendUpstreamRequest(); void HandleUpstreamData(uint8_t * buff, std::size_t len); - void HandleUpstreamSockSend(const boost::system::error_code & ecode, std::size_t bytes_transfered); - void HandleUpstreamSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); - void HandleUpstreamConnected(const boost::system::error_code & ecode, - boost::asio::ip::tcp::resolver::iterator itr); - void HandleUpstreamResolved(const boost::system::error_code & ecode, - boost::asio::ip::tcp::resolver::iterator itr); + void HandleUpstreamSockSend(const boost::system::error_code & ecode, std::size_t bytes_transfered); + void HandleUpstreamSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); + void HandleUpstreamConnected(const boost::system::error_code & ecode, + boost::asio::ip::tcp::resolver::iterator itr); + void HandleUpstreamResolved(const boost::system::error_code & ecode, + boost::asio::ip::tcp::resolver::iterator itr); - boost::asio::ip::tcp::resolver m_proxy_resolver; - uint8_t m_sock_buff[socks_buffer_size]; - std::shared_ptr m_sock, m_upstreamSock; + boost::asio::ip::tcp::resolver m_proxy_resolver; + uint8_t m_sock_buff[socks_buffer_size]; + std::shared_ptr m_sock, m_upstreamSock; std::shared_ptr m_stream; uint8_t *m_remaining_data; //Data left to be sent uint8_t *m_remaining_upstream_data; //upstream data left to be forwarded uint8_t m_response[7+max_socks_hostname_size]; - uint8_t m_upstream_response[SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE]; - uint8_t m_upstream_request[14+max_socks_hostname_size]; - std::size_t m_upstream_response_len; + uint8_t m_upstream_response[SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE]; + uint8_t m_upstream_request[14+max_socks_hostname_size]; + std::size_t m_upstream_response_len; address m_address; //Address std::size_t m_remaining_data_len; //Size of the data left to be sent uint32_t m_4aip; //Used in 4a requests @@ -161,11 +170,12 @@ namespace proxy cmdTypes m_cmd; // Command requested state m_state; const bool m_UseUpstreamProxy; // do we want to use the upstream proxy for non i2p addresses? - const std::string m_UpstreamProxyAddress; - const uint16_t m_UpstreamProxyPort; + const std::string m_UpstreamProxyAddress; + const uint16_t m_UpstreamProxyPort; public: - SOCKSHandler(SOCKSServer * parent, std::shared_ptr sock, const std::string & upstreamAddr, const uint16_t upstreamPort, const bool useUpstream) : + + SOCKSHandler(SOCKSServer * parent, std::shared_ptr sock, const std::string & upstreamAddr, const uint16_t upstreamPort, const bool useUpstream) : I2PServiceHandler(parent), m_proxy_resolver(parent->GetService()), m_sock(sock), m_stream(nullptr), @@ -184,8 +194,8 @@ namespace proxy LogPrint(eLogDebug, "SOCKS: async sock read"); if (m_sock) { m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size), - std::bind(&SOCKSHandler::HandleSockRecv, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); + std::bind(&SOCKSHandler::HandleSockRecv, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); } else { LogPrint(eLogError,"SOCKS: no socket for read"); } @@ -217,39 +227,51 @@ namespace proxy boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port) { assert(error >= SOCKS4_OK); - m_response[0] = '\x00'; //Version - m_response[1] = error; //Response code - htobe16buf(m_response+2,port); //Port - htobe32buf(m_response+4,ip); //IP + m_response[0] = '\x00'; // version + m_response[1] = error; // response code + htobe16buf(m_response + 2, port); // port + htobe32buf(m_response + 4, ip); // IP return boost::asio::const_buffers_1(m_response,8); } boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type, const SOCKSHandler::address &addr, uint16_t port) { - size_t size = 6; + size_t size = 6; // header + port assert(error <= SOCKS5_ADDR_UNSUP); - m_response[0] = '\x05'; //Version - m_response[1] = error; //Response code - m_response[2] = '\x00'; //RSV - m_response[3] = type; //Address type + m_response[0] = '\x05'; // version + m_response[1] = error; // response code + m_response[2] = '\x00'; // reserved + m_response[3] = type; // address type switch (type) { case ADDR_IPV4: - size = 10; - htobe32buf(m_response+4,addr.ip); + size += 4; + htobe32buf(m_response + 4, addr.ip); + htobe16buf(m_response + size - 2, port); break; case ADDR_IPV6: - size = 22; - memcpy(m_response+4,addr.ipv6, 16); + size += 16; + memcpy(m_response + 4, addr.ipv6, 16); + htobe16buf(m_response + size - 2, port); break; case ADDR_DNS: - size = 7+addr.dns.size; - m_response[4] = addr.dns.size; - memcpy(m_response+5,addr.dns.value, addr.dns.size); + std::string address(addr.dns.value, addr.dns.size); + if(address.substr(addr.dns.size - 4, 4) == ".i2p") // overwrite if requested address inside I2P + { + m_response[3] = ADDR_IPV4; + size += 4; + memset(m_response + 4, 0, 6); // six HEX zeros + } + else + { + size += (1 + addr.dns.size); /* name length + resolved address */ + m_response[4] = addr.dns.size; + memcpy(m_response + 5, addr.dns.value, addr.dns.size); + htobe16buf(m_response + size - 2, port); + } break; } - htobe16buf(m_response+size-2,port); //Port - return boost::asio::const_buffers_1(m_response,size); + return boost::asio::const_buffers_1(m_response, size); } boost::asio::const_buffers_1 SOCKSHandler::GenerateUpstreamRequest() @@ -259,7 +281,7 @@ namespace proxy // SOCKS 4a m_upstream_request[0] = '\x04'; //version m_upstream_request[1] = m_cmd; - htobe16buf(m_upstream_request+2, m_port); + htobe16buf(m_upstream_request + 2, m_port); m_upstream_request[4] = 0; m_upstream_request[5] = 0; m_upstream_request[6] = 0; @@ -270,7 +292,7 @@ namespace proxy m_upstream_request[10] = 'p'; m_upstream_request[11] = 'd'; m_upstream_request[12] = 0; - upstreamRequestSize += 13; + upstreamRequestSize += 13; if (m_address.dns.size <= max_socks_hostname_size - ( upstreamRequestSize + 1) ) { // bounds check okay memcpy(m_upstream_request + upstreamRequestSize, m_address.dns.value, m_address.dns.size); @@ -285,21 +307,19 @@ namespace proxy bool SOCKSHandler::Socks5ChooseAuth() { - m_response[0] = '\x05'; //Version - m_response[1] = m_authchosen; //Response code - boost::asio::const_buffers_1 response(m_response,2); + m_response[0] = '\x05'; // Version + m_response[1] = m_authchosen; // Response code + boost::asio::const_buffers_1 response(m_response, 2); if (m_authchosen == AUTH_UNACCEPTABLE) { LogPrint(eLogWarning, "SOCKS: v5 authentication negotiation failed"); - boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, - shared_from_this(), std::placeholders::_1)); + boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, shared_from_this(), std::placeholders::_1)); return false; } else { LogPrint(eLogDebug, "SOCKS: v5 choosing authentication method: ", m_authchosen); - boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksResponse, - shared_from_this(), std::placeholders::_1)); + boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksResponse, shared_from_this(), std::placeholders::_1)); return true; } } @@ -313,7 +333,7 @@ namespace proxy { case SOCKS4: LogPrint(eLogWarning, "SOCKS: v4 request failed: ", error); - if (error < SOCKS4_OK) error = SOCKS4_FAIL; //Transparently map SOCKS5 errors + if (error < SOCKS4_OK) error = SOCKS4_FAIL; // Transparently map SOCKS5 errors response = GenerateSOCKS4Response(error, m_4aip, m_port); break; case SOCKS5: @@ -322,13 +342,13 @@ namespace proxy break; } boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, - shared_from_this(), std::placeholders::_1)); + shared_from_this(), std::placeholders::_1)); } void SOCKSHandler::SocksRequestSuccess() { boost::asio::const_buffers_1 response(nullptr,0); - //TODO: this should depend on things like the command type and callbacks may change + // TODO: this should depend on things like the command type and callbacks may change switch (m_socksv) { case SOCKS4: @@ -339,12 +359,11 @@ namespace proxy LogPrint(eLogInfo, "SOCKS: v5 connection success"); auto s = i2p::client::context.GetAddressBook().ToAddress(GetOwner()->GetLocalDestination()->GetIdentHash()); address ad; ad.dns.FromString(s); - //HACK only 16 bits passed in port as SOCKS5 doesn't allow for more + // HACK only 16 bits passed in port as SOCKS5 doesn't allow for more response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, ad, m_stream->GetRecvStreamID()); break; } - boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone, - shared_from_this(), std::placeholders::_1)); + boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone, shared_from_this(), std::placeholders::_1)); } void SOCKSHandler::EnterState(SOCKSHandler::state nstate, uint8_t parseleft) { @@ -366,12 +385,12 @@ namespace proxy { if ( m_cmd != CMD_CONNECT ) { - //TODO: we need to support binds and other shit! + // TODO: we need to support binds and other shit! LogPrint(eLogError, "SOCKS: unsupported command: ", m_cmd); SocksRequestFailed(SOCKS5_CMD_UNSUP); return false; } - //TODO: we may want to support other address types! + // TODO: we may want to support other address types! if ( m_addrtype != ADDR_DNS ) { switch (m_socksv) @@ -433,9 +452,9 @@ namespace proxy break; case CMD_UDP: if (m_socksv == SOCKS5) break; -#if (__cplusplus >= 201703L) // C++ 17 or higher +#if (__cplusplus >= 201703L) // C++ 17 or higher [[fallthrough]]; -#endif +#endif default: LogPrint(eLogError, "SOCKS: invalid command: ", ((int)*sock_buff)); SocksRequestFailed(SOCKS5_GEN_FAIL); @@ -652,8 +671,7 @@ namespace proxy LogPrint(eLogDebug, "SOCKS: async upstream sock read"); if (m_upstreamSock) { m_upstreamSock->async_read_some(boost::asio::buffer(m_upstream_response, SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE), - std::bind(&SOCKSHandler::HandleUpstreamSockRecv, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); + std::bind(&SOCKSHandler::HandleUpstreamSockRecv, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } else { LogPrint(eLogError, "SOCKS: no upstream socket for read"); SocksRequestFailed(SOCKS5_GEN_FAIL); @@ -735,8 +753,7 @@ namespace proxy LogPrint(eLogInfo, "SOCKS: negotiating with upstream proxy"); EnterState(UPSTREAM_HANDSHAKE); if (m_upstreamSock) { - boost::asio::write(*m_upstreamSock, - GenerateUpstreamRequest()); + boost::asio::write(*m_upstreamSock, GenerateUpstreamRequest()); AsyncUpstreamSockRead(); } else { LogPrint(eLogError, "SOCKS: no upstream socket to send handshake to"); @@ -774,7 +791,7 @@ namespace proxy SOCKSServer::SOCKSServer(const std::string& name, const std::string& address, int port, bool outEnable, const std::string& outAddress, uint16_t outPort, std::shared_ptr localDestination) : - TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()), m_Name (name) + TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()), m_Name (name) { m_UseUpstreamProxy = false; if (outAddress.length() > 0 && outEnable) diff --git a/libi2pd_client/SOCKS.h b/libi2pd_client/SOCKS.h index 87f08de4..f41cfd72 100644 --- a/libi2pd_client/SOCKS.h +++ b/libi2pd_client/SOCKS.h @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #ifndef SOCKS_H__ #define SOCKS_H__ @@ -14,6 +22,7 @@ namespace proxy class SOCKSServer: public i2p::client::TCPIPAcceptor { public: + SOCKSServer(const std::string& name, const std::string& address, int port, bool outEnable, const std::string& outAddress, uint16_t outPort, std::shared_ptr localDestination = nullptr); ~SOCKSServer() {}; @@ -21,6 +30,7 @@ namespace proxy void SetUpstreamProxy(const std::string & addr, const uint16_t port); protected: + // Implements TCPIPAcceptor std::shared_ptr CreateHandler(std::shared_ptr socket); const char* GetName() { return m_Name.c_str (); } diff --git a/qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml b/qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml index 00b6974a..452d750f 100644 --- a/qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml +++ b/qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml @@ -35,6 +35,8 @@ + +